Hi,
I have tried writing HSADC driver, Using IOCTL call to read ADC converted value on HSADC0 channel,
that inside calls mxs_read_general() in which enabling interrupts(END_SEQ, ADC_DONE and TIMEOUT), setting RUN bit in Ctrl0 reg and triggring it.
and waiting for interrupt(ADC_DONE or END_SEQ or TIMEOUT). if TIMEOUT comes again caling mxs_read_general().
So now I am getting ADC_DONE interrupt and Reading FIFO_DATA reg for value in interrupt handler. but the values are different every time with same voltage.
Please help me in this any where I am going wrong.
static int mxs_hsadc_module_probe(struct platform_device *pdev)
{
int ret = 0;
struct device *temp_class;
struct resource *res;
void __iomem *base;
/* ioremap the base address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "No HSADC base address provided\n");
goto err_out0;
}
base = (void __iomem *)IO_ADDRESS(res->start);
if (base == NULL) {
dev_err(&pdev->dev, "Failed to rebase HSADC base address\n");
goto err_out0;
}
hsadc_base = (unsigned int)base;
/* create the chrdev */
mxs_hsadc_major = register_chrdev(0, "mxs-hsadc", &mxs_hsadc_fops);
if (mxs_hsadc_major < 0) {
dev_err(&pdev->dev, "Unable to get a major for mxs_hsadc\n");
return mxs_hsadc_major;
}
/*creating hsadc class */
mxs_hsadc_class = class_create(THIS_MODULE, "mxs-hsadc");
if (IS_ERR(mxs_hsadc_class)) {
dev_err(&pdev->dev, "Error creating mxs_hsadc class.\n");
ret = PTR_ERR(mxs_hsadc_class);
goto err_out1;
}
/*Creating device node */
temp_class = device_create(mxs_hsadc_class, NULL,
MKDEV(mxs_hsadc_major, 0), NULL, "mxs-hsadc");
if (IS_ERR(temp_class)) {
dev_err(&pdev->dev, "Error creating mxs_hsadc class device.\n");
ret = PTR_ERR(temp_class);
goto err_out2;
}
adc_data = kmalloc(sizeof(struct mxs_hsadc_data), GFP_KERNEL);
if (adc_data == NULL)
return -ENOMEM;
/* requesting hsadc irq */
adc_data->irq = platform_get_irq(pdev, 0);
ret = request_irq(adc_data->irq, mxs_hsadc_interrupt, 0, MOD_NAME, MOD_NAME);
if (ret) {
printk(KERN_DEBUG"HSADC : %s : irq registration failed %d\n",__FILE__,ret);
return ret;
}
if (ret != MXS_HSADC_SUCCESS) {
dev_err(&pdev->dev, "Error in mxs_hsadc_init.\n");
goto err_out4;
}
/*HSADC initialization */
ret = mxs_hsadc_init();
/* By default, devices should wakeup if they can */
/* So HSADC is set as "should wakeup" as it can */
device_init_wakeup(&pdev->dev, 1);
return ret;
err_out4:
device_destroy(mxs_hsadc_class, MKDEV(mxs_hsadc_major, 0));
err_out2:
class_destroy(mxs_hsadc_class);
err_out1:
unregister_chrdev(mxs_hsadc_major, "mxs-hsadc");
err_out0:
return ret;
}
int mxs_hsadc_init()
{
int i;
unsigned int reg;
// mxs_hsadc_clk_enable();
__raw_writel(BF_CLKCTRL_FRAC1_HSADCFRAC(0x3f), HW_CLKCTRL_BASE_ADDRESS+HW_CLKCTRL_FRAC1_CLR); /* Clear */
__raw_writel(BF_CLKCTRL_FRAC1_HSADCFRAC(30), HW_CLKCTRL_BASE_ADDRESS+HW_CLKCTRL_FRAC1_SET); /* Set to 288 MHz*/
/* Clear the clock gate*/
__raw_writel(BM_CLKCTRL_FRAC1_CLKGATEHSADC, HW_CLKCTRL_BASE_ADDRESS+HW_CLKCTRL_FRAC1_CLR);
/* Set the HSADC CLK ADC divider to ge the Operation clock of 16 MHz*/
__raw_writel(BM_CLKCTRL_HSADC_RESETB | BF_CLKCTRL_HSADC_FREQDIV( 1 /*CLKCTRL_HSADC_FREQDIV_18*/), HW_CLKCTRL_BASE_ADDRESS+HW_CLKCTRL_HSADC);
/* 1st workaround for HSADC */
__raw_writel(HSADC_CTRL0_RESET,hsadc_base + HSADC_CTRL0_CLR);
for (i = 0; i < 10000; i++) {
reg = __raw_readl(hsadc_base + HSADC_CTRL0);
if (!(reg & HSADC_CTRL0_RESET))
break;
udelay(3);
}
__raw_writel(HSADC_CTRL0_RESET & (~HSADC_CTRL0_CLKGATE_OFF), hsadc_base + HSADC_CTRL0_SET);
__raw_writel(HSADC_CTRL0_CLKGATE_OFF,hsadc_base + HSADC_CTRL0_SET);
__raw_writel(HSADC_CTRL0_CLKGATE_OFF,hsadc_base + HSADC_CTRL0_CLR);
__raw_writel(HSADC_CTRL0_CLKGATE_OFF,hsadc_base + HSADC_CTRL0_SET);
/* 1st workaround for HSADC */
// __raw_writel(HSADC_CTRL0_CLKGATE_OFF,hsadc_base + HSADC_CTRL0_CLR);
__raw_writel(HSADC_CTRL0_RESET,hsadc_base + HSADC_CTRL0_CLR);
for (i = 0; i < 10000; i++) {
reg = __raw_readl(hsadc_base + HSADC_CTRL0);
if (!(reg & HSADC_CTRL0_RESET))
break;
udelay(3);
}
__raw_writel(HSADC_CTRL0_CLKGATE_OFF,hsadc_base + HSADC_CTRL0_CLR);
/*Disable the interrupts*/
__raw_writel(HSADC_INTR_CTRL_TIMEOUT_ENB ,hsadc_base + HSADC_CTRL1_CLR);
__raw_writel(HSADC_INTR_CTRL_END_SEQ_ENB,hsadc_base + HSADC_CTRL1_CLR);
__raw_writel(HSADC_INTR_CTRL_ADC_DONE_ENB,hsadc_base + HSADC_CTRL1_CLR);
__raw_writel(HSADC_INTR_CTRL_FIFO_OVFW_ENB,hsadc_base + HSADC_CTRL1_CLR);
__raw_writel(HSADC_INTR_CLR,hsadc_base + HSADC_CTRL1_SET);
__raw_writel(HSADC_INTR_STATUS_CLR,hsadc_base + HSADC_CTRL1_SET);
/* HSADC is set to SINGLE Mode*/
reg = 0x1;
__raw_writel(reg , hsadc_base +HSADC_SEQ_NUM);
/*Number of samples*/
reg = 0x1;
__raw_writel(reg , hsadc_base +HSADC_SEQ_SAM_NUM);
/*Power Register config */
reg =0x84000626;
__raw_writel(reg , REGS_POWER_BASE + HW_POWER_ANACLKCTRL);
/* Wake up from power down mode */
__raw_writel(HSADC_CTRL2_POWER_DOWN,hsadc_base + HSADC_CTRL2_CLR);
/*Set PreCharge Enable */
__raw_writel(HSADC_CTRL2_PRECHARGE,hsadc_base + HSADC_CTRL2_SET);
/*Set HSADC_RUN mode*/
__raw_writel(HSADC_CTRL0_RUN,hsadc_base + HSADC_CTRL0_SET);
return 0;
}
/* interrupt handler */
static irqreturn_t mxs_hsadc_interrupt(int irq, void *dev_id)
{
unsigned long reg=0;
hsadc_value = __raw_readl(hsadc_base + HSADC_FIFO_DATA);
printk(KERN_DEBUG"HSADC : value %x\n",hsadc_value);
reg = __raw_readl(hsadc_base + HSADC_CTRL1) & HSADC_INTR_STATUS_END_SEQ;
if(reg)
{
printk(KERN_DEBUG"%s : reg value HSADC_INTR_STATUS_END_SEQ %d\n",__FILE__,(int)reg);
goto ADC_DONE
}
reg = __raw_readl(hsadc_base + HSADC_CTRL1) & HSADC_INTR_STATUS_ADC_DONE;
if(reg)
{
printk(KERN_DEBUG"%s : reg value HSADC_INTR_STATUS_ADC_DONE %d\n",__FILE__,(int)reg);
goto ADC_DONE
}
reg = __raw_readl(hsadc_base + HSADC_CTRL1) & HSADC_INTR_STATUS_TIMEOUT;
if (reg)
{
/*Disable timeout interrupt*/
__raw_writel(HSADC_INTR_CTRL_TIMEOUT_ENB,hsadc_base + HSADC_CTRL1_CLR);
/*Clear interrupt status and pending interrupts*/
__raw_writel(HSADC_INTR_CLR,hsadc_base + HSADC_CTRL1_SET);
__raw_writel(HSADC_INTR_STATUS_CLR,hsadc_base + HSADC_CTRL1_CLR);
printk(KERN_DEBUG"%s : reg value HSADC_INTR_STATUS_TIMEOUT %d\n",__FILE__,(int)reg);
timeout = 1;
hsadc_conversion=1;
return IRQ_HANDLED;
}
ADC_DONE:
/*Clear interrupt status and pending interrupts*/
__raw_writel(HSADC_INTR_CLR,hsadc_base + HSADC_CTRL1_SET);
__raw_writel(HSADC_INTR_STATUS_CLR,hsadc_base + HSADC_CTRL1_CLR);
timeout = 0;
hsadc_conversion=1;
return IRQ_HANDLED;
}
enum MXS_HSADC_STATUS mxs_hsadc_read_general()
{
unsigned long reg = 0;
hsadc_conversion=0;
/*channel selection */
__raw_writel((7 << 1), hsadc_base + HSADC_CTRL2_SET );
/*Clear interrupt status and pending interrupts*/
__raw_writel(HSADC_INTR_CLR,hsadc_base + HSADC_CTRL1_SET);
__raw_writel(HSADC_INTR_STATUS_CLR,hsadc_base + HSADC_CTRL1_SET);
/*Enabling the interrupts*/
__raw_writel(HSADC_INTR_CTRL_END_SEQ_ENB,hsadc_base + HSADC_CTRL1_SET);
__raw_writel(HSADC_INTR_CTRL_ADC_DONE_ENB,hsadc_base + HSADC_CTRL1_SET);
__raw_writel(HSADC_INTR_CTRL_FIFO_OVFW_ENB,hsadc_base + HSADC_CTRL1_SET);
__raw_writel(HSADC_INTR_CTRL_TIMEOUT_ENB,hsadc_base + HSADC_CTRL1_SET);
/*Delay cycles*/
reg = 4 << 1;
__raw_writel(reg, hsadc_base + HSADC_CTRL0_SET);
/*Set HSADC_RUN bit*/
__raw_writel(HSADC_CTRL0_RUN,hsadc_base + HSADC_CTRL0_SET);
mdelay(100);
__raw_writel(HSADC_CTRL0_SW_TRIGGER, hsadc_base + HSADC_CTRL0_SET);
mdelay(10);
__raw_writel(HSADC_CTRL0_SW_TRIGGER, hsadc_base + HSADC_CTRL0_SET);
while(!hsadc_conversion)
{
/*Waiting for the interrupt to occur*/
mdelay(100);
}
/*Disable the RUN Bit*/
__raw_writel(HSADC_CTRL0_RUN,hsadc_base + HSADC_CTRL0_CLR);
if(timeout)
return MXS_HSADC_ERROR;
else
return MXS_HSADC_SUCCESS;
}
Thanks,
Iranna