bus: ti-sysc: Add handling for clkctrl opt clocks
authorTony Lindgren <tony@atomide.com>
Mon, 16 Apr 2018 17:25:52 +0000 (10:25 -0700)
committerTony Lindgren <tony@atomide.com>
Mon, 30 Apr 2018 19:04:51 +0000 (12:04 -0700)
There can be up to eight optional device functional gate gate clocks for
each clkctrl instance in clkctrl register bits 8 to 15. Some of them are
only needed for module level reset while others may always be needed
during use. Let's add support for those and update the binding doc
accordingly.

Note that the optional clkctrl mux and divider clocks starting at bit 20
can be directly mapped to the child devices, and ti-sysc does not need to
manage those.

And as GPIOs need the optional clocks for reset, we can now add it with
SYSC_QUIRK_OPT_CLKS_IN_RESET.

Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Tero Kristo <t-kristo@ti.com>
Cc: devicetree@vger.kernel.org
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Documentation/devicetree/bindings/bus/ti-sysc.txt
drivers/bus/ti-sysc.c

index 2957a9ae291f4384ea145b61b2fb42876598a04c..d8ed5b780ed9d31dec1ee7b59aa6758abde4b41d 100644 (file)
@@ -79,7 +79,11 @@ Optional properties:
                mode as for example omap4 L4_CFG_CLKCTRL
 
 - clock-names  should contain at least "fck", and optionally also "ick"
-               depending on the SoC and the interconnect target module
+               depending on the SoC and the interconnect target module,
+               some interconnect target modules also need additional
+               optional clocks that can be specified as listed in TRM
+               for the related CLKCTRL register bits 8 to 15 such as
+               "dbclk" or "clk32k" depending on their role
 
 - ti,hwmods    optional TI interconnect module name to use legacy
                hwmod platform data
index e12d1580f21d8ca05f2daa4401fe5d08b2500eaa..f27b182384cd53fe4b55d87990b677564f63000c 100644 (file)
@@ -32,10 +32,18 @@ static const char * const reg_names[] = { "rev", "sysc", "syss", };
 enum sysc_clocks {
        SYSC_FCK,
        SYSC_ICK,
+       SYSC_OPTFCK0,
+       SYSC_OPTFCK1,
+       SYSC_OPTFCK2,
+       SYSC_OPTFCK3,
+       SYSC_OPTFCK4,
+       SYSC_OPTFCK5,
+       SYSC_OPTFCK6,
+       SYSC_OPTFCK7,
        SYSC_MAX_CLOCKS,
 };
 
-static const char * const clock_names[] = { "fck", "ick", };
+static const char * const clock_names[SYSC_ICK + 1] = { "fck", "ick", };
 
 #define SYSC_IDLEMODE_MASK             3
 #define SYSC_CLOCKACTIVITY_MASK                3
@@ -48,6 +56,8 @@ static const char * const clock_names[] = { "fck", "ick", };
  * @module_va: virtual address of the interconnect target module
  * @offsets: register offsets from module base
  * @clocks: clocks used by the interconnect target module
+ * @clock_roles: clock role names for the found clocks
+ * @nr_clocks: number of clocks used by the interconnect target module
  * @legacy_mode: configured for legacy mode if set
  * @cap: interconnect target module capabilities
  * @cfg: interconnect target module configuration
@@ -61,7 +71,9 @@ struct sysc {
        u32 module_size;
        void __iomem *module_va;
        int offsets[SYSC_MAX_REGS];
-       struct clk *clocks[SYSC_MAX_CLOCKS];
+       struct clk **clocks;
+       const char **clock_roles;
+       int nr_clocks;
        const char *legacy_mode;
        const struct sysc_capabilities *cap;
        struct sysc_config cfg;
@@ -88,6 +100,11 @@ static u32 sysc_read(struct sysc *ddata, int offset)
        return readl_relaxed(ddata->module_va + offset);
 }
 
+static bool sysc_opt_clks_needed(struct sysc *ddata)
+{
+       return !!(ddata->cfg.quirks & SYSC_QUIRK_OPT_CLKS_NEEDED);
+}
+
 static u32 sysc_read_revision(struct sysc *ddata)
 {
        int offset = ddata->offsets[SYSC_REVISION];
@@ -98,21 +115,28 @@ static u32 sysc_read_revision(struct sysc *ddata)
        return sysc_read(ddata, offset);
 }
 
-static int sysc_get_one_clock(struct sysc *ddata,
-                             enum sysc_clocks index)
+static int sysc_get_one_clock(struct sysc *ddata, const char *name)
 {
-       const char *name;
-       int error;
+       int error, i, index = -ENODEV;
+
+       if (!strncmp(clock_names[SYSC_FCK], name, 3))
+               index = SYSC_FCK;
+       else if (!strncmp(clock_names[SYSC_ICK], name, 3))
+               index = SYSC_ICK;
+
+       if (index < 0) {
+               for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) {
+                       if (!clock_names[i]) {
+                               index = i;
+                               break;
+                       }
+               }
+       }
 
