[PATCH 2/8] clk: keystone: Add gate control clock driver

Mark Rutland mark.rutland at arm.com
Tue Aug 13 12:13:14 EDT 2013


[adding dt maintainers]

On Mon, Aug 05, 2013 at 05:12:21PM +0100, Santosh Shilimkar wrote:
> Add the driver for the clock gate control which uses PSC (Power Sleep
> Controller) IP on Keystone 2 based SOCs. It is responsible for enabling and
> disabling of the clocks for different IPs present in the SoC.
> 
> Cc: Mike Turquette <mturquette at linaro.org>
> 
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar at ti.com>
> ---
>  .../devicetree/bindings/clock/keystone-gate.txt    |   30 ++
>  drivers/clk/keystone/gate.c                        |  306 ++++++++++++++++++++
>  include/linux/clk/keystone.h                       |    1 +
>  3 files changed, 337 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/keystone-gate.txt
>  create mode 100644 drivers/clk/keystone/gate.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/keystone-gate.txt b/Documentation/devicetree/bindings/clock/keystone-gate.txt
> new file mode 100644
> index 0000000..40afef5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/keystone-gate.txt
> @@ -0,0 +1,30 @@
> +Binding for Keystone gate control driver which uses PSC controller IP.
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible : shall be "keystone,psc-clk".

Similarly to my comments on patch 1, this should probably be something
more like "ti,keystone-psc-clock"

> +- #clock-cells : from common clock binding; shall be set to 0.
> +- clocks : parent clock phandle
> +- reg :        psc address address space
> +
> +Optional properties:
> +- clock-output-names : From common clock binding to override the
> +                       default output clock name
> +- status : "enabled" if clock is always enabled

Huh? This is a standard property, for which ePAPR defines "okay",
"disabled", "fail", and "fail-sss", and not "enabled". This also doesn't
seem to follow the standard meaning judging by the code.

It looks like this could be a boolean property
("clock-always-enabled"?), but that assumes it's necessary.

Why do you need this?

> +- lpsc : lpsc module id, if not set defaults to zero

What's that needed for? Are there multiple clocks sharing a common
register bank?

> +- pd : power domain number, if not set defaults to zero (always ON)

Please don't use abbreviations unnecessarily. "power-domain-id" or
similar would be far better. What exactly does this represent? Does the
clock IP itself handle power domains, or is there some external unit
that controls power domains?

> +- gpsc : gpsc number, if not set defaults to zero

How does that interact with the lpsc property? When are these necessary?

