17a29a869SCarlo Caione /* 27a29a869SCarlo Caione * Copyright (c) 2015 Endless Mobile, Inc. 37a29a869SCarlo Caione * Author: Carlo Caione <carlo@endlessm.com> 47a29a869SCarlo Caione * 57a29a869SCarlo Caione * This program is free software; you can redistribute it and/or modify it 67a29a869SCarlo Caione * under the terms and conditions of the GNU General Public License, 77a29a869SCarlo Caione * version 2, as published by the Free Software Foundation. 87a29a869SCarlo Caione * 97a29a869SCarlo Caione * This program is distributed in the hope it will be useful, but WITHOUT 107a29a869SCarlo Caione * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 117a29a869SCarlo Caione * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 127a29a869SCarlo Caione * more details. 137a29a869SCarlo Caione * 147a29a869SCarlo Caione * You should have received a copy of the GNU General Public License along with 157a29a869SCarlo Caione * this program. If not, see <http://www.gnu.org/licenses/>. 167a29a869SCarlo Caione */ 177a29a869SCarlo Caione 187a29a869SCarlo Caione /* 197a29a869SCarlo Caione * In the most basic form, a Meson PLL is composed as follows: 207a29a869SCarlo Caione * 217a29a869SCarlo Caione * PLL 227a29a869SCarlo Caione * +------------------------------+ 237a29a869SCarlo Caione * | | 247a29a869SCarlo Caione * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out 257a29a869SCarlo Caione * | ^ ^ | 267a29a869SCarlo Caione * +------------------------------+ 277a29a869SCarlo Caione * | | 287a29a869SCarlo Caione * FREF VCO 297a29a869SCarlo Caione * 307a29a869SCarlo Caione * out = (in * M / N) >> OD 317a29a869SCarlo Caione */ 327a29a869SCarlo Caione 337a29a869SCarlo Caione #include <linux/clk-provider.h> 347a29a869SCarlo Caione #include <linux/delay.h> 357a29a869SCarlo Caione #include <linux/err.h> 367a29a869SCarlo Caione #include <linux/io.h> 377a29a869SCarlo Caione #include <linux/module.h> 387a29a869SCarlo Caione #include <linux/of_address.h> 397a29a869SCarlo Caione #include <linux/slab.h> 407a29a869SCarlo Caione #include <linux/string.h> 417a29a869SCarlo Caione 427a29a869SCarlo Caione #include "clkc.h" 437a29a869SCarlo Caione 447a29a869SCarlo Caione #define MESON_PLL_RESET BIT(29) 457a29a869SCarlo Caione #define MESON_PLL_LOCK BIT(31) 467a29a869SCarlo Caione 477a29a869SCarlo Caione #define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw) 487a29a869SCarlo Caione 497a29a869SCarlo Caione static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, 507a29a869SCarlo Caione unsigned long parent_rate) 517a29a869SCarlo Caione { 527a29a869SCarlo Caione struct meson_clk_pll *pll = to_meson_clk_pll(hw); 537a29a869SCarlo Caione struct parm *p; 547a29a869SCarlo Caione unsigned long parent_rate_mhz = parent_rate / 1000000; 557a29a869SCarlo Caione unsigned long rate_mhz; 564a472951SMichael Turquette u16 n, m, frac = 0, od, od2 = 0; 577a29a869SCarlo Caione u32 reg; 587a29a869SCarlo Caione 59ec623f2aSMichael Turquette p = &pll->n; 607a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 617a29a869SCarlo Caione n = PARM_GET(p->width, p->shift, reg); 627a29a869SCarlo Caione 63ec623f2aSMichael Turquette p = &pll->m; 647a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 657a29a869SCarlo Caione m = PARM_GET(p->width, p->shift, reg); 667a29a869SCarlo Caione 67ec623f2aSMichael Turquette p = &pll->od; 687a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 697a29a869SCarlo Caione od = PARM_GET(p->width, p->shift, reg); 707a29a869SCarlo Caione 714a472951SMichael Turquette p = &pll->od2; 724a472951SMichael Turquette if (p->width) { 734a472951SMichael Turquette reg = readl(pll->base + p->reg_off); 744a472951SMichael Turquette od2 = PARM_GET(p->width, p->shift, reg); 754a472951SMichael Turquette } 764a472951SMichael Turquette 774a472951SMichael Turquette p = &pll->frac; 784a472951SMichael Turquette if (p->width) { 794a472951SMichael Turquette reg = readl(pll->base + p->reg_off); 804a472951SMichael Turquette frac = PARM_GET(p->width, p->shift, reg); 814a472951SMichael Turquette rate_mhz = (parent_rate_mhz * m + \ 824a472951SMichael Turquette (parent_rate_mhz * frac >> 12)) * 2 / n; 834a472951SMichael Turquette rate_mhz = rate_mhz >> od >> od2; 844a472951SMichael Turquette } else 854a472951SMichael Turquette rate_mhz = (parent_rate_mhz * m / n) >> od >> od2; 867a29a869SCarlo Caione 877a29a869SCarlo Caione return rate_mhz * 1000000; 887a29a869SCarlo Caione } 897a29a869SCarlo Caione 907a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, 917a29a869SCarlo Caione unsigned long *parent_rate) 927a29a869SCarlo Caione { 937a29a869SCarlo Caione struct meson_clk_pll *pll = to_meson_clk_pll(hw); 94ec623f2aSMichael Turquette const struct pll_rate_table *rate_table = pll->rate_table; 957a29a869SCarlo Caione int i; 967a29a869SCarlo Caione 977a29a869SCarlo Caione for (i = 0; i < pll->rate_count; i++) { 987a29a869SCarlo Caione if (rate <= rate_table[i].rate) 997a29a869SCarlo Caione return rate_table[i].rate; 1007a29a869SCarlo Caione } 1017a29a869SCarlo Caione 1027a29a869SCarlo Caione /* else return the smallest value */ 1037a29a869SCarlo Caione return rate_table[0].rate; 1047a29a869SCarlo Caione } 1057a29a869SCarlo Caione 1067a29a869SCarlo Caione static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll, 1077a29a869SCarlo Caione unsigned long rate) 1087a29a869SCarlo Caione { 109ec623f2aSMichael Turquette const struct pll_rate_table *rate_table = pll->rate_table; 1107a29a869SCarlo Caione int i; 1117a29a869SCarlo Caione 1127a29a869SCarlo Caione for (i = 0; i < pll->rate_count; i++) { 1137a29a869SCarlo Caione if (rate == rate_table[i].rate) 1147a29a869SCarlo Caione return &rate_table[i]; 1157a29a869SCarlo Caione } 1167a29a869SCarlo Caione return NULL; 1177a29a869SCarlo Caione } 1187a29a869SCarlo Caione 119*45fcbec7SNeil Armstrong /* Specific wait loop for GXL/GXM GP0 PLL */ 120*45fcbec7SNeil Armstrong static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll, 121*45fcbec7SNeil Armstrong struct parm *p_n) 122*45fcbec7SNeil Armstrong { 123*45fcbec7SNeil Armstrong int delay = 100; 124*45fcbec7SNeil Armstrong u32 reg; 125*45fcbec7SNeil Armstrong 126*45fcbec7SNeil Armstrong while (delay > 0) { 127*45fcbec7SNeil Armstrong reg = readl(pll->base + p_n->reg_off); 128*45fcbec7SNeil Armstrong writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off); 129*45fcbec7SNeil Armstrong udelay(10); 130*45fcbec7SNeil Armstrong writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off); 131*45fcbec7SNeil Armstrong 132*45fcbec7SNeil Armstrong /* This delay comes from AMLogic tree clk-gp0-gxl driver */ 133*45fcbec7SNeil Armstrong mdelay(1); 134*45fcbec7SNeil Armstrong 135*45fcbec7SNeil Armstrong reg = readl(pll->base + p_n->reg_off); 136*45fcbec7SNeil Armstrong if (reg & MESON_PLL_LOCK) 137*45fcbec7SNeil Armstrong return 0; 138*45fcbec7SNeil Armstrong delay--; 139*45fcbec7SNeil Armstrong } 140*45fcbec7SNeil Armstrong return -ETIMEDOUT; 141*45fcbec7SNeil Armstrong } 142*45fcbec7SNeil Armstrong 1437a29a869SCarlo Caione static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, 1447a29a869SCarlo Caione struct parm *p_n) 1457a29a869SCarlo Caione { 1467a29a869SCarlo Caione int delay = 24000000; 1477a29a869SCarlo Caione u32 reg; 1487a29a869SCarlo Caione 1497a29a869SCarlo Caione while (delay > 0) { 1507a29a869SCarlo Caione reg = readl(pll->base + p_n->reg_off); 1517a29a869SCarlo Caione 1527a29a869SCarlo Caione if (reg & MESON_PLL_LOCK) 1537a29a869SCarlo Caione return 0; 1547a29a869SCarlo Caione delay--; 1557a29a869SCarlo Caione } 1567a29a869SCarlo Caione return -ETIMEDOUT; 1577a29a869SCarlo Caione } 1587a29a869SCarlo Caione 159*45fcbec7SNeil Armstrong static void meson_clk_pll_init_params(struct meson_clk_pll *pll) 160*45fcbec7SNeil Armstrong { 161*45fcbec7SNeil Armstrong int i; 162*45fcbec7SNeil Armstrong 163*45fcbec7SNeil Armstrong for (i = 0 ; i < pll->params.params_count ; ++i) 164*45fcbec7SNeil Armstrong writel(pll->params.params_table[i].value, 165*45fcbec7SNeil Armstrong pll->base + pll->params.params_table[i].reg_off); 166*45fcbec7SNeil Armstrong } 167*45fcbec7SNeil Armstrong 1687a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1697a29a869SCarlo Caione unsigned long parent_rate) 1707a29a869SCarlo Caione { 1717a29a869SCarlo Caione struct meson_clk_pll *pll = to_meson_clk_pll(hw); 1727a29a869SCarlo Caione struct parm *p; 1737a29a869SCarlo Caione const struct pll_rate_table *rate_set; 1747a29a869SCarlo Caione unsigned long old_rate; 1757a29a869SCarlo Caione int ret = 0; 1767a29a869SCarlo Caione u32 reg; 1777a29a869SCarlo Caione 1787a29a869SCarlo Caione if (parent_rate == 0 || rate == 0) 1797a29a869SCarlo Caione return -EINVAL; 1807a29a869SCarlo Caione 1817a29a869SCarlo Caione old_rate = rate; 1827a29a869SCarlo Caione 1837a29a869SCarlo Caione rate_set = meson_clk_get_pll_settings(pll, rate); 1847a29a869SCarlo Caione if (!rate_set) 1857a29a869SCarlo Caione return -EINVAL; 1867a29a869SCarlo Caione 187*45fcbec7SNeil Armstrong /* Initialize the PLL in a clean state if specified */ 188*45fcbec7SNeil Armstrong if (pll->params.params_count) 189*45fcbec7SNeil Armstrong meson_clk_pll_init_params(pll); 190*45fcbec7SNeil Armstrong 1917a29a869SCarlo Caione /* PLL reset */ 192ec623f2aSMichael Turquette p = &pll->n; 1937a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 194*45fcbec7SNeil Armstrong /* If no_init_reset is provided, avoid resetting at this point */ 195*45fcbec7SNeil Armstrong if (!pll->params.no_init_reset) 1967a29a869SCarlo Caione writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); 1977a29a869SCarlo Caione 1987a29a869SCarlo Caione reg = PARM_SET(p->width, p->shift, reg, rate_set->n); 1997a29a869SCarlo Caione writel(reg, pll->base + p->reg_off); 2007a29a869SCarlo Caione 201ec623f2aSMichael Turquette p = &pll->m; 2027a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 2037a29a869SCarlo Caione reg = PARM_SET(p->width, p->shift, reg, rate_set->m); 2047a29a869SCarlo Caione writel(reg, pll->base + p->reg_off); 2057a29a869SCarlo Caione 206ec623f2aSMichael Turquette p = &pll->od; 2077a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 2087a29a869SCarlo Caione reg = PARM_SET(p->width, p->shift, reg, rate_set->od); 2097a29a869SCarlo Caione writel(reg, pll->base + p->reg_off); 2107a29a869SCarlo Caione 2114a472951SMichael Turquette p = &pll->od2; 2124a472951SMichael Turquette if (p->width) { 2134a472951SMichael Turquette reg = readl(pll->base + p->reg_off); 2144a472951SMichael Turquette reg = PARM_SET(p->width, p->shift, reg, rate_set->od2); 2154a472951SMichael Turquette writel(reg, pll->base + p->reg_off); 2164a472951SMichael Turquette } 2174a472951SMichael Turquette 2184a472951SMichael Turquette p = &pll->frac; 2194a472951SMichael Turquette if (p->width) { 2204a472951SMichael Turquette reg = readl(pll->base + p->reg_off); 2214a472951SMichael Turquette reg = PARM_SET(p->width, p->shift, reg, rate_set->frac); 2224a472951SMichael Turquette writel(reg, pll->base + p->reg_off); 2234a472951SMichael Turquette } 2244a472951SMichael Turquette 225ec623f2aSMichael Turquette p = &pll->n; 226*45fcbec7SNeil Armstrong /* If clear_reset_for_lock is provided, remove the reset bit here */ 227*45fcbec7SNeil Armstrong if (pll->params.clear_reset_for_lock) { 228*45fcbec7SNeil Armstrong reg = readl(pll->base + p->reg_off); 229*45fcbec7SNeil Armstrong writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off); 230*45fcbec7SNeil Armstrong } 231*45fcbec7SNeil Armstrong 232*45fcbec7SNeil Armstrong /* If reset_lock_loop, use a special loop including resetting */ 233*45fcbec7SNeil Armstrong if (pll->params.reset_lock_loop) 234*45fcbec7SNeil Armstrong ret = meson_clk_pll_wait_lock_reset(pll, p); 235*45fcbec7SNeil Armstrong else 2367a29a869SCarlo Caione ret = meson_clk_pll_wait_lock(pll, p); 2377a29a869SCarlo Caione if (ret) { 2387a29a869SCarlo Caione pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", 2397a29a869SCarlo Caione __func__, old_rate); 2407a29a869SCarlo Caione meson_clk_pll_set_rate(hw, old_rate, parent_rate); 2417a29a869SCarlo Caione } 2427a29a869SCarlo Caione 2437a29a869SCarlo Caione return ret; 2447a29a869SCarlo Caione } 2457a29a869SCarlo Caione 246ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = { 2477a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 2487a29a869SCarlo Caione .round_rate = meson_clk_pll_round_rate, 2497a29a869SCarlo Caione .set_rate = meson_clk_pll_set_rate, 2507a29a869SCarlo Caione }; 2517a29a869SCarlo Caione 252ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = { 2537a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 2547a29a869SCarlo Caione }; 255