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> 129fcb6be3SA.s. Dong #include <linux/iopoll.h> 139fcb6be3SA.s. Dong #include <linux/slab.h> 149fcb6be3SA.s. Dong 159fcb6be3SA.s. Dong #include "clk.h" 169fcb6be3SA.s. Dong 179fcb6be3SA.s. Dong /** 189fcb6be3SA.s. Dong * struct clk_pfdv2 - IMX PFD clock 199fcb6be3SA.s. Dong * @clk_hw: clock source 209fcb6be3SA.s. Dong * @reg: PFD register address 219fcb6be3SA.s. Dong * @gate_bit: Gate bit offset 229fcb6be3SA.s. Dong * @vld_bit: Valid bit offset 239fcb6be3SA.s. Dong * @frac_off: PLL Fractional Divider offset 249fcb6be3SA.s. Dong */ 259fcb6be3SA.s. Dong 269fcb6be3SA.s. Dong struct clk_pfdv2 { 279fcb6be3SA.s. Dong struct clk_hw hw; 289fcb6be3SA.s. Dong void __iomem *reg; 299fcb6be3SA.s. Dong u8 gate_bit; 309fcb6be3SA.s. Dong u8 vld_bit; 319fcb6be3SA.s. Dong u8 frac_off; 329fcb6be3SA.s. Dong }; 339fcb6be3SA.s. Dong 349fcb6be3SA.s. Dong #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw) 359fcb6be3SA.s. Dong 369fcb6be3SA.s. Dong #define CLK_PFDV2_FRAC_MASK 0x3f 379fcb6be3SA.s. Dong 389fcb6be3SA.s. Dong #define LOCK_TIMEOUT_US USEC_PER_MSEC 399fcb6be3SA.s. Dong 409fcb6be3SA.s. Dong static DEFINE_SPINLOCK(pfd_lock); 419fcb6be3SA.s. Dong 429fcb6be3SA.s. Dong static int clk_pfdv2_wait(struct clk_pfdv2 *pfd) 439fcb6be3SA.s. Dong { 449fcb6be3SA.s. Dong u32 val; 459fcb6be3SA.s. Dong 469fcb6be3SA.s. Dong return readl_poll_timeout(pfd->reg, val, val & pfd->vld_bit, 479fcb6be3SA.s. Dong 0, LOCK_TIMEOUT_US); 489fcb6be3SA.s. Dong } 499fcb6be3SA.s. Dong 509fcb6be3SA.s. Dong static int clk_pfdv2_enable(struct clk_hw *hw) 519fcb6be3SA.s. Dong { 529fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 539fcb6be3SA.s. Dong unsigned long flags; 549fcb6be3SA.s. Dong u32 val; 559fcb6be3SA.s. Dong 569fcb6be3SA.s. Dong spin_lock_irqsave(&pfd_lock, flags); 579fcb6be3SA.s. Dong val = readl_relaxed(pfd->reg); 589fcb6be3SA.s. Dong val &= ~pfd->gate_bit; 599fcb6be3SA.s. Dong writel_relaxed(val, pfd->reg); 609fcb6be3SA.s. Dong spin_unlock_irqrestore(&pfd_lock, flags); 619fcb6be3SA.s. Dong 629fcb6be3SA.s. Dong return clk_pfdv2_wait(pfd); 639fcb6be3SA.s. Dong } 649fcb6be3SA.s. Dong 659fcb6be3SA.s. Dong static void clk_pfdv2_disable(struct clk_hw *hw) 669fcb6be3SA.s. Dong { 679fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 689fcb6be3SA.s. Dong unsigned long flags; 699fcb6be3SA.s. Dong u32 val; 709fcb6be3SA.s. Dong 719fcb6be3SA.s. Dong spin_lock_irqsave(&pfd_lock, flags); 729fcb6be3SA.s. Dong val = readl_relaxed(pfd->reg); 739fcb6be3SA.s. Dong val |= pfd->gate_bit; 749fcb6be3SA.s. Dong writel_relaxed(val, pfd->reg); 759fcb6be3SA.s. Dong spin_unlock_irqrestore(&pfd_lock, flags); 769fcb6be3SA.s. Dong } 779fcb6be3SA.s. Dong 789fcb6be3SA.s. Dong static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw, 799fcb6be3SA.s. Dong unsigned long parent_rate) 809fcb6be3SA.s. Dong { 819fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 829fcb6be3SA.s. Dong u64 tmp = parent_rate; 839fcb6be3SA.s. Dong u8 frac; 849fcb6be3SA.s. Dong 859fcb6be3SA.s. Dong frac = (readl_relaxed(pfd->reg) >> pfd->frac_off) 869fcb6be3SA.s. Dong & CLK_PFDV2_FRAC_MASK; 879fcb6be3SA.s. Dong 889fcb6be3SA.s. Dong if (!frac) { 899fcb6be3SA.s. Dong pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n", 909fcb6be3SA.s. Dong clk_hw_get_name(hw)); 919fcb6be3SA.s. Dong return 0; 929fcb6be3SA.s. Dong } 939fcb6be3SA.s. Dong 949fcb6be3SA.s. Dong tmp *= 18; 959fcb6be3SA.s. Dong do_div(tmp, frac); 969fcb6be3SA.s. Dong 979fcb6be3SA.s. Dong return tmp; 989fcb6be3SA.s. Dong } 999fcb6be3SA.s. Dong 1009fcb6be3SA.s. Dong static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate, 1019fcb6be3SA.s. Dong unsigned long *prate) 1029fcb6be3SA.s. Dong { 1039fcb6be3SA.s. Dong u64 tmp = *prate; 1049fcb6be3SA.s. Dong u8 frac; 1059fcb6be3SA.s. Dong 1069fcb6be3SA.s. Dong tmp = tmp * 18 + rate / 2; 1079fcb6be3SA.s. Dong do_div(tmp, rate); 1089fcb6be3SA.s. Dong frac = tmp; 1099fcb6be3SA.s. Dong 1109fcb6be3SA.s. Dong if (frac < 12) 1119fcb6be3SA.s. Dong frac = 12; 1129fcb6be3SA.s. Dong else if (frac > 35) 1139fcb6be3SA.s. Dong frac = 35; 1149fcb6be3SA.s. Dong 1159fcb6be3SA.s. Dong tmp = *prate; 1169fcb6be3SA.s. Dong tmp *= 18; 1179fcb6be3SA.s. Dong do_div(tmp, frac); 1189fcb6be3SA.s. Dong 1199fcb6be3SA.s. Dong return tmp; 1209fcb6be3SA.s. Dong } 1219fcb6be3SA.s. Dong 1229fcb6be3SA.s. Dong static int clk_pfdv2_is_enabled(struct clk_hw *hw) 1239fcb6be3SA.s. Dong { 1249fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 1259fcb6be3SA.s. Dong 1269fcb6be3SA.s. Dong if (readl_relaxed(pfd->reg) & pfd->gate_bit) 1279fcb6be3SA.s. Dong return 0; 1289fcb6be3SA.s. Dong 1299fcb6be3SA.s. Dong return 1; 1309fcb6be3SA.s. Dong } 1319fcb6be3SA.s. Dong 1329fcb6be3SA.s. Dong static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, 1339fcb6be3SA.s. Dong unsigned long parent_rate) 1349fcb6be3SA.s. Dong { 1359fcb6be3SA.s. Dong struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 1369fcb6be3SA.s. Dong unsigned long flags; 1379fcb6be3SA.s. Dong u64 tmp = parent_rate; 1389fcb6be3SA.s. Dong u32 val; 1399fcb6be3SA.s. Dong u8 frac; 1409fcb6be3SA.s. Dong 1419fcb6be3SA.s. Dong tmp = tmp * 18 + rate / 2; 1429fcb6be3SA.s. Dong do_div(tmp, rate); 1439fcb6be3SA.s. Dong frac = tmp; 1449fcb6be3SA.s. Dong if (frac < 12) 1459fcb6be3SA.s. Dong frac = 12; 1469fcb6be3SA.s. Dong else if (frac > 35) 1479fcb6be3SA.s. Dong frac = 35; 1489fcb6be3SA.s. Dong 1499fcb6be3SA.s. Dong spin_lock_irqsave(&pfd_lock, flags); 1509fcb6be3SA.s. Dong val = readl_relaxed(pfd->reg); 1519fcb6be3SA.s. Dong val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); 1529fcb6be3SA.s. Dong val |= frac << pfd->frac_off; 1539fcb6be3SA.s. Dong writel_relaxed(val, pfd->reg); 1549fcb6be3SA.s. Dong spin_unlock_irqrestore(&pfd_lock, flags); 1559fcb6be3SA.s. Dong 1569fcb6be3SA.s. Dong return 0; 1579fcb6be3SA.s. Dong } 1589fcb6be3SA.s. Dong 1599fcb6be3SA.s. Dong static const struct clk_ops clk_pfdv2_ops = { 1609fcb6be3SA.s. Dong .enable = clk_pfdv2_enable, 1619fcb6be3SA.s. Dong .disable = clk_pfdv2_disable, 1629fcb6be3SA.s. Dong .recalc_rate = clk_pfdv2_recalc_rate, 1639fcb6be3SA.s. Dong .round_rate = clk_pfdv2_round_rate, 1649fcb6be3SA.s. Dong .set_rate = clk_pfdv2_set_rate, 1659fcb6be3SA.s. Dong .is_enabled = clk_pfdv2_is_enabled, 1669fcb6be3SA.s. Dong }; 1679fcb6be3SA.s. Dong 1689fcb6be3SA.s. Dong struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name, 1699fcb6be3SA.s. Dong void __iomem *reg, u8 idx) 1709fcb6be3SA.s. Dong { 1719fcb6be3SA.s. Dong struct clk_init_data init; 1729fcb6be3SA.s. Dong struct clk_pfdv2 *pfd; 1739fcb6be3SA.s. Dong struct clk_hw *hw; 1749fcb6be3SA.s. Dong int ret; 1759fcb6be3SA.s. Dong 1769fcb6be3SA.s. Dong WARN_ON(idx > 3); 1779fcb6be3SA.s. Dong 1789fcb6be3SA.s. Dong pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 1799fcb6be3SA.s. Dong if (!pfd) 1809fcb6be3SA.s. Dong return ERR_PTR(-ENOMEM); 1819fcb6be3SA.s. Dong 1829fcb6be3SA.s. Dong pfd->reg = reg; 1839fcb6be3SA.s. Dong pfd->gate_bit = 1 << ((idx + 1) * 8 - 1); 1849fcb6be3SA.s. Dong pfd->vld_bit = pfd->gate_bit - 1; 1859fcb6be3SA.s. Dong pfd->frac_off = idx * 8; 1869fcb6be3SA.s. Dong 1879fcb6be3SA.s. Dong init.name = name; 1889fcb6be3SA.s. Dong init.ops = &clk_pfdv2_ops; 1899fcb6be3SA.s. Dong init.parent_names = &parent_name; 1909fcb6be3SA.s. Dong init.num_parents = 1; 1919fcb6be3SA.s. Dong init.flags = CLK_SET_RATE_GATE; 1929fcb6be3SA.s. Dong 1939fcb6be3SA.s. Dong pfd->hw.init = &init; 1949fcb6be3SA.s. Dong 1959fcb6be3SA.s. Dong hw = &pfd->hw; 1969fcb6be3SA.s. Dong ret = clk_hw_register(NULL, hw); 1979fcb6be3SA.s. Dong if (ret) { 1989fcb6be3SA.s. Dong kfree(pfd); 1999fcb6be3SA.s. Dong hw = ERR_PTR(ret); 2009fcb6be3SA.s. Dong } 2019fcb6be3SA.s. Dong 2029fcb6be3SA.s. Dong return hw; 2039fcb6be3SA.s. Dong } 204