19e288cefSKuninori Morimoto // SPDX-License-Identifier: GPL-2.0 2b3a33077SSimon Horman /* 3b3a33077SSimon Horman * R-Car MSTP clocks 4b3a33077SSimon Horman * 5b3a33077SSimon Horman * Copyright (C) 2013 Ideas On Board SPRL 6b3a33077SSimon Horman * Copyright (C) 2015 Glider bvba 7b3a33077SSimon Horman * 8b3a33077SSimon Horman * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 9b3a33077SSimon Horman */ 10b3a33077SSimon Horman 11b3a33077SSimon Horman #include <linux/clk.h> 12b3a33077SSimon Horman #include <linux/clk-provider.h> 1309c32427SSimon Horman #include <linux/clk/renesas.h> 14b3a33077SSimon Horman #include <linux/device.h> 15b3a33077SSimon Horman #include <linux/io.h> 16d0414e76SGeert Uytterhoeven #include <linux/iopoll.h> 17b3a33077SSimon Horman #include <linux/of.h> 18b3a33077SSimon Horman #include <linux/of_address.h> 19b3a33077SSimon Horman #include <linux/pm_clock.h> 20b3a33077SSimon Horman #include <linux/pm_domain.h> 214ae2c995SGeert Uytterhoeven #include <linux/slab.h> 22b3a33077SSimon Horman #include <linux/spinlock.h> 23b3a33077SSimon Horman 24b3a33077SSimon Horman /* 25b3a33077SSimon Horman * MSTP clocks. We can't use standard gate clocks as we need to poll on the 26b3a33077SSimon Horman * status register when enabling the clock. 27b3a33077SSimon Horman */ 28b3a33077SSimon Horman 29b3a33077SSimon Horman #define MSTP_MAX_CLOCKS 32 30b3a33077SSimon Horman 31b3a33077SSimon Horman /** 32b3a33077SSimon Horman * struct mstp_clock_group - MSTP gating clocks group 33b3a33077SSimon Horman * 34a79f5836SGeert Uytterhoeven * @data: clock specifier translation for clocks in this group 35b3a33077SSimon Horman * @smstpcr: module stop control register 36b3a33077SSimon Horman * @mstpsr: module stop status register (optional) 37b3a33077SSimon Horman * @lock: protects writes to SMSTPCR 38e2a33c34SChris Brandt * @width_8bit: registers are 8-bit, not 32-bit 39a79f5836SGeert Uytterhoeven * @clks: clocks in this group 40b3a33077SSimon Horman */ 41b3a33077SSimon Horman struct mstp_clock_group { 42b3a33077SSimon Horman struct clk_onecell_data data; 43b3a33077SSimon Horman void __iomem *smstpcr; 44b3a33077SSimon Horman void __iomem *mstpsr; 45b3a33077SSimon Horman spinlock_t lock; 46e2a33c34SChris Brandt bool width_8bit; 47a79f5836SGeert Uytterhoeven struct clk *clks[]; 48b3a33077SSimon Horman }; 49b3a33077SSimon Horman 50b3a33077SSimon Horman /** 51b3a33077SSimon Horman * struct mstp_clock - MSTP gating clock 52b3a33077SSimon Horman * @hw: handle between common and hardware-specific interfaces 53b3a33077SSimon Horman * @bit_index: control bit index 54b3a33077SSimon Horman * @group: MSTP clocks group 55b3a33077SSimon Horman */ 56b3a33077SSimon Horman struct mstp_clock { 57b3a33077SSimon Horman struct clk_hw hw; 58b3a33077SSimon Horman u32 bit_index; 59b3a33077SSimon Horman struct mstp_clock_group *group; 60b3a33077SSimon Horman }; 61b3a33077SSimon Horman 62b3a33077SSimon Horman #define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw) 63b3a33077SSimon Horman 64e2a33c34SChris Brandt static inline u32 cpg_mstp_read(struct mstp_clock_group *group, 65e2a33c34SChris Brandt u32 __iomem *reg) 66e2a33c34SChris Brandt { 6780275198SGeert Uytterhoeven return group->width_8bit ? readb(reg) : readl(reg); 68e2a33c34SChris Brandt } 69e2a33c34SChris Brandt 70e2a33c34SChris Brandt static inline void cpg_mstp_write(struct mstp_clock_group *group, u32 val, 71e2a33c34SChris Brandt u32 __iomem *reg) 72e2a33c34SChris Brandt { 7380275198SGeert Uytterhoeven group->width_8bit ? writeb(val, reg) : writel(val, reg); 74e2a33c34SChris Brandt } 75e2a33c34SChris Brandt 76b3a33077SSimon Horman static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) 77b3a33077SSimon Horman { 78b3a33077SSimon Horman struct mstp_clock *clock = to_mstp_clock(hw); 79b3a33077SSimon Horman struct mstp_clock_group *group = clock->group; 80b3a33077SSimon Horman u32 bitmask = BIT(clock->bit_index); 81b3a33077SSimon Horman unsigned long flags; 82b3a33077SSimon Horman u32 value; 83d0414e76SGeert Uytterhoeven int ret; 84b3a33077SSimon Horman 85b3a33077SSimon Horman spin_lock_irqsave(&group->lock, flags); 86b3a33077SSimon Horman 87e2a33c34SChris Brandt value = cpg_mstp_read(group, group->smstpcr); 88b3a33077SSimon Horman if (enable) 89b3a33077SSimon Horman value &= ~bitmask; 90b3a33077SSimon Horman else 91b3a33077SSimon Horman value |= bitmask; 92e2a33c34SChris Brandt cpg_mstp_write(group, value, group->smstpcr); 93b3a33077SSimon Horman 94f59de563SChris Brandt if (!group->mstpsr) { 95f59de563SChris Brandt /* dummy read to ensure write has completed */ 96f59de563SChris Brandt cpg_mstp_read(group, group->smstpcr); 97f59de563SChris Brandt barrier_data(group->smstpcr); 98f59de563SChris Brandt } 99f59de563SChris Brandt 100b3a33077SSimon Horman spin_unlock_irqrestore(&group->lock, flags); 101b3a33077SSimon Horman 102b3a33077SSimon Horman if (!enable || !group->mstpsr) 103b3a33077SSimon Horman return 0; 104b3a33077SSimon Horman 105d0414e76SGeert Uytterhoeven /* group->width_8bit is always false if group->mstpsr is present */ 106d0414e76SGeert Uytterhoeven ret = readl_poll_timeout_atomic(group->mstpsr, value, 107d0414e76SGeert Uytterhoeven !(value & bitmask), 0, 10); 108d0414e76SGeert Uytterhoeven if (ret) 109b3a33077SSimon Horman pr_err("%s: failed to enable %p[%d]\n", __func__, 110b3a33077SSimon Horman group->smstpcr, clock->bit_index); 111b3a33077SSimon Horman 112d0414e76SGeert Uytterhoeven return ret; 113b3a33077SSimon Horman } 114b3a33077SSimon Horman 115b3a33077SSimon Horman static int cpg_mstp_clock_enable(struct clk_hw *hw) 116b3a33077SSimon Horman { 117b3a33077SSimon Horman return cpg_mstp_clock_endisable(hw, true); 118b3a33077SSimon Horman } 119b3a33077SSimon Horman 120b3a33077SSimon Horman static void cpg_mstp_clock_disable(struct clk_hw *hw) 121b3a33077SSimon Horman { 122b3a33077SSimon Horman cpg_mstp_clock_endisable(hw, false); 123b3a33077SSimon Horman } 124b3a33077SSimon Horman 125b3a33077SSimon Horman static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) 126b3a33077SSimon Horman { 127b3a33077SSimon Horman struct mstp_clock *clock = to_mstp_clock(hw); 128b3a33077SSimon Horman struct mstp_clock_group *group = clock->group; 129b3a33077SSimon Horman u32 value; 130b3a33077SSimon Horman 131b3a33077SSimon Horman if (group->mstpsr) 132e2a33c34SChris Brandt value = cpg_mstp_read(group, group->mstpsr); 133b3a33077SSimon Horman else 134e2a33c34SChris Brandt value = cpg_mstp_read(group, group->smstpcr); 135b3a33077SSimon Horman 136b3a33077SSimon Horman return !(value & BIT(clock->bit_index)); 137b3a33077SSimon Horman } 138b3a33077SSimon Horman 139b3a33077SSimon Horman static const struct clk_ops cpg_mstp_clock_ops = { 140b3a33077SSimon Horman .enable = cpg_mstp_clock_enable, 141b3a33077SSimon Horman .disable = cpg_mstp_clock_disable, 142b3a33077SSimon Horman .is_enabled = cpg_mstp_clock_is_enabled, 143b3a33077SSimon Horman }; 144b3a33077SSimon Horman 1451ce87dd2SGeert Uytterhoeven static struct clk * __init cpg_mstp_clock_register(const char *name, 1461ce87dd2SGeert Uytterhoeven const char *parent_name, unsigned int index, 1471ce87dd2SGeert Uytterhoeven struct mstp_clock_group *group) 148b3a33077SSimon Horman { 149f2fb4fe6SGeert Uytterhoeven struct clk_init_data init = {}; 150b3a33077SSimon Horman struct mstp_clock *clock; 151b3a33077SSimon Horman struct clk *clk; 152b3a33077SSimon Horman 153b3a33077SSimon Horman clock = kzalloc(sizeof(*clock), GFP_KERNEL); 154168bcbcbSMarkus Elfring if (!clock) 155b3a33077SSimon Horman return ERR_PTR(-ENOMEM); 156b3a33077SSimon Horman 157b3a33077SSimon Horman init.name = name; 158b3a33077SSimon Horman init.ops = &cpg_mstp_clock_ops; 159ddbae665SStephen Boyd init.flags = CLK_SET_RATE_PARENT; 160e34084fbSGeert Uytterhoeven /* INTC-SYS is the module clock of the GIC, and must not be disabled */ 161e34084fbSGeert Uytterhoeven if (!strcmp(name, "intc-sys")) { 162e34084fbSGeert Uytterhoeven pr_debug("MSTP %s setting CLK_IS_CRITICAL\n", name); 163e34084fbSGeert Uytterhoeven init.flags |= CLK_IS_CRITICAL; 164e34084fbSGeert Uytterhoeven } 165b3a33077SSimon Horman init.parent_names = &parent_name; 166b3a33077SSimon Horman init.num_parents = 1; 167b3a33077SSimon Horman 168b3a33077SSimon Horman clock->bit_index = index; 169b3a33077SSimon Horman clock->group = group; 170b3a33077SSimon Horman clock->hw.init = &init; 171b3a33077SSimon Horman 172b3a33077SSimon Horman clk = clk_register(NULL, &clock->hw); 173b3a33077SSimon Horman 174b3a33077SSimon Horman if (IS_ERR(clk)) 175b3a33077SSimon Horman kfree(clock); 176b3a33077SSimon Horman 177b3a33077SSimon Horman return clk; 178b3a33077SSimon Horman } 179b3a33077SSimon Horman 180b3a33077SSimon Horman static void __init cpg_mstp_clocks_init(struct device_node *np) 181b3a33077SSimon Horman { 182b3a33077SSimon Horman struct mstp_clock_group *group; 183b3a33077SSimon Horman const char *idxname; 184b3a33077SSimon Horman struct clk **clks; 185b3a33077SSimon Horman unsigned int i; 186b3a33077SSimon Horman 187a79f5836SGeert Uytterhoeven group = kzalloc(struct_size(group, clks, MSTP_MAX_CLOCKS), GFP_KERNEL); 188ba28236eSMarkus Elfring if (!group) 189b3a33077SSimon Horman return; 190b3a33077SSimon Horman 191a79f5836SGeert Uytterhoeven clks = group->clks; 192b3a33077SSimon Horman spin_lock_init(&group->lock); 193b3a33077SSimon Horman group->data.clks = clks; 194b3a33077SSimon Horman 195b3a33077SSimon Horman group->smstpcr = of_iomap(np, 0); 196b3a33077SSimon Horman group->mstpsr = of_iomap(np, 1); 197b3a33077SSimon Horman 198b3a33077SSimon Horman if (group->smstpcr == NULL) { 199b3a33077SSimon Horman pr_err("%s: failed to remap SMSTPCR\n", __func__); 200b3a33077SSimon Horman kfree(group); 201b3a33077SSimon Horman return; 202b3a33077SSimon Horman } 203b3a33077SSimon Horman 204e2a33c34SChris Brandt if (of_device_is_compatible(np, "renesas,r7s72100-mstp-clocks")) 205e2a33c34SChris Brandt group->width_8bit = true; 206e2a33c34SChris Brandt 207b3a33077SSimon Horman for (i = 0; i < MSTP_MAX_CLOCKS; ++i) 208b3a33077SSimon Horman clks[i] = ERR_PTR(-ENOENT); 209b3a33077SSimon Horman 210*66b06523SRob Herring (Arm) if (of_property_present(np, "clock-indices")) 211b3a33077SSimon Horman idxname = "clock-indices"; 212b3a33077SSimon Horman else 213b3a33077SSimon Horman idxname = "renesas,clock-indices"; 214b3a33077SSimon Horman 215b3a33077SSimon Horman for (i = 0; i < MSTP_MAX_CLOCKS; ++i) { 216b3a33077SSimon Horman const char *parent_name; 217b3a33077SSimon Horman const char *name; 218b3a33077SSimon Horman u32 clkidx; 219b3a33077SSimon Horman int ret; 220b3a33077SSimon Horman 221b3a33077SSimon Horman /* Skip clocks with no name. */ 222b3a33077SSimon Horman ret = of_property_read_string_index(np, "clock-output-names", 223b3a33077SSimon Horman i, &name); 224b3a33077SSimon Horman if (ret < 0 || strlen(name) == 0) 225b3a33077SSimon Horman continue; 226b3a33077SSimon Horman 227b3a33077SSimon Horman parent_name = of_clk_get_parent_name(np, i); 228b3a33077SSimon Horman ret = of_property_read_u32_index(np, idxname, i, &clkidx); 229b3a33077SSimon Horman if (parent_name == NULL || ret < 0) 230b3a33077SSimon Horman break; 231b3a33077SSimon Horman 232b3a33077SSimon Horman if (clkidx >= MSTP_MAX_CLOCKS) { 233e665f029SRob Herring pr_err("%s: invalid clock %pOFn %s index %u\n", 234e665f029SRob Herring __func__, np, name, clkidx); 235b3a33077SSimon Horman continue; 236b3a33077SSimon Horman } 237b3a33077SSimon Horman 238b3a33077SSimon Horman clks[clkidx] = cpg_mstp_clock_register(name, parent_name, 239b3a33077SSimon Horman clkidx, group); 2404ae2c995SGeert Uytterhoeven if (!IS_ERR(clks[clkidx])) 241b3a33077SSimon Horman group->data.clk_num = max(group->data.clk_num, 242b3a33077SSimon Horman clkidx + 1); 2434ae2c995SGeert Uytterhoeven else 244e665f029SRob Herring pr_err("%s: failed to register %pOFn %s clock (%ld)\n", 245e665f029SRob Herring __func__, np, name, PTR_ERR(clks[clkidx])); 246b3a33077SSimon Horman } 247b3a33077SSimon Horman 248b3a33077SSimon Horman of_clk_add_provider(np, of_clk_src_onecell_get, &group->data); 249b3a33077SSimon Horman } 250b3a33077SSimon Horman CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init); 251b3a33077SSimon Horman 25212a56817SGeert Uytterhoeven int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev) 253b3a33077SSimon Horman { 254b3a33077SSimon Horman struct device_node *np = dev->of_node; 255b3a33077SSimon Horman struct of_phandle_args clkspec; 256b3a33077SSimon Horman struct clk *clk; 257b3a33077SSimon Horman int i = 0; 258b3a33077SSimon Horman int error; 259b3a33077SSimon Horman 260b3a33077SSimon Horman while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, 261b3a33077SSimon Horman &clkspec)) { 262b3a33077SSimon Horman if (of_device_is_compatible(clkspec.np, 263b3a33077SSimon Horman "renesas,cpg-mstp-clocks")) 264b3a33077SSimon Horman goto found; 265b3a33077SSimon Horman 266b3a33077SSimon Horman /* BSC on r8a73a4/sh73a0 uses zb_clk instead of an mstp clock */ 26787ab1151SRob Herring if (of_node_name_eq(clkspec.np, "zb_clk")) 268b3a33077SSimon Horman goto found; 269b3a33077SSimon Horman 270b3a33077SSimon Horman of_node_put(clkspec.np); 271b3a33077SSimon Horman i++; 272b3a33077SSimon Horman } 273b3a33077SSimon Horman 274b3a33077SSimon Horman return 0; 275b3a33077SSimon Horman 276b3a33077SSimon Horman found: 277b3a33077SSimon Horman clk = of_clk_get_from_provider(&clkspec); 278b3a33077SSimon Horman of_node_put(clkspec.np); 279b3a33077SSimon Horman 280b3a33077SSimon Horman if (IS_ERR(clk)) 281b3a33077SSimon Horman return PTR_ERR(clk); 282b3a33077SSimon Horman 283b3a33077SSimon Horman error = pm_clk_create(dev); 2840f7ece0dSGeert Uytterhoeven if (error) 285b3a33077SSimon Horman goto fail_put; 286b3a33077SSimon Horman 287b3a33077SSimon Horman error = pm_clk_add_clk(dev, clk); 2880f7ece0dSGeert Uytterhoeven if (error) 289b3a33077SSimon Horman goto fail_destroy; 290b3a33077SSimon Horman 291b3a33077SSimon Horman return 0; 292b3a33077SSimon Horman 293b3a33077SSimon Horman fail_destroy: 294b3a33077SSimon Horman pm_clk_destroy(dev); 295b3a33077SSimon Horman fail_put: 296b3a33077SSimon Horman clk_put(clk); 297b3a33077SSimon Horman return error; 298b3a33077SSimon Horman } 299b3a33077SSimon Horman 30012a56817SGeert Uytterhoeven void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev) 301b3a33077SSimon Horman { 302e05e853eSGeert Uytterhoeven if (!pm_clk_no_clocks(dev)) 303b3a33077SSimon Horman pm_clk_destroy(dev); 304b3a33077SSimon Horman } 305b3a33077SSimon Horman 306b3a33077SSimon Horman void __init cpg_mstp_add_clk_domain(struct device_node *np) 307b3a33077SSimon Horman { 308b3a33077SSimon Horman struct generic_pm_domain *pd; 309b3a33077SSimon Horman u32 ncells; 310b3a33077SSimon Horman 311b3a33077SSimon Horman if (of_property_read_u32(np, "#power-domain-cells", &ncells)) { 31216673931SRob Herring pr_warn("%pOF lacks #power-domain-cells\n", np); 313b3a33077SSimon Horman return; 314b3a33077SSimon Horman } 315b3a33077SSimon Horman 316b3a33077SSimon Horman pd = kzalloc(sizeof(*pd), GFP_KERNEL); 317b3a33077SSimon Horman if (!pd) 318b3a33077SSimon Horman return; 319b3a33077SSimon Horman 320b3a33077SSimon Horman pd->name = np->name; 321a459a184SGeert Uytterhoeven pd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON | 322a459a184SGeert Uytterhoeven GENPD_FLAG_ACTIVE_WAKEUP; 323b3a33077SSimon Horman pd->attach_dev = cpg_mstp_attach_dev; 324b3a33077SSimon Horman pd->detach_dev = cpg_mstp_detach_dev; 32520729300SGeert Uytterhoeven pm_genpd_init(pd, &pm_domain_always_on_gov, false); 326b3a33077SSimon Horman 327b3a33077SSimon Horman of_genpd_add_provider_simple(np, pd); 328b3a33077SSimon Horman } 329