18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * R-Car MSTP clocks 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Ideas On Board SPRL 68c2ecf20Sopenharmony_ci * Copyright (C) 2015 Glider bvba 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 138c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 148c2ecf20Sopenharmony_ci#include <linux/clk/renesas.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_address.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_clock.h> 208c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 218c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * MSTP clocks. We can't use standard gate clocks as we need to poll on the 258c2ecf20Sopenharmony_ci * status register when enabling the clock. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define MSTP_MAX_CLOCKS 32 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/** 318c2ecf20Sopenharmony_ci * struct mstp_clock_group - MSTP gating clocks group 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * @data: clock specifier translation for clocks in this group 348c2ecf20Sopenharmony_ci * @smstpcr: module stop control register 358c2ecf20Sopenharmony_ci * @mstpsr: module stop status register (optional) 368c2ecf20Sopenharmony_ci * @lock: protects writes to SMSTPCR 378c2ecf20Sopenharmony_ci * @width_8bit: registers are 8-bit, not 32-bit 388c2ecf20Sopenharmony_ci * @clks: clocks in this group 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistruct mstp_clock_group { 418c2ecf20Sopenharmony_ci struct clk_onecell_data data; 428c2ecf20Sopenharmony_ci void __iomem *smstpcr; 438c2ecf20Sopenharmony_ci void __iomem *mstpsr; 448c2ecf20Sopenharmony_ci spinlock_t lock; 458c2ecf20Sopenharmony_ci bool width_8bit; 468c2ecf20Sopenharmony_ci struct clk *clks[]; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/** 508c2ecf20Sopenharmony_ci * struct mstp_clock - MSTP gating clock 518c2ecf20Sopenharmony_ci * @hw: handle between common and hardware-specific interfaces 528c2ecf20Sopenharmony_ci * @bit_index: control bit index 538c2ecf20Sopenharmony_ci * @group: MSTP clocks group 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistruct mstp_clock { 568c2ecf20Sopenharmony_ci struct clk_hw hw; 578c2ecf20Sopenharmony_ci u32 bit_index; 588c2ecf20Sopenharmony_ci struct mstp_clock_group *group; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic inline u32 cpg_mstp_read(struct mstp_clock_group *group, 648c2ecf20Sopenharmony_ci u32 __iomem *reg) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci return group->width_8bit ? readb(reg) : readl(reg); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline void cpg_mstp_write(struct mstp_clock_group *group, u32 val, 708c2ecf20Sopenharmony_ci u32 __iomem *reg) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci group->width_8bit ? writeb(val, reg) : writel(val, reg); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct mstp_clock *clock = to_mstp_clock(hw); 788c2ecf20Sopenharmony_ci struct mstp_clock_group *group = clock->group; 798c2ecf20Sopenharmony_ci u32 bitmask = BIT(clock->bit_index); 808c2ecf20Sopenharmony_ci unsigned long flags; 818c2ecf20Sopenharmony_ci unsigned int i; 828c2ecf20Sopenharmony_ci u32 value; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci spin_lock_irqsave(&group->lock, flags); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci value = cpg_mstp_read(group, group->smstpcr); 878c2ecf20Sopenharmony_ci if (enable) 888c2ecf20Sopenharmony_ci value &= ~bitmask; 898c2ecf20Sopenharmony_ci else 908c2ecf20Sopenharmony_ci value |= bitmask; 918c2ecf20Sopenharmony_ci cpg_mstp_write(group, value, group->smstpcr); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!group->mstpsr) { 948c2ecf20Sopenharmony_ci /* dummy read to ensure write has completed */ 958c2ecf20Sopenharmony_ci cpg_mstp_read(group, group->smstpcr); 968c2ecf20Sopenharmony_ci barrier_data(group->smstpcr); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&group->lock, flags); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (!enable || !group->mstpsr) 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci for (i = 1000; i > 0; --i) { 1058c2ecf20Sopenharmony_ci if (!(cpg_mstp_read(group, group->mstpsr) & bitmask)) 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci cpu_relax(); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!i) { 1118c2ecf20Sopenharmony_ci pr_err("%s: failed to enable %p[%d]\n", __func__, 1128c2ecf20Sopenharmony_ci group->smstpcr, clock->bit_index); 1138c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int cpg_mstp_clock_enable(struct clk_hw *hw) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci return cpg_mstp_clock_endisable(hw, true); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void cpg_mstp_clock_disable(struct clk_hw *hw) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci cpg_mstp_clock_endisable(hw, false); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int cpg_mstp_clock_is_enabled(struct clk_hw *hw) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct mstp_clock *clock = to_mstp_clock(hw); 1328c2ecf20Sopenharmony_ci struct mstp_clock_group *group = clock->group; 1338c2ecf20Sopenharmony_ci u32 value; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (group->mstpsr) 1368c2ecf20Sopenharmony_ci value = cpg_mstp_read(group, group->mstpsr); 1378c2ecf20Sopenharmony_ci else 1388c2ecf20Sopenharmony_ci value = cpg_mstp_read(group, group->smstpcr); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return !(value & BIT(clock->bit_index)); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct clk_ops cpg_mstp_clock_ops = { 1448c2ecf20Sopenharmony_ci .enable = cpg_mstp_clock_enable, 1458c2ecf20Sopenharmony_ci .disable = cpg_mstp_clock_disable, 1468c2ecf20Sopenharmony_ci .is_enabled = cpg_mstp_clock_is_enabled, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct clk * __init cpg_mstp_clock_register(const char *name, 1508c2ecf20Sopenharmony_ci const char *parent_name, unsigned int index, 1518c2ecf20Sopenharmony_ci struct mstp_clock_group *group) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct clk_init_data init; 1548c2ecf20Sopenharmony_ci struct mstp_clock *clock; 1558c2ecf20Sopenharmony_ci struct clk *clk; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci clock = kzalloc(sizeof(*clock), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!clock) 1598c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci init.name = name; 1628c2ecf20Sopenharmony_ci init.ops = &cpg_mstp_clock_ops; 1638c2ecf20Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 1648c2ecf20Sopenharmony_ci /* INTC-SYS is the module clock of the GIC, and must not be disabled */ 1658c2ecf20Sopenharmony_ci if (!strcmp(name, "intc-sys")) { 1668c2ecf20Sopenharmony_ci pr_debug("MSTP %s setting CLK_IS_CRITICAL\n", name); 1678c2ecf20Sopenharmony_ci init.flags |= CLK_IS_CRITICAL; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1708c2ecf20Sopenharmony_ci init.num_parents = 1; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci clock->bit_index = index; 1738c2ecf20Sopenharmony_ci clock->group = group; 1748c2ecf20Sopenharmony_ci clock->hw.init = &init; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci clk = clk_register(NULL, &clock->hw); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 1798c2ecf20Sopenharmony_ci kfree(clock); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return clk; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void __init cpg_mstp_clocks_init(struct device_node *np) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct mstp_clock_group *group; 1878c2ecf20Sopenharmony_ci const char *idxname; 1888c2ecf20Sopenharmony_ci struct clk **clks; 1898c2ecf20Sopenharmony_ci unsigned int i; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci group = kzalloc(struct_size(group, clks, MSTP_MAX_CLOCKS), GFP_KERNEL); 1928c2ecf20Sopenharmony_ci if (!group) 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci clks = group->clks; 1968c2ecf20Sopenharmony_ci spin_lock_init(&group->lock); 1978c2ecf20Sopenharmony_ci group->data.clks = clks; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci group->smstpcr = of_iomap(np, 0); 2008c2ecf20Sopenharmony_ci group->mstpsr = of_iomap(np, 1); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (group->smstpcr == NULL) { 2038c2ecf20Sopenharmony_ci pr_err("%s: failed to remap SMSTPCR\n", __func__); 2048c2ecf20Sopenharmony_ci kfree(group); 2058c2ecf20Sopenharmony_ci return; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "renesas,r7s72100-mstp-clocks")) 2098c2ecf20Sopenharmony_ci group->width_8bit = true; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (i = 0; i < MSTP_MAX_CLOCKS; ++i) 2128c2ecf20Sopenharmony_ci clks[i] = ERR_PTR(-ENOENT); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (of_find_property(np, "clock-indices", &i)) 2158c2ecf20Sopenharmony_ci idxname = "clock-indices"; 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci idxname = "renesas,clock-indices"; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (i = 0; i < MSTP_MAX_CLOCKS; ++i) { 2208c2ecf20Sopenharmony_ci const char *parent_name; 2218c2ecf20Sopenharmony_ci const char *name; 2228c2ecf20Sopenharmony_ci u32 clkidx; 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Skip clocks with no name. */ 2268c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, "clock-output-names", 2278c2ecf20Sopenharmony_ci i, &name); 2288c2ecf20Sopenharmony_ci if (ret < 0 || strlen(name) == 0) 2298c2ecf20Sopenharmony_ci continue; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci parent_name = of_clk_get_parent_name(np, i); 2328c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, idxname, i, &clkidx); 2338c2ecf20Sopenharmony_ci if (parent_name == NULL || ret < 0) 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (clkidx >= MSTP_MAX_CLOCKS) { 2378c2ecf20Sopenharmony_ci pr_err("%s: invalid clock %pOFn %s index %u\n", 2388c2ecf20Sopenharmony_ci __func__, np, name, clkidx); 2398c2ecf20Sopenharmony_ci continue; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci clks[clkidx] = cpg_mstp_clock_register(name, parent_name, 2438c2ecf20Sopenharmony_ci clkidx, group); 2448c2ecf20Sopenharmony_ci if (!IS_ERR(clks[clkidx])) { 2458c2ecf20Sopenharmony_ci group->data.clk_num = max(group->data.clk_num, 2468c2ecf20Sopenharmony_ci clkidx + 1); 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * Register a clkdev to let board code retrieve the 2498c2ecf20Sopenharmony_ci * clock by name and register aliases for non-DT 2508c2ecf20Sopenharmony_ci * devices. 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * FIXME: Remove this when all devices that require a 2538c2ecf20Sopenharmony_ci * clock will be instantiated from DT. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci clk_register_clkdev(clks[clkidx], name, NULL); 2568c2ecf20Sopenharmony_ci } else { 2578c2ecf20Sopenharmony_ci pr_err("%s: failed to register %pOFn %s clock (%ld)\n", 2588c2ecf20Sopenharmony_ci __func__, np, name, PTR_ERR(clks[clkidx])); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, &group->data); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ciCLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciint cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 2698c2ecf20Sopenharmony_ci struct of_phandle_args clkspec; 2708c2ecf20Sopenharmony_ci struct clk *clk; 2718c2ecf20Sopenharmony_ci int i = 0; 2728c2ecf20Sopenharmony_ci int error; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, 2758c2ecf20Sopenharmony_ci &clkspec)) { 2768c2ecf20Sopenharmony_ci if (of_device_is_compatible(clkspec.np, 2778c2ecf20Sopenharmony_ci "renesas,cpg-mstp-clocks")) 2788c2ecf20Sopenharmony_ci goto found; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* BSC on r8a73a4/sh73a0 uses zb_clk instead of an mstp clock */ 2818c2ecf20Sopenharmony_ci if (of_node_name_eq(clkspec.np, "zb_clk")) 2828c2ecf20Sopenharmony_ci goto found; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci of_node_put(clkspec.np); 2858c2ecf20Sopenharmony_ci i++; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cifound: 2918c2ecf20Sopenharmony_ci clk = of_clk_get_from_provider(&clkspec); 2928c2ecf20Sopenharmony_ci of_node_put(clkspec.np); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 2958c2ecf20Sopenharmony_ci return PTR_ERR(clk); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci error = pm_clk_create(dev); 2988c2ecf20Sopenharmony_ci if (error) 2998c2ecf20Sopenharmony_ci goto fail_put; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci error = pm_clk_add_clk(dev, clk); 3028c2ecf20Sopenharmony_ci if (error) 3038c2ecf20Sopenharmony_ci goto fail_destroy; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cifail_destroy: 3088c2ecf20Sopenharmony_ci pm_clk_destroy(dev); 3098c2ecf20Sopenharmony_cifail_put: 3108c2ecf20Sopenharmony_ci clk_put(clk); 3118c2ecf20Sopenharmony_ci return error; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_civoid cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci if (!pm_clk_no_clocks(dev)) 3178c2ecf20Sopenharmony_ci pm_clk_destroy(dev); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_civoid __init cpg_mstp_add_clk_domain(struct device_node *np) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct generic_pm_domain *pd; 3238c2ecf20Sopenharmony_ci u32 ncells; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "#power-domain-cells", &ncells)) { 3268c2ecf20Sopenharmony_ci pr_warn("%pOF lacks #power-domain-cells\n", np); 3278c2ecf20Sopenharmony_ci return; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pd = kzalloc(sizeof(*pd), GFP_KERNEL); 3318c2ecf20Sopenharmony_ci if (!pd) 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci pd->name = np->name; 3358c2ecf20Sopenharmony_ci pd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON | 3368c2ecf20Sopenharmony_ci GENPD_FLAG_ACTIVE_WAKEUP; 3378c2ecf20Sopenharmony_ci pd->attach_dev = cpg_mstp_attach_dev; 3388c2ecf20Sopenharmony_ci pd->detach_dev = cpg_mstp_detach_dev; 3398c2ecf20Sopenharmony_ci pm_genpd_init(pd, &pm_domain_always_on_gov, false); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci of_genpd_add_provider_simple(np, pd); 3428c2ecf20Sopenharmony_ci} 343