-       switch (index) {
-       case SYSC_FCK:
-               break;
-       case SYSC_ICK:
-               break;
-       default:
-               return -EINVAL;
+       if (index < 0) {
+               dev_err(ddata->dev, "clock %s not added\n", name);
+               return index;
        }
-       name = clock_names[index];
 
        ddata->clocks[index] = devm_clk_get(ddata->dev, name);
        if (IS_ERR(ddata->clocks[index])) {
@@ -138,10 +162,50 @@ static int sysc_get_one_clock(struct sysc *ddata,
 
 static int sysc_get_clocks(struct sysc *ddata)
 {
-       int i, error;
+       struct device_node *np = ddata->dev->of_node;
+       struct property *prop;
+       const char *name;
+       int nr_fck = 0, nr_ick = 0, i, error = 0;
 
-       for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
-               error = sysc_get_one_clock(ddata, i);
+       ddata->clock_roles = devm_kzalloc(ddata->dev,
+                                         sizeof(*ddata->clock_roles) *
+                                         SYSC_MAX_CLOCKS,
+                                         GFP_KERNEL);
+       if (!ddata->clock_roles)
+               return -ENOMEM;
+
+       of_property_for_each_string(np, "clock-names", prop, name) {
+               if (!strncmp(clock_names[SYSC_FCK], name, 3))
+                       nr_fck++;
+               if (!strncmp(clock_names[SYSC_ICK], name, 3))
+                       nr_ick++;
+               ddata->clock_roles[ddata->nr_clocks] = name;
+               ddata->nr_clocks++;
+       }
+
+       if (ddata->nr_clocks < 1)
+               return 0;
+
+       if (ddata->nr_clocks > SYSC_MAX_CLOCKS) {
+               dev_err(ddata->dev, "too many clocks for %pOF\n", np);
+
+               return -EINVAL;
+       }
+
+       if (nr_fck > 1 || nr_ick > 1) {
+               dev_err(ddata->dev, "max one fck and ick for %pOF\n", np);
+
+               return -EINVAL;
+       }
+
+       ddata->clocks = devm_kzalloc(ddata->dev,
+                                    sizeof(*ddata->clocks) * ddata->nr_clocks,
+                                    GFP_KERNEL);
+       if (!ddata->clocks)
+               return -ENOMEM;
+
+       for (i = 0; i < ddata->nr_clocks; i++) {
+               error = sysc_get_one_clock(ddata, ddata->clock_roles[i]);
                if (error && error != -ENOENT)
                        return error;
        }
@@ -533,9 +597,13 @@ static int __maybe_unused sysc_runtime_suspend(struct device *dev)
                goto idled;
        }
 
-       for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
+       for (i = 0; i < ddata->nr_clocks; i++) {
                if (IS_ERR_OR_NULL(ddata->clocks[i]))
                        continue;
+
+               if (i >= SYSC_OPTFCK0 && !sysc_opt_clks_needed(ddata))
+                       break;
+
                clk_disable(ddata->clocks[i]);
        }
 
@@ -572,9 +640,13 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
                goto awake;
        }
 
-       for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
+       for (i = 0; i < ddata->nr_clocks; i++) {
                if (IS_ERR_OR_NULL(ddata->clocks[i]))
                        continue;
+
+               if (i >= SYSC_OPTFCK0 && !sysc_opt_clks_needed(ddata))
+                       break;
+
                error = clk_enable(ddata->clocks[i]);
                if (error)
                        return error;
@@ -651,7 +723,7 @@ struct sysc_revision_quirk {
 static const struct sysc_revision_quirk sysc_revision_quirks[] = {
        /* These drivers need to be fixed to not use pm_runtime_irq_safe() */
        SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffffffff,
-                  SYSC_QUIRK_LEGACY_IDLE),
+                  SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET),
        SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
                   SYSC_QUIRK_LEGACY_IDLE),
        SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000030, 0xffffffff,
@@ -845,6 +917,26 @@ static int sysc_child_add_named_clock(struct sysc *ddata,
        return error;
 }
 
+static int sysc_child_add_clocks(struct sysc *ddata,
+                                struct device *child)
+{
+       int i, error;
+
+       for (i = 0; i < ddata->nr_clocks; i++) {
+               error = sysc_child_add_named_clock(ddata,
+                                                  child,
+                                                  ddata->clock_roles[i]);
+               if (error && error != -EEXIST) {
+                       dev_err(ddata->dev, "could not add child clock %s: %i\n",
+                               ddata->clock_roles[i], error);
+
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
 static struct device_type sysc_device_type = {
 };
 
@@ -992,11 +1084,9 @@ static int sysc_notifier_call(struct notifier_block *nb,
 
        switch (event) {
        case BUS_NOTIFY_ADD_DEVICE:
-               error = sysc_child_add_named_clock(ddata, dev,
-                                                  clock_names[SYSC_FCK]);
-               if (error && error != -EEXIST)
-                       dev_warn(ddata->dev, "could not add %s fck: %i\n",
-                                dev_name(dev), error);
+               error = sysc_child_add_clocks(ddata, dev);
+               if (error)
+                       return error;
                sysc_legacy_idle_quirk(ddata, dev);
                break;
        default: