export kernel clock information to user space

Amit Kucheria amit.kucheria at linaro.org
Tue Oct 12 07:39:05 UTC 2010


Adding linaro-dev to cc. Kernel consolidation WG might have comments.

On Tue, Oct 12, 2010 at 9:04 AM, Yong Shen <yong.shen at linaro.org> wrote:
> Hi Amit and Jeremy,
>
> This is not a patch review. But patch may better present my idea. Basically,
> I want to add some code in common clock code to export clock information, so
> every platform can benefit. This information is present in a tree-like
> pattern.
> Currently, each platform uses their own way to show clock info, which is
> hard to use a common user space tool to collect information.
> For this purpose, I need do the rest:
> 1. Add a clock name check in the clkdev_add. We don't accept two clocks with
> the same name to clkdev_add, do we? otherwise, it is impossible to create a
> tree-like structure under file system, cause no same names under a
> directory.
> 2. Recursive function creates the clock tree in debugfs, which referred
> omap's clock implementation.
> 3. Add interface needed to let mach related drivers to report their
> information. clk_get_rate is already there. Maybe we need clk_get_flags()
> and clk_get_usecount() and more.

Agreed, this functionality is necessary for common clk infrastructure
to be useful.

We've also incorporated this functionality into a tool called
powerdebug that'll show runtime state of the clock tree. This is very
useful for driver developers.

/Amit

> Below is the patch, please share your thoughts.
>
> diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
> index e2b2bb6..2ec09ee 100644
> --- a/arch/arm/common/clkdev.c
> +++ b/arch/arm/common/clkdev.c
> @@ -19,6 +19,7 @@
>  #include <linux/mutex.h>
>  #include <linux/clk.h>
>  #include <linux/slab.h>
> +#include <linux/debugfs.h>
>
>  #include <asm/clkdev.h>
>  #include <mach/clkdev.h>
> @@ -95,6 +96,21 @@ EXPORT_SYMBOL(clk_put);
>
>  void clkdev_add(struct clk_lookup *cl)
>  {
> +    struct clk_lookup *p;
> +    struct clk *clk = NULL;
> +
> +    list_for_each_entry(p, &clocks, node) {
> +        if (p->dev_id) {
> +            if (!cl->dev_id || strcmp(p->dev_id, cl->dev_id))
> +                continue;
> +        }
> +        if (p->con_id) {
> +            if (!cl->con_id || strcmp(p->con_id, cl->con_id))
> +                continue;
> +        }
> +
> +        return;
> +    }
>      mutex_lock(&clocks_mutex);
>      list_add_tail(&cl->node, &clocks);
>      mutex_unlock(&clocks_mutex);
> @@ -177,3 +193,131 @@ void clkdev_drop(struct clk_lookup *cl)
>      kfree(cl);
>  }
>  EXPORT_SYMBOL(clkdev_drop);
> +
> +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
> +/*
> + *    debugfs support to trace clock tree hierarchy and attributes
> + */
> +
> +static struct clk_lookup *clk_lookup_get_parent(struct clk *clk)
> +{
> +    struct clk_lookup *cl;
> +
> +    list_for_each_entry(cl, &clocks, node) {
> +        if (cl->clk == clk_get_parent(clk))
> +            return cl;
> +    }
> +
> +    return NULL;
> +}
> +static struct dentry *clk_debugfs_root;
> +
> +static int clk_debugfs_register_one(struct clk_lookup *cl)
> +{
> +    int err;
> +    struct dentry *d, *child, *child_tmp;
> +    struct clk_lookup *pa = clk_lookup_get_parent(cl->clk);
> +    char s[255];
> +    char *p = s;
> +
> +    if (cl->dev_id && cl->con_id)
> +        p += sprintf(p, "%s-%s", cl->dev_id, cl->con_id);
> +    else if (cl->dev_id)
> +        p += sprintf(p, "%s-NULL", cl->dev_id);
> +    else if (cl->con_id)
> +        p += sprintf(p, "NULL-%s", cl->con_id);
> +    else
> +        p += sprintf(p, "%s", "unknown");
> +
> +    printk("create dir %s, %s....\n", s, pa? "nonroot" : "root");
> +    d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
> +    if (!d) {
> +        printk("failed to create dir....\n");
> +        return -ENOMEM;
> +    }
> +
> +    cl->dent = d;
> +
> +    if (cl->usecount != NULL) {
> +        d = debugfs_create_u8("usecount", S_IRUGO, cl->dent, (u8
> *)&cl->usecount);
> +        if (!d) {
> +            err = -ENOMEM;
> +            goto err_out;
> +        }
> +    }
> +    if (cl->rate) {
> +        d = debugfs_create_u32("rate", S_IRUGO, cl->dent, (u32
> *)&cl->rate);
> +        if (!d) {
> +            err = -ENOMEM;
> +            goto err_out;
> +        }
> +    }
> +    if (cl->flags) {
> +        d = debugfs_create_x32("flags", S_IRUGO, cl->dent, (u32
> *)&cl->flags);
> +        if (!d) {
> +            err = -ENOMEM;
> +            goto err_out;
> +        }
> +    }
> +    return 0;
> +
> +err_out:
> +    printk("err out....\n");
> +    return -ENOMEM;
> +    d = cl->dent;
> +    list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
> +        debugfs_remove(child);
> +    debugfs_remove(cl->dent);
> +    return err;
> +}
> +
> +static int clk_debugfs_register(struct clk_lookup *cl)
> +{
> +    int err;
> +    struct clk_lookup *pa = clk_lookup_get_parent(cl->clk);
> +
> +    if (pa && !pa->dent) {
> +        err = clk_debugfs_register(pa);
> +        if (err)
> +            return err;
> +    }
> +
> +    if (!cl->dent) {
> +        err = clk_debugfs_register_one(cl);
> +        if (err)
> +            return err;
> +    }
> +    return 0;
> +}
> +
> +static int __init clk_debugfs_init(void)
> +{
> +    struct clk_lookup *cl;
> +    struct dentry *d;
> +    int err;
> +
> +    d = debugfs_create_dir("clock", NULL);
> +    if (!d)
> +        return -ENOMEM;
> +    clk_debugfs_root = d;
> +
> +    list_for_each_entry(cl, &clocks, node) {
> +        //cl->usercount = clk_get_usercount(cl->clk);
> +        //cl->flags = clk_get_flags(cl->clk);
> +        cl->rate = clk_get_rate(cl->clk);
> +        printk("dev name %s, con name %s\n",\
> +            cl->dev_id ? cl->dev_id : NULL,\
> +                   cl->con_id ? cl->con_id : NULL);
> +        err = clk_debugfs_register(cl);
> +        if (err)
> +            goto err_out;
> +    }
> +    return 0;
> +err_out:
> +    printk("on my god ........\n");
> +    debugfs_remove_recursive(clk_debugfs_root);
> +    return err;
> +}
> +late_initcall(clk_debugfs_init);
> +
> +#endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */
> diff --git a/arch/arm/include/asm/clkdev.h b/arch/arm/include/asm/clkdev.h
> index b56c138..51734ef 100644
> --- a/arch/arm/include/asm/clkdev.h
> +++ b/arch/arm/include/asm/clkdev.h
> @@ -20,6 +20,13 @@ struct clk_lookup {
>      const char        *dev_id;
>      const char        *con_id;
>      struct clk        *clk;
> +
> +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
> +    short            usecount;
> +    unsigned short        flags;
> +    unsigned long        rate;
> +    struct dentry           *dent;
> +#endif
>  };
>
>  struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id
>
>
> This is the information output from powerdebug after I apply the patch to
> mx51 kernel.
> /
> |-- fec.0-NULL <flags=0x0:rate=66500000:usecount=0>
> |-- NULL-gpt <flags=0x0:rate=8000000:usecount=0>
> |-- NULL-cko1 <flags=0x0:rate=8000000:usecount=0>
> |-- mxc_w1.0-NULL <flags=0x0:rate=8000000:usecount=0>
> |-- pata_fsl-NULL <flags=0x0:rate=66500000:usecount=0>
> |-- NULL-usb_clk <flags=0x0:rate=60000000:usecount=0>
> |-- NULL-usb_utmi_clk <flags=0x0:rate=60000000:usecount=0>
> |-- NULL-usb_ahb_clk <flags=0x0:rate=66500000:usecount=0>
> |-- NULL-iim_clk <flags=0x0:rate=66500000:usecount=0>
> |-- mxc_spi.2-NULL <flags=0x0:rate=66500000:usecount=0>
> |-- mxc_spi.1-NULL <flags=0x0:rate=6000000:usecount=0>
> |-- mxc_spi.0-NULL <flags=0x0:rate=6000000:usecount=0>
> |-- mxc_pwm.1-NULL <flags=0x0:rate=8000000:usecount=0>
> |-- mxc_pwm.0-NULL <flags=0x0:rate=8000000:usecount=0>
> |-- imx-i2c.1-NULL <flags=0x0:rate=8000000:usecount=0>
> |-- imx-i2c.0-NULL <flags=0x0:rate=8000000:usecount=0>
> |-- mxcintuart.2-NULL <flags=0x0:rate=66666666:usecount=0>
> |-- mxcintuart.1-NULL <flags=0x0:rate=66666666:usecount=0>
> |-- mxcintuart.0-NULL <flags=0x0:rate=66666666:usecount=0>
> |-- mxc_sdma-sdma_ipg_clk <flags=0x0:rate=66500000:usecount=0>
> |-- NULL-gpc_dvfs_clk <flags=0x0:rate=66500000:usecount=0>
> |-- NULL-ckil <flags=0x0:rate=32768:usecount=0>
> |   |-- mxc_rtc.0-NULL <flags=0x0:rate=32768:usecount=0>
> |   |-- NULL-lpsr_clk <flags=0x0:rate=32768:usecount=0>
> |-- NULL-ckih2 <flags=0x0:rate=24576000:usecount=0>
> |-- NULL-ckih <flags=0x0:rate=22579200:usecount=0>
> |-- NULL-osc <flags=0x0:rate=24000000:usecount=0>
> |   |-- NULL-spdif_xtal_clk <flags=0x0:rate=24000000:usecount=0>
> |   |   |-- mxc_alsa_spdif.0-NULL <flags=0x0:rate=24000000:usecount=0>
> |   |-- NULL-usb_phy1_clk <flags=0x0:rate=24000000:usecount=0>
> |   |-- NULL-lp_apm <flags=0x0:rate=24000000:usecount=0>
> |   |   |-- NULL-ssi_lp_apm_clk <flags=0x0:rate=24000000:usecount=0>
> |   |   |   |-- mxc_ssi.1-NULL <flags=0x0:rate=12000000:usecount=0>
> |   |   |   |   |-- NULL-ssi_ext2_clk <flags=0x0:rate=12000000:usecount=0>
> |   |   |   |-- mxc_ssi.0-NULL <flags=0x0:rate=12000000:usecount=0>
> |   |   |   |   |-- NULL-ssi_ext1_clk <flags=0x0:rate=12000000:usecount=0>
> |   |-- NULL-pll3 <flags=0x0:rate=216000000:usecount=0>
> |   |   |-- mxc_sim.0-NULL <flags=0x0:rate=54000000:usecount=0>
> |   |   |-- mxc_i2c_hs.3-NULL <flags=0x0:rate=54000000:usecount=0>
> |   |   |-- tve.0-NULL <flags=0x0:rate=216000000:usecount=0>
> |   |   |-- NULL-csi_mclk2 <flags=0x0:rate=54000000:usecount=0>
> |   |   |-- NULL-csi_mclk1 <flags=0x0:rate=54000000:usecount=0>
> |   |   |-- NULL-ipu_di1_clk <flags=0x0:rate=27000000:usecount=0>
> |   |   |-- NULL-ipu_di0_clk <flags=0x0:rate=27000000:usecount=0>
> |   |-- NULL-pll2 <flags=0x0:rate=665000000:usecount=0>
> |   |   |-- mxsdhci.1-NULL <flags=0x0:rate=166250000:usecount=0>
> |   |   |-- mxsdhci.0-NULL <flags=0x0:rate=166250000:usecount=0>
> |   |   |   |-- mxsdhci.3-NULL <flags=0x0:rate=166250000:usecount=0>
> |   |   |   |-- mxsdhci.2-NULL <flags=0x0:rate=166250000:usecount=0>/q
> |   |   |-- NULL-usboh3_clk <flags=0x0:rate=66500000:usecount=0>
> |   |   |-- NULL-main_bus_clk <flags=0x0:rate=665000000:usecount=0>
> |   |   |   |-- NULL-ahb_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- mxc_scc.0-NULL <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- NULL-sahara_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- NULL-emi_intr_clk.1
> <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- NULL-emi_intr_clk.0
> <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- mxc_sdma-sdma_ahb_clk
> <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- NULL-emi_slow_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |   |-- NULL-emi_enfc_clk
> <flags=0x0:rate=33250000:usecount=0>
> |   |   |   |   |   |-- NULL-nfc_clk <flags=0x0:rate=33250000:usecount=0>
> |   |   |   |   |-- NULL-ahb_max_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |-- NULL-axi_b_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- mxc_vpu.0-NULL <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- NULL-ipu_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |   |-- NULL-mipi_hsp_clk
> <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- NULL-vpu_core_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |   |-- NULL-vpu_clk <flags=0x0:rate=133000000:usecount=0>
> |   |   |   |-- NULL-axi_a_clk <flags=0x0:rate=166250000:usecount=0>
> |   |   |   |   |-- NULL-gpu2d_clk <flags=0x0:rate=166250000:usecount=0>
> |   |   |   |   |-- NULL-garb_clk <flags=0x0:rate=166250000:usecount=0>
> |   |   |   |   |-- NULL-gpu3d_clk <flags=0x0:rate=166250000:usecount=0>
> |   |-- NULL-pll1_main_clk <flags=0x0:rate=800000000:usecount=0>
> |   |   |-- NULL-pll1_sw_clk <flags=0x0:rate=800000000:usecount=0>
> |   |   |   |-- NULL-ddr_hf_clk <flags=0x0:rate=0:usecount=0>
> |   |   |   |   |-- NULL-ddr_clk <flags=0x0:rate=200000000:usecount=0>
> |   |   |   |   |   |-- NULL-emi_fast_clk
> <flags=0x0:rate=200000000:usecount=0>
> |   |   |   |-- NULL-periph_apm_clk <flags=0x0:rate=800000000:usecount=0>
> |   |   |   |-- NULL-cpu_clk <flags=0x0:rate=800000000:usecount=0>
>
>
> Yong
>



More information about the linaro-dev mailing list