19fcb6be3SA.s. Dong // SPDX-License-Identifier: GPL-2.0+ 29fcb6be3SA.s. Dong /* 39fcb6be3SA.s. Dong * Copyright (C) 2016 Freescale Semiconductor, Inc. 49fcb6be3SA.s. Dong * Copyright 2017~2018 NXP 59fcb6be3SA.s. Dong * 69fcb6be3SA.s. Dong * Author: Dong Aisheng <aisheng.dong@nxp.com> 79fcb6be3SA.s. Dong * 89fcb6be3SA.s. Dong */ 99fcb6be3SA.s. Dong 109fcb6be3SA.s. Dong #include <linux/clk-provider.h> 119fcb6be3SA.s. Dong #include <linux/err.h> 1262e59c4eSStephen Boyd #include <linux/io.h> 139fcb6be3SA.s. Dong #include <linux/iopoll.h> 149fcb6be3SA.s. Dong #include <linux/slab.h> 159fcb6be3SA.s. Dong 169fcb6be3SA.s. Dong #include "clk.h" 179fcb6be3SA.s. Dong 189fcb6be3SA.s. Dong /** 199fcb6be3SA.s. Dong * struct clk_pfdv2 - IMX PFD clock 20cca87e5cSKrzysztof Kozlowski * @hw: clock source 219fcb6be3SA.s. Dong * @reg: PFD register address 229fcb6be3SA.s. Dong * @gate_bit: Gate bit offset 239fcb6be3SA.s. Dong * @vld_bit: Valid bit offset 249fcb6be3SA.s. Dong * @frac_off: PLL Fractional Divider offset 259fcb6be3SA.s. Dong */ 269fcb6be3SA.s. Dong 279fcb6be3SA.s. Dong struct clk_pfdv2 { 289fcb6be3SA.s. Dong struct clk_hw hw; 299fcb6be3SA.s. Dong void __iomem *reg; 309fcb6be3SA.s. Dong u8 gate_bit; 319fcb6be3SA.s. Dong u8 vld_bit; 329fcb6be3SA.s. Dong u8 frac_off; 339fcb6be3SA.s. Dong }; 349fcb6be3SA.s. Dong 359fcb6be3SA.s. Dong #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw) 369fcb6be3SA.s. Dong 379fcb6be3SA.s. Dong #define CLK_PFDV2_FRAC_MASK 0x3f 389fcb6be3SA.s. Dong 399fcb6be3SA.s. Dong #define LOCK_TIMEOUT_US USEC_PER_MSEC 409fcb6be3SA.s. Dong 419fcb6be3SA.s. Dong static DEFINE_SPINLOCK(pfd_lock); 429fcb6be3SA.s. Dong 439fcb6be3SA.s. Dong static int clk_pfdv2_wait(struct clk_pfdv2 *pfd) 449fcb6be3SA.s. Dong { 459fcb6be3SA.s. Dong u32 val; 469fcb6be3SA.s. Dong 47a5a627c6SAnson Huang return readl_poll_timeout(pfd->reg, val, val & (1 << pfd->vld_bit), 489fcb6be3SA.s. Dong 0, LOCK_TIMEOUT_US); 499fcb6be3SA.s. Dong } 509fcb6be3SA.s. Dong 519fcb6be3SA.s. Dong static int clk_pfdv2_enable(struct clk_hw *hw) 529fcb6be3SA.s. Dong { 539fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 549fcb6be3SA.s. Dong unsigned long flags; 559fcb6be3SA.s. Dong u32 val; 569fcb6be3SA.s. Dong 579fcb6be3SA.s. Dong spin_lock_irqsave(&pfd_lock, flags); 589fcb6be3SA.s. Dong val = readl_relaxed(pfd->reg); 59a5a627c6SAnson Huang val &= ~(1 << pfd->gate_bit); 609fcb6be3SA.s. Dong writel_relaxed(val, pfd->reg); 619fcb6be3SA.s. Dong spin_unlock_irqrestore(&pfd_lock, flags); 629fcb6be3SA.s. Dong 639fcb6be3SA.s. Dong return clk_pfdv2_wait(pfd); 649fcb6be3SA.s. Dong } 659fcb6be3SA.s. Dong 669fcb6be3SA.s. Dong static void clk_pfdv2_disable(struct clk_hw *hw) 679fcb6be3SA.s. Dong { 689fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 699fcb6be3SA.s. Dong unsigned long flags; 709fcb6be3SA.s. Dong u32 val; 719fcb6be3SA.s. Dong 729fcb6be3SA.s. Dong spin_lock_irqsave(&pfd_lock, flags); 739fcb6be3SA.s. Dong val = readl_relaxed(pfd->reg); 74a5a627c6SAnson Huang val |= (1 << pfd->gate_bit); 759fcb6be3SA.s. Dong writel_relaxed(val, pfd->reg); 769fcb6be3SA.s. Dong spin_unlock_irqrestore(&pfd_lock, flags); 779fcb6be3SA.s. Dong } 789fcb6be3SA.s. Dong 799fcb6be3SA.s. Dong static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw, 809fcb6be3SA.s. Dong unsigned long parent_rate) 819fcb6be3SA.s. Dong { 829fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 839fcb6be3SA.s. Dong u64 tmp = parent_rate; 849fcb6be3SA.s. Dong u8 frac; 859fcb6be3SA.s. Dong 869fcb6be3SA.s. Dong frac = (readl_relaxed(pfd->reg) >> pfd->frac_off) 879fcb6be3SA.s. Dong & CLK_PFDV2_FRAC_MASK; 889fcb6be3SA.s. Dong 899fcb6be3SA.s. Dong if (!frac) { 909fcb6be3SA.s. Dong pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n", 919fcb6be3SA.s. Dong clk_hw_get_name(hw)); 929fcb6be3SA.s. Dong return 0; 939fcb6be3SA.s. Dong } 949fcb6be3SA.s. Dong 959fcb6be3SA.s. Dong tmp *= 18; 969fcb6be3SA.s. Dong do_div(tmp, frac); 979fcb6be3SA.s. Dong 989fcb6be3SA.s. Dong return tmp; 999fcb6be3SA.s. Dong } 1009fcb6be3SA.s. Dong 1018ffe9c7bSPeng Fan static int clk_pfdv2_determine_rate(struct clk_hw *hw, 1028ffe9c7bSPeng Fan struct clk_rate_request *req) 1039fcb6be3SA.s. Dong { 104c88a4c79SPeng Fan unsigned long parent_rates[] = { 105c88a4c79SPeng Fan 480000000, 106c88a4c79SPeng Fan 528000000, 107c88a4c79SPeng Fan req->best_parent_rate 108c88a4c79SPeng Fan }; 109c88a4c79SPeng Fan unsigned long best_rate = -1UL, rate = req->rate; 110c88a4c79SPeng Fan unsigned long best_parent_rate = req->best_parent_rate; 111c88a4c79SPeng Fan u64 tmp; 1129fcb6be3SA.s. Dong u8 frac; 113c88a4c79SPeng Fan int i; 1149fcb6be3SA.s. Dong 115c88a4c79SPeng Fan for (i = 0; i < ARRAY_SIZE(parent_rates); i++) { 116c88a4c79SPeng Fan tmp = parent_rates[i]; 1179fcb6be3SA.s. Dong tmp = tmp * 18 + rate / 2; 1189fcb6be3SA.s. Dong do_div(tmp, rate); 1199fcb6be3SA.s. Dong frac = tmp; 1209fcb6be3SA.s. Dong 1219fcb6be3SA.s. Dong if (frac < 12) 1229fcb6be3SA.s. Dong frac = 12; 1239fcb6be3SA.s. Dong else if (frac > 35) 1249fcb6be3SA.s. Dong frac = 35; 1259fcb6be3SA.s. Dong 126c88a4c79SPeng Fan tmp = parent_rates[i]; 1279fcb6be3SA.s. Dong tmp *= 18; 1289fcb6be3SA.s. Dong do_div(tmp, frac); 1299fcb6be3SA.s. Dong 130c88a4c79SPeng Fan if (abs(tmp - req->rate) < abs(best_rate - req->rate)) { 131c88a4c79SPeng Fan best_rate = tmp; 132c88a4c79SPeng Fan best_parent_rate = parent_rates[i]; 133c88a4c79SPeng Fan } 134c88a4c79SPeng Fan } 135c88a4c79SPeng Fan 136c88a4c79SPeng Fan req->best_parent_rate = best_parent_rate; 137c88a4c79SPeng Fan req->rate = best_rate; 1388ffe9c7bSPeng Fan 1398ffe9c7bSPeng Fan return 0; 1409fcb6be3SA.s. Dong } 1419fcb6be3SA.s. Dong 1429fcb6be3SA.s. Dong static int clk_pfdv2_is_enabled(struct clk_hw *hw) 1439fcb6be3SA.s. Dong { 1449fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 1459fcb6be3SA.s. Dong 146a5a627c6SAnson Huang if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit)) 1479fcb6be3SA.s. Dong return 0; 1489fcb6be3SA.s. Dong 1499fcb6be3SA.s. Dong return 1; 1509fcb6be3SA.s. Dong } 1519fcb6be3SA.s. Dong 1529fcb6be3SA.s. Dong static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, 1539fcb6be3SA.s. Dong unsigned long parent_rate) 1549fcb6be3SA.s. Dong { 1559fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 1569fcb6be3SA.s. Dong unsigned long flags; 1579fcb6be3SA.s. Dong u64 tmp = parent_rate; 1589fcb6be3SA.s. Dong u32 val; 1599fcb6be3SA.s. Dong u8 frac; 1609fcb6be3SA.s. Dong 16128b2f82eSAnson Huang if (!rate) 16228b2f82eSAnson Huang return -EINVAL; 16328b2f82eSAnson Huang 164*ae8a10d6SJacky Bai /* 165*ae8a10d6SJacky Bai * PFD can NOT change rate without gating. 166*ae8a10d6SJacky Bai * as the PFDs may enabled in HW by default but no 167*ae8a10d6SJacky Bai * consumer used it, the enable count is '0', so the 168*ae8a10d6SJacky Bai * 'SET_RATE_GATE' can NOT help on blocking the set_rate 169*ae8a10d6SJacky Bai * ops especially for 'assigned-clock-xxx'. In order 170*ae8a10d6SJacky Bai * to simplify the case, just disable the PFD if it is 171*ae8a10d6SJacky Bai * enabled in HW but not in SW. 172*ae8a10d6SJacky Bai */ 173*ae8a10d6SJacky Bai if (clk_pfdv2_is_enabled(hw)) 174*ae8a10d6SJacky Bai clk_pfdv2_disable(hw); 17528b2f82eSAnson Huang 1769fcb6be3SA.s. Dong tmp = tmp * 18 + rate / 2; 1779fcb6be3SA.s. Dong do_div(tmp, rate); 1789fcb6be3SA.s. Dong frac = tmp; 1799fcb6be3SA.s. Dong if (frac < 12) 1809fcb6be3SA.s. Dong frac = 12; 1819fcb6be3SA.s. Dong else if (frac > 35) 1829fcb6be3SA.s. Dong frac = 35; 1839fcb6be3SA.s. Dong 1849fcb6be3SA.s. Dong spin_lock_irqsave(&pfd_lock, flags); 1859fcb6be3SA.s. Dong val = readl_relaxed(pfd->reg); 1869fcb6be3SA.s. Dong val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); 1879fcb6be3SA.s. Dong val |= frac << pfd->frac_off; 1889fcb6be3SA.s. Dong writel_relaxed(val, pfd->reg); 1899fcb6be3SA.s. Dong spin_unlock_irqrestore(&pfd_lock, flags); 1909fcb6be3SA.s. Dong 1919fcb6be3SA.s. Dong return 0; 1929fcb6be3SA.s. Dong } 1939fcb6be3SA.s. Dong 1949fcb6be3SA.s. Dong static const struct clk_ops clk_pfdv2_ops = { 1959fcb6be3SA.s. Dong .enable = clk_pfdv2_enable, 1969fcb6be3SA.s. Dong .disable = clk_pfdv2_disable, 1979fcb6be3SA.s. Dong .recalc_rate = clk_pfdv2_recalc_rate, 1988ffe9c7bSPeng Fan .determine_rate = clk_pfdv2_determine_rate, 1999fcb6be3SA.s. Dong .set_rate = clk_pfdv2_set_rate, 2009fcb6be3SA.s. Dong .is_enabled = clk_pfdv2_is_enabled, 2019fcb6be3SA.s. Dong }; 2029fcb6be3SA.s. Dong 20340ad61d6SAbel Vesa struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name, 2049fcb6be3SA.s. Dong void __iomem *reg, u8 idx) 2059fcb6be3SA.s. Dong { 2069fcb6be3SA.s. Dong struct clk_init_data init; 2079fcb6be3SA.s. Dong struct clk_pfdv2 *pfd; 2089fcb6be3SA.s. Dong struct clk_hw *hw; 2099fcb6be3SA.s. Dong int ret; 2109fcb6be3SA.s. Dong 2119fcb6be3SA.s. Dong WARN_ON(idx > 3); 2129fcb6be3SA.s. Dong 2139fcb6be3SA.s. Dong pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 2149fcb6be3SA.s. Dong if (!pfd) 2159fcb6be3SA.s. Dong return ERR_PTR(-ENOMEM); 2169fcb6be3SA.s. Dong 2179fcb6be3SA.s. Dong pfd->reg = reg; 218a5a627c6SAnson Huang pfd->gate_bit = (idx + 1) * 8 - 1; 2199fcb6be3SA.s. Dong pfd->vld_bit = pfd->gate_bit - 1; 2209fcb6be3SA.s. Dong pfd->frac_off = idx * 8; 2219fcb6be3SA.s. Dong 2229fcb6be3SA.s. Dong init.name = name; 2239fcb6be3SA.s. Dong init.ops = &clk_pfdv2_ops; 2249fcb6be3SA.s. Dong init.parent_names = &parent_name; 2259fcb6be3SA.s. Dong init.num_parents = 1; 226c88a4c79SPeng Fan init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; 2279fcb6be3SA.s. Dong 2289fcb6be3SA.s. Dong pfd->hw.init = &init; 2299fcb6be3SA.s. Dong 2309fcb6be3SA.s. Dong hw = &pfd->hw; 2319fcb6be3SA.s. Dong ret = clk_hw_register(NULL, hw); 2329fcb6be3SA.s. Dong if (ret) { 2339fcb6be3SA.s. Dong kfree(pfd); 2349fcb6be3SA.s. Dong hw = ERR_PTR(ret); 2359fcb6be3SA.s. Dong } 2369fcb6be3SA.s. Dong 2379fcb6be3SA.s. Dong return hw; 2389fcb6be3SA.s. Dong } 239