> +
> +Example:
> +       clock {
> +               #clock-cells = <0>;
> +               compatible = "keystone,psc-clk";
> +               clocks = <&chipclk3>;
> +               clock-output-names = "debugss_trc";
> +               reg = <0x02350000 4096>;
> +               lpsc = <5>;
> +               pd = <1>;
> +       };
> diff --git a/drivers/clk/keystone/gate.c b/drivers/clk/keystone/gate.c
> new file mode 100644
> index 0000000..72ac478
> --- /dev/null
> +++ b/drivers/clk/keystone/gate.c
> @@ -0,0 +1,306 @@
> +/*
> + * Clock driver for Keystone 2 based devices
> + *
> + * Copyright (C) 2013 Texas Instruments.
> + *     Murali Karicheri <m-karicheri2 at ti.com>
> + *     Santosh Shilimkar <santosh.shilimkar at ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/of_address.h>
> +#include <linux/of.h>
> +#include <linux/module.h>
> +
> +/* PSC register offsets */
> +#define PTCMD                  0x120
> +#define PTSTAT                 0x128
> +#define PDSTAT                 0x200
> +#define PDCTL                  0x300
> +#define MDSTAT                 0x800
> +#define MDCTL                  0xa00
> +
> +/* PSC module states */
> +#define PSC_STATE_SWRSTDISABLE 0
> +#define PSC_STATE_SYNCRST      1
> +#define PSC_STATE_DISABLE      2
> +#define PSC_STATE_ENABLE       3
> +
> +#define MDSTAT_STATE_MASK      0x3f
> +#define MDSTAT_MCKOUT          BIT(12)
> +#define PDSTAT_STATE_MASK      0x1f
> +#define MDCTL_FORCE            BIT(31)
> +#define MDCTL_LRESET           BIT(8)
> +#define PDCTL_NEXT             BIT(0)
> +
> +/* PSC flags. Disable state is SwRstDisable */
> +#define PSC_SWRSTDISABLE       BIT(0)
> +/* Force module state transtition */
> +#define PSC_FORCE              BIT(1)
> +/* Keep module in local reset */
> +#define PSC_LRESET             BIT(2)
> +#define NUM_GPSC               2
> +#define REG_OFFSET             4
> +
> +/**
> + * struct clk_psc_data - PSC data
> + * @base: Base address for a PSC
> + * @flags: framework flags
> + * @lpsc: Is it local PSC
> + * @gpsc: Is it global PSC
> + * @domain: PSC domain
> + *
> + */
> +struct clk_psc_data {
> +       void __iomem *base;
> +       u32     flags;
> +       u32     psc_flags;
> +       u8      lpsc;
> +       u8      gpsc;
> +       u8      domain;
> +};
> +
> +/**
> + * struct clk_psc - PSC clock structure
> + * @hw: clk_hw for the psc
> + * @psc_data: PSC driver specific data
> + * @lock: Spinlock used by the driver
> + */
> +struct clk_psc {
> +       struct clk_hw hw;
> +       struct clk_psc_data *psc_data;
> +       spinlock_t *lock;
> +};
> +
> +struct reg_psc {
> +       u32 phy_base;
> +       u32 size;
> +       void __iomem *io_base;
> +};
> +
> +static struct reg_psc psc_addr[NUM_GPSC];
> +static DEFINE_SPINLOCK(psc_lock);
> +
> +#define to_clk_psc(_hw) container_of(_hw, struct clk_psc, hw)
> +
> +static void psc_config(void __iomem *psc_base, unsigned int domain,
> +               unsigned int id, bool enable, u32 flags)
> +{
> +       u32 ptcmd, pdstat, pdctl, mdstat, mdctl, next_state;
> +
> +       if (enable)
> +               next_state = PSC_STATE_ENABLE;
> +       else if (flags & PSC_SWRSTDISABLE)
> +               next_state = PSC_STATE_SWRSTDISABLE;
> +       else
> +               next_state = PSC_STATE_DISABLE;
> +
> +       mdctl = readl(psc_base + MDCTL + REG_OFFSET * id);
> +       mdctl &= ~MDSTAT_STATE_MASK;
> +       mdctl |= next_state;
> +       if (flags & PSC_FORCE)
> +               mdctl |= MDCTL_FORCE;
> +       if (flags & PSC_LRESET)
> +               mdctl &= ~MDCTL_LRESET;
> +       writel(mdctl, psc_base + MDCTL + REG_OFFSET * id);
> +
> +       pdstat = readl(psc_base + PDSTAT + REG_OFFSET * domain);
> +       if (!(pdstat & PDSTAT_STATE_MASK)) {
> +               pdctl = readl(psc_base + PDCTL + REG_OFFSET * domain);
> +               pdctl |= PDCTL_NEXT;
> +               writel(pdctl, psc_base + PDCTL + REG_OFFSET * domain);
> +       }
> +
> +       ptcmd = 1 << domain;
> +       writel(ptcmd, psc_base + PTCMD);
> +       while ((readl(psc_base + PTSTAT) >> domain) & 1)
> +               ;
> +
> +       do {
> +               mdstat = readl(psc_base + MDSTAT + REG_OFFSET * id);
> +       } while (!((mdstat & MDSTAT_STATE_MASK) == next_state));
> +}
> +
> +static int keystone_clk_is_enabled(struct clk_hw *hw)
> +{
> +       struct clk_psc *psc = to_clk_psc(hw);
> +       struct clk_psc_data *data = psc->psc_data;
> +       u32 mdstat = readl(data->base + MDSTAT + REG_OFFSET * data->lpsc);
> +
> +       return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
> +}
> +
> +static int keystone_clk_enable(struct clk_hw *hw)
> +{
> +       struct clk_psc *psc = to_clk_psc(hw);
> +       struct clk_psc_data *data = psc->psc_data;
> +       unsigned long flags = 0;
> +
> +       if (psc->lock)
> +               spin_lock_irqsave(psc->lock, flags);
> +
> +       psc_config(data->base, data->domain, data->lpsc, 1, data->psc_flags);
> +
> +       if (psc->lock)
> +               spin_unlock_irqrestore(psc->lock, flags);
> +
> +       return 0;
> +}
> +
> +static void keystone_clk_disable(struct clk_hw *hw)
> +{
> +       struct clk_psc *psc = to_clk_psc(hw);
> +       struct clk_psc_data *data = psc->psc_data;
> +       unsigned long flags = 0;
> +
> +       if (psc->lock)
> +               spin_lock_irqsave(psc->lock, flags);
> +
> +       psc_config(data->base, data->domain, data->lpsc, 0, data->psc_flags);
> +
> +       if (psc->lock)
> +               spin_unlock_irqrestore(psc->lock, flags);
> +}
> +
> +static const struct clk_ops clk_psc_ops = {
> +       .enable = keystone_clk_enable,
> +       .disable = keystone_clk_disable,
> +       .is_enabled = keystone_clk_is_enabled,
> +};
> +
> +/**
> + * clk_register_psc - register psc clock
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @psc_data: platform data to configure this clock
> + * @lock: spinlock used by this clock
> + */
> +static struct clk *clk_register_psc(struct device *dev,
> +                       const char *name,
> +                       const char *parent_name,
> +                       struct clk_psc_data *psc_data,
> +                       spinlock_t *lock)
> +{
> +       struct clk_init_data init;
> +       struct clk_psc *psc;
> +       struct clk *clk;
> +
> +       psc = kzalloc(sizeof(*psc), GFP_KERNEL);
> +       if (!psc)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = name;
> +       init.ops = &clk_psc_ops;
> +       init.flags = psc_data->flags;
> +       init.parent_names = (parent_name ? &parent_name : NULL);

Is &parent_name not a pointer to a pointer on the stack? Is this only
used once?

> +       init.num_parents = (parent_name ? 1 : 0);
> +
> +       psc->psc_data = psc_data;
> +       psc->lock = lock;
> +       psc->hw.init = &init;

That's definitely a pointer to some data on the stack, and that seems to
be kept around. Is this only used for one-time initialisation, or might
it be used later?

> +
> +       clk = clk_register(NULL, &psc->hw);
> +       if (IS_ERR(clk))
> +               kfree(psc);
> +
> +       return clk;
> +}
> +
> +/**
> + * of_psc_clk_init - initialize psc clock through DT
> + * @node: device tree node for this clock
> + * @lock: spinlock used by this clock
> + */
> +static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock)
> +{
> +       const char *parent_name, *status = NULL, *base_flags = NULL;
> +       struct clk_psc_data *data;
> +       const char *clk_name = node->name;
> +       u32 gpsc = 0, lpsc = 0, pd = 0;
> +       struct resource res;
> +       struct clk *clk;
> +       int rc;
> +
> +       data = kzalloc(sizeof(*data), GFP_KERNEL);
> +       WARN_ON(!data);

Does that not mean things will blow up later? return now?

> +
> +       if (of_address_to_resource(node, 0, &res)) {
> +               pr_err("psc_clk_init - no reg property defined\n");
> +               goto out;
> +       }
> +
> +       of_property_read_u32(node, "gpsc", &gpsc);
> +       of_property_read_u32(node, "lpsc", &lpsc);
> +       of_property_read_u32(node, "pd", &pd);
> +
> +       if (gpsc >= NUM_GPSC) {
> +               pr_err("psc_clk_init - no reg property defined\n");

Wrong error message.

> +               goto out;
> +       }
> +
> +       of_property_read_string(node,
> +                       "clock-output-names", &clk_name);
> +       parent_name = of_clk_get_parent_name(node, 0);
> +       WARN_ON(!parent_name);

Do you require the parent name? If so you need to fail gracefully, if
not you don't need the WARN_ON (perhaps a pr_warn would be better?).

> +
> +       /* Expected that same phy_base is used for all psc clocks of
> +        * a give gpsc. So ioremap is done only once.
> +        */

So these clocks are all components of a larger IP block? It might make
more sense to describe the containing block, with the actual clocks as
sub-nodes (or if the set of clocks for the containing block is known,
just the containing block).

> +       if (psc_addr[gpsc].phy_base) {
> +               if (psc_addr[gpsc].phy_base != res.start) {
> +                       pr_err("Different psc base for same GPSC\n");
> +                       goto out;
> +               }
> +       } else {
> +               psc_addr[gpsc].phy_base = res.start;
> +               psc_addr[gpsc].io_base =
> +                       ioremap(res.start, resource_size(&res));
> +       }
> +
> +       WARN_ON(!psc_addr[gpsc].io_base);

Surely things will blow up later if that's the case? return here instead?

> +       data->base = psc_addr[gpsc].io_base;
> +       data->lpsc = lpsc;
> +       data->gpsc = gpsc;
> +       data->domain = pd;
> +
> +       if (of_property_read_bool(node, "ti,psc-lreset"))
> +               data->psc_flags |= PSC_LRESET;
> +       if (of_property_read_bool(node, "ti,psc-force"))
> +               data->psc_flags |= PSC_FORCE;

Neither of these were defined in the binding, and they don't appear to
have been inherited form another binding. What are they for? Why are
they needed?

> +
> +       clk = clk_register_psc(NULL, clk_name, parent_name,
> +                               data, lock);
> +
> +       if (clk) {
> +               of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +
> +               rc = of_property_read_string(node, "status", &status);
> +               if (status && !strcmp(status, "enabled"))
> +                       clk_prepare_enable(clk);
> +               return;
> +       }

As mentioned above, this abuses the standard status property, and it's
not clear why this is necessary.

Thanks,
Mark.

> +       pr_err("psc_clk_init - error registering psc clk %s\n", node->name);
> +out:
> +       kfree(data);
> +       return;
> +}
> +
> +/**
> + * of_keystone_psc_clk_init - initialize psc clock through DT
> + * @node: device tree node for this clock
> + */
> +void __init of_keystone_psc_clk_init(struct device_node *node)
> +{
> +       of_psc_clk_init(node, &psc_lock);
> +}
> +EXPORT_SYMBOL_GPL(of_keystone_psc_clk_init);
> +CLK_OF_DECLARE(keystone_gate_clk, "keystone,psc-clk", of_keystone_psc_clk_init);
> diff --git a/include/linux/clk/keystone.h b/include/linux/clk/keystone.h
> index 1ade95d..7b3e154 100644
> --- a/include/linux/clk/keystone.h
> +++ b/include/linux/clk/keystone.h
> @@ -14,5 +14,6 @@
>  #define __LINUX_CLK_KEYSTONE_H_
> 
>  extern void of_keystone_pll_clk_init(struct device_node *node);
> +extern void of_keystone_psc_clk_init(struct device_node *node);
> 
>  #endif /* __LINUX_CLK_KEYSTONE_H_ */
> --
> 1.7.9.5
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 



More information about the linux-arm-kernel mailing list