This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

kernel crash when reading ADC registers

Hi,

       I'm getting a kernel crash with non-linefetch error in ti_tscadc.c:

[    1.548095] Unhandled fault: external abort on non-linefetch (0x1028) at 0xf9e0d054

It happens every time I'm trying to do this:

tscadc_readl(ts_dev, TSCADC_REG_SE);

or this:

tscadc_readl(ts_dev, TSCADC_REG_FIFO1) & 0xFFF;

Basically if I'm trying to read ADC registers then I'm getting the error.

In this thread: http://e2e.ti.com/support/arm/sitara_arm/f/791/p/158103/928768.aspx#928768 it seems to suggest that I need to enabled ADC in PRCM module. Having read the relevant sections in SPRUH73G.pdf (the technical reference manual) I now know that it's probably the OCP clock that I haven't enabled.

Can anyone please suggest how to enable it? Where is the PRCM module and how to enable ADC in it?

Thanks.

  • Hi Arturs,

    The PRCM is the Power, Reset and Clock Management module (please read chapter 8 of the AM335x TRM).

    The ADC_TSC module is in the L4_WKUP_CLK domain. The CM_WKUP_ADC_TSC_CLKCTRL register manages the ADC clocks and the CM_WKUP_CLKSTCTRL register field CLKACTIVITY_ADC_FCLK indicates the state of the ADC clock in the domain.

    The tsc_adc clock isn't enabled on init - the ti_tscadc.c driver should do this in its probe function.

    Please mount the debug file system in user space and check the adc_tsc clocks:

    mount -t debugfs none /sys/kernel/debug/
    cat /sys/kernel/debug/clock/summary | busybox grep "adc_tsc"

    Best regards,
    Miroslav

  • Thanks Miroslav,

                   The information you provided gave me a lot to chew on. I've read about the PRCM module and I understand it. I think the part that I'm missing is how to actually do it in ti_tscadc.c driver's probe function.

    I can show you what I've tried so far:

    attempt #1 Use kernel clock management functions declared in "include/linux/clk.h" : in ti_tscadc.c probe function insert this code:

    CODE said:

        clk = clk_get(&pdev->dev, "adc_tsc_ick");
        if (IS_ERR(clk)) {
            dev_err(&pdev->dev, "failed to get TSC ick\n");
            err = PTR_ERR(clk);
            goto err_free_irq;
        }
        clk_prepare(clk);
        clk_enable(clk);
        clock_rate = clk_get_rate(clk);
        printk(KERN_INFO "ADC ick = %u @\n", clock_rate);
        
        clk = clk_get(&pdev->dev, "adc_tsc_fck");
        if (IS_ERR(clk)) {
            dev_err(&pdev->dev, "failed to get TSC fck\n");
            err = PTR_ERR(clk);
            goto err_free_irq;
        }
        clk_prepare(clk);
        clk_enable(clk);
        clock_rate = clk_get_rate(clk);
        printk(KERN_INFO "ADC fck = %u @\n", clock_rate);

    I was hoping that this will enable both the ADC clock and the OCP clock because in "arch/arm/mach-omap2/clock33xx_data.c" source file we have a collection of clocks:

    CODE said:

    static struct omap_clk am33xx_clks[] = {
    ...
        CLK(NULL,    "adc_tsc_fck",        &adc_tsc_fck,    CK_AM33XX),
        CLK(NULL,    "adc_tsc_ick",        &adc_tsc_ick,    CK_AM33XX),
    ...
    }

    and the adc_tsc_fck and adc_tsc_ick are defined as follows:

    CODE said:

    static struct clk adc_tsc_fck = {
        .name        = "adc_tsc_fck",
        .clkdm_name    = "l4_wkup_clkdm",
        .parent        = &sys_clkin_ck,
        .ops        = &clkops_null,
        .recalc        = &followparent_recalc,
    };

    static struct clk adc_tsc_ick = {
        .name        = "adc_tsc_ick",
        .clkdm_name    = "l4_wkup_clkdm",
        .parent        = &l4_wkup_gclk,
        .enable_reg    = AM33XX_CM_WKUP_ADC_TSC_CLKCTRL,
        .enable_bit    = AM33XX_MODULEMODE_SWCTRL,
        .ops        = &clkops_omap2_dflt,
        .recalc        = &followparent_recalc,
    };

    And AM33XX_CM_WKUP_ADC_TSC_CLKCTRL is defined in "arch/arm/mach-omap2/cm33xx.h" like this:

    #define AM33XX_CM_WKUP_ADC_TSC_CLKCTRL            AM33XX_CM_REGADDR(AM33XX_CM_WKUP_MOD, 0x00bc)

    I thought that this is the register I'm looking for and I should just enable the clocks with "clk_prepare(clk)" and "clk_enable(clk)". Well, much to my regret it didn't work-- no change at all. The only thing that caught my eye was that the fck value was printed out as "25 000 000" in contrary to figure mentioned in TRM (spruh73h.pdf) in section:
    12.2.2 TSC_ADC Clock and Reset Management
    Here it says that adc_clk is typically 24 MHz.

    attempt #2 Crudely write the value to the register in clock33xx_data.c : In "arch/arm/mach-omap2/clock33xx_data.c" "__init am33xx_clk_init(void)" function insert this code:

    CODE said:

    writel(readl(AM33XX_CM_WKUP_ADC_TSC_CLKCTRL) | 0x02, AM33XX_CM_WKUP_ADC_TSC_CLKCTRL);

    Unfortunately that had no effect either.

    attempt #3 Crudely write the value to the register in prcm.c :  In "arch/arm/mach-omap2/prcm.c" function "omap_prcm_arch_reset" insert this code:


    CODE said:

    writel(readl(AM33XX_PRM_REGADDR(0x400, 0xBC)) | 0x02, AM33XX_PRM_REGADDR(0x400, 0xBC));


    Unfortunately that didn't work either.

    I've also tried to disable power management through menuconfig and I've tried setting the 'performance' governor for CPU frequency scaling in menuconfig just in case it was the power management that disables the clocks but unfortunately these attempts have been fruitless too.

    I accept that I'm only a beginner in kernel programming and may have done silly things in the examples above. Perhaps these registers should be handled differently. I'll be grateful for some online literature too on how to handle AM335x internals in kernel. Unfortunately there's not much comments in the source files and at times trawling it can be daunting for a beginner like me so please share a pointer in the right direction if you can.

    Thanks and Regards,

    Arturs

  • Ok, a little update. I've solved my problem but the answer was not where I was expecting it. Just in case it helps someone else who's struggling with a similar issue, my advice is: check how you're registering your device (touchscreen device in my case). In an older kernel I used function "platform_device_register" which is defined in "/arch/arm/mach-omap2/devices.c" and it worked fine. With later kernels however you have to use function "omap_device_build" defined in "/arch/arm/plat-omap/omap-device.c". You'll need to do "omap_hwmod_lookup" first. The best example is TI's own one: function "am33xx_register_mfd_tscadc" in "/arch/arm/mach-omap2/devices.c". This way it registers the device to omap bus and enables all required clocks for you so strictly speaking this:

    Miroslav Kiradzhiyski XID said:
    The tsc_adc clock isn't enabled on init - the ti_tscadc.c driver should do this in its probe function.

    is not really true. The tsc_adc clock should already be running if the device is registered properly. Well, that's how I understood it. If someone knows better, feel free to correct me.

    Thanks,

    Arturs