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> 3794aa8a41SJerome Brunet #include <linux/math64.h> 387a29a869SCarlo Caione #include <linux/module.h> 397a29a869SCarlo Caione #include <linux/of_address.h> 407a29a869SCarlo Caione #include <linux/slab.h> 417a29a869SCarlo Caione #include <linux/string.h> 427a29a869SCarlo Caione 437a29a869SCarlo Caione #include "clkc.h" 447a29a869SCarlo Caione 45722825dcSJerome Brunet static inline struct meson_clk_pll_data * 46722825dcSJerome Brunet meson_clk_pll_data(struct clk_regmap *clk) 47722825dcSJerome Brunet { 48722825dcSJerome Brunet return (struct meson_clk_pll_data *)clk->data; 49722825dcSJerome Brunet } 507a29a869SCarlo Caione 517a29a869SCarlo Caione static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, 527a29a869SCarlo Caione unsigned long parent_rate) 537a29a869SCarlo Caione { 54722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 55722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 5694aa8a41SJerome Brunet u64 rate; 577d3142e5SJerome Brunet u16 n, m, frac = 0, od, od2 = 0, od3 = 0; 587a29a869SCarlo Caione 59722825dcSJerome Brunet n = meson_parm_read(clk->map, &pll->n); 60722825dcSJerome Brunet m = meson_parm_read(clk->map, &pll->m); 61722825dcSJerome Brunet od = meson_parm_read(clk->map, &pll->od); 627a29a869SCarlo Caione 63722825dcSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->od2)) 64722825dcSJerome Brunet od2 = meson_parm_read(clk->map, &pll->od2); 657a29a869SCarlo Caione 66722825dcSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->od3)) 67722825dcSJerome Brunet od3 = meson_parm_read(clk->map, &pll->od3); 687d3142e5SJerome Brunet 697d3142e5SJerome Brunet rate = (u64)m * parent_rate; 7094aa8a41SJerome Brunet 71722825dcSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->frac)) { 72722825dcSJerome Brunet frac = meson_parm_read(clk->map, &pll->frac); 737a29a869SCarlo Caione 74722825dcSJerome Brunet rate += mul_u64_u32_shr(parent_rate, frac, pll->frac.width); 7594aa8a41SJerome Brunet } 7694aa8a41SJerome Brunet 777d3142e5SJerome Brunet return div_u64(rate, n) >> od >> od2 >> od3; 787a29a869SCarlo Caione } 797a29a869SCarlo Caione 807a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, 817a29a869SCarlo Caione unsigned long *parent_rate) 827a29a869SCarlo Caione { 83722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 84722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 85722825dcSJerome Brunet const struct pll_rate_table *pllt; 867a29a869SCarlo Caione 87840e1a73SJerome Brunet /* 88840e1a73SJerome Brunet * if the table is missing, just return the current rate 89840e1a73SJerome Brunet * since we don't have the other available frequencies 90840e1a73SJerome Brunet */ 91722825dcSJerome Brunet if (!pll->table) 92840e1a73SJerome Brunet return meson_clk_pll_recalc_rate(hw, *parent_rate); 93840e1a73SJerome Brunet 94722825dcSJerome Brunet for (pllt = pll->table; pllt->rate; pllt++) { 95722825dcSJerome Brunet if (rate <= pllt->rate) 96722825dcSJerome Brunet return pllt->rate; 977a29a869SCarlo Caione } 987a29a869SCarlo Caione 997a29a869SCarlo Caione /* else return the smallest value */ 100722825dcSJerome Brunet return pll->table[0].rate; 1017a29a869SCarlo Caione } 1027a29a869SCarlo Caione 103722825dcSJerome Brunet static const struct pll_rate_table * 104722825dcSJerome Brunet meson_clk_get_pll_settings(const struct pll_rate_table *table, 1057a29a869SCarlo Caione unsigned long rate) 1067a29a869SCarlo Caione { 107722825dcSJerome Brunet const struct pll_rate_table *pllt; 1087a29a869SCarlo Caione 109722825dcSJerome Brunet if (!table) 110840e1a73SJerome Brunet return NULL; 111840e1a73SJerome Brunet 112722825dcSJerome Brunet for (pllt = table; pllt->rate; pllt++) { 113722825dcSJerome Brunet if (rate == pllt->rate) 114722825dcSJerome Brunet return pllt; 1157a29a869SCarlo Caione } 116722825dcSJerome Brunet 1177a29a869SCarlo Caione return NULL; 1187a29a869SCarlo Caione } 1197a29a869SCarlo Caione 120722825dcSJerome Brunet static int meson_clk_pll_wait_lock(struct clk_hw *hw) 121722825dcSJerome Brunet { 122722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 123722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 124*c178b003SJerome Brunet int delay = 24000000; 125722825dcSJerome Brunet 126722825dcSJerome Brunet do { 127722825dcSJerome Brunet /* Is the clock locked now ? */ 128722825dcSJerome Brunet if (meson_parm_read(clk->map, &pll->l)) 129722825dcSJerome Brunet return 0; 130722825dcSJerome Brunet 131722825dcSJerome Brunet delay--; 132722825dcSJerome Brunet } while (delay > 0); 133722825dcSJerome Brunet 13445fcbec7SNeil Armstrong return -ETIMEDOUT; 13545fcbec7SNeil Armstrong } 13645fcbec7SNeil Armstrong 137722825dcSJerome Brunet static void meson_clk_pll_init(struct clk_hw *hw) 1387a29a869SCarlo Caione { 139722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 140722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 1417a29a869SCarlo Caione 142722825dcSJerome Brunet if (pll->init_count) { 143722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 144722825dcSJerome Brunet regmap_multi_reg_write(clk->map, pll->init_regs, 145722825dcSJerome Brunet pll->init_count); 146722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 1477a29a869SCarlo Caione } 14845fcbec7SNeil Armstrong } 14945fcbec7SNeil Armstrong 1507a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1517a29a869SCarlo Caione unsigned long parent_rate) 1527a29a869SCarlo Caione { 153722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 154722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 155722825dcSJerome Brunet const struct pll_rate_table *pllt; 1567a29a869SCarlo Caione unsigned long old_rate; 1577a29a869SCarlo Caione 1587a29a869SCarlo Caione if (parent_rate == 0 || rate == 0) 1597a29a869SCarlo Caione return -EINVAL; 1607a29a869SCarlo Caione 1617a29a869SCarlo Caione old_rate = rate; 1627a29a869SCarlo Caione 163722825dcSJerome Brunet pllt = meson_clk_get_pll_settings(pll->table, rate); 164722825dcSJerome Brunet if (!pllt) 1657a29a869SCarlo Caione return -EINVAL; 1667a29a869SCarlo Caione 167722825dcSJerome Brunet /* Put the pll in reset to write the params */ 168722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 16945fcbec7SNeil Armstrong 170722825dcSJerome Brunet meson_parm_write(clk->map, &pll->n, pllt->n); 171722825dcSJerome Brunet meson_parm_write(clk->map, &pll->m, pllt->m); 172722825dcSJerome Brunet meson_parm_write(clk->map, &pll->od, pllt->od); 1737a29a869SCarlo Caione 174722825dcSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->od2)) 175722825dcSJerome Brunet meson_parm_write(clk->map, &pll->od2, pllt->od2); 1767a29a869SCarlo Caione 177722825dcSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->od3)) 178722825dcSJerome Brunet meson_parm_write(clk->map, &pll->od3, pllt->od3); 1797a29a869SCarlo Caione 180722825dcSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->frac)) 181722825dcSJerome Brunet meson_parm_write(clk->map, &pll->frac, pllt->frac); 1827a29a869SCarlo Caione 183722825dcSJerome Brunet /* make sure the reset is cleared at this point */ 184722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 1854a472951SMichael Turquette 186722825dcSJerome Brunet if (meson_clk_pll_wait_lock(hw)) { 1877a29a869SCarlo Caione pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", 1887a29a869SCarlo Caione __func__, old_rate); 189722825dcSJerome Brunet /* 190722825dcSJerome Brunet * FIXME: Do we really need/want this HACK ? 191722825dcSJerome Brunet * It looks unsafe. what happens if the clock gets into a 192722825dcSJerome Brunet * broken state and we can't lock back on the old_rate ? Looks 193722825dcSJerome Brunet * like an infinite recursion is possible 194722825dcSJerome Brunet */ 1957a29a869SCarlo Caione meson_clk_pll_set_rate(hw, old_rate, parent_rate); 1967a29a869SCarlo Caione } 1977a29a869SCarlo Caione 198722825dcSJerome Brunet return 0; 1997a29a869SCarlo Caione } 2007a29a869SCarlo Caione 201ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = { 202722825dcSJerome Brunet .init = meson_clk_pll_init, 2037a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 2047a29a869SCarlo Caione .round_rate = meson_clk_pll_round_rate, 2057a29a869SCarlo Caione .set_rate = meson_clk_pll_set_rate, 2067a29a869SCarlo Caione }; 2077a29a869SCarlo Caione 208ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = { 2097a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 2107a29a869SCarlo Caione }; 211