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; 567a29a869SCarlo Caione u16 n, m, od; 577a29a869SCarlo Caione u32 reg; 587a29a869SCarlo Caione 59*ec623f2aSMichael 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 63*ec623f2aSMichael 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 67*ec623f2aSMichael 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 717a29a869SCarlo Caione rate_mhz = (parent_rate_mhz * m / n) >> od; 727a29a869SCarlo Caione 737a29a869SCarlo Caione return rate_mhz * 1000000; 747a29a869SCarlo Caione } 757a29a869SCarlo Caione 767a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, 777a29a869SCarlo Caione unsigned long *parent_rate) 787a29a869SCarlo Caione { 797a29a869SCarlo Caione struct meson_clk_pll *pll = to_meson_clk_pll(hw); 80*ec623f2aSMichael Turquette const struct pll_rate_table *rate_table = pll->rate_table; 817a29a869SCarlo Caione int i; 827a29a869SCarlo Caione 837a29a869SCarlo Caione for (i = 0; i < pll->rate_count; i++) { 847a29a869SCarlo Caione if (rate <= rate_table[i].rate) 857a29a869SCarlo Caione return rate_table[i].rate; 867a29a869SCarlo Caione } 877a29a869SCarlo Caione 887a29a869SCarlo Caione /* else return the smallest value */ 897a29a869SCarlo Caione return rate_table[0].rate; 907a29a869SCarlo Caione } 917a29a869SCarlo Caione 927a29a869SCarlo Caione static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll, 937a29a869SCarlo Caione unsigned long rate) 947a29a869SCarlo Caione { 95*ec623f2aSMichael Turquette const struct pll_rate_table *rate_table = pll->rate_table; 967a29a869SCarlo Caione int i; 977a29a869SCarlo Caione 987a29a869SCarlo Caione for (i = 0; i < pll->rate_count; i++) { 997a29a869SCarlo Caione if (rate == rate_table[i].rate) 1007a29a869SCarlo Caione return &rate_table[i]; 1017a29a869SCarlo Caione } 1027a29a869SCarlo Caione return NULL; 1037a29a869SCarlo Caione } 1047a29a869SCarlo Caione 1057a29a869SCarlo Caione static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, 1067a29a869SCarlo Caione struct parm *p_n) 1077a29a869SCarlo Caione { 1087a29a869SCarlo Caione int delay = 24000000; 1097a29a869SCarlo Caione u32 reg; 1107a29a869SCarlo Caione 1117a29a869SCarlo Caione while (delay > 0) { 1127a29a869SCarlo Caione reg = readl(pll->base + p_n->reg_off); 1137a29a869SCarlo Caione 1147a29a869SCarlo Caione if (reg & MESON_PLL_LOCK) 1157a29a869SCarlo Caione return 0; 1167a29a869SCarlo Caione delay--; 1177a29a869SCarlo Caione } 1187a29a869SCarlo Caione return -ETIMEDOUT; 1197a29a869SCarlo Caione } 1207a29a869SCarlo Caione 1217a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1227a29a869SCarlo Caione unsigned long parent_rate) 1237a29a869SCarlo Caione { 1247a29a869SCarlo Caione struct meson_clk_pll *pll = to_meson_clk_pll(hw); 1257a29a869SCarlo Caione struct parm *p; 1267a29a869SCarlo Caione const struct pll_rate_table *rate_set; 1277a29a869SCarlo Caione unsigned long old_rate; 1287a29a869SCarlo Caione int ret = 0; 1297a29a869SCarlo Caione u32 reg; 1307a29a869SCarlo Caione 1317a29a869SCarlo Caione if (parent_rate == 0 || rate == 0) 1327a29a869SCarlo Caione return -EINVAL; 1337a29a869SCarlo Caione 1347a29a869SCarlo Caione old_rate = rate; 1357a29a869SCarlo Caione 1367a29a869SCarlo Caione rate_set = meson_clk_get_pll_settings(pll, rate); 1377a29a869SCarlo Caione if (!rate_set) 1387a29a869SCarlo Caione return -EINVAL; 1397a29a869SCarlo Caione 1407a29a869SCarlo Caione /* PLL reset */ 141*ec623f2aSMichael Turquette p = &pll->n; 1427a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 1437a29a869SCarlo Caione writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); 1447a29a869SCarlo Caione 1457a29a869SCarlo Caione reg = PARM_SET(p->width, p->shift, reg, rate_set->n); 1467a29a869SCarlo Caione writel(reg, pll->base + p->reg_off); 1477a29a869SCarlo Caione 148*ec623f2aSMichael Turquette p = &pll->m; 1497a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 1507a29a869SCarlo Caione reg = PARM_SET(p->width, p->shift, reg, rate_set->m); 1517a29a869SCarlo Caione writel(reg, pll->base + p->reg_off); 1527a29a869SCarlo Caione 153*ec623f2aSMichael Turquette p = &pll->od; 1547a29a869SCarlo Caione reg = readl(pll->base + p->reg_off); 1557a29a869SCarlo Caione reg = PARM_SET(p->width, p->shift, reg, rate_set->od); 1567a29a869SCarlo Caione writel(reg, pll->base + p->reg_off); 1577a29a869SCarlo Caione 158*ec623f2aSMichael Turquette p = &pll->n; 1597a29a869SCarlo Caione ret = meson_clk_pll_wait_lock(pll, p); 1607a29a869SCarlo Caione if (ret) { 1617a29a869SCarlo Caione pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", 1627a29a869SCarlo Caione __func__, old_rate); 1637a29a869SCarlo Caione meson_clk_pll_set_rate(hw, old_rate, parent_rate); 1647a29a869SCarlo Caione } 1657a29a869SCarlo Caione 1667a29a869SCarlo Caione return ret; 1677a29a869SCarlo Caione } 1687a29a869SCarlo Caione 169*ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = { 1707a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 1717a29a869SCarlo Caione .round_rate = meson_clk_pll_round_rate, 1727a29a869SCarlo Caione .set_rate = meson_clk_pll_set_rate, 1737a29a869SCarlo Caione }; 1747a29a869SCarlo Caione 175*ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = { 1767a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 1777a29a869SCarlo Caione }; 178