16b63f023SChao Xie /* 26b63f023SChao Xie * mmp factor clock operation source file 36b63f023SChao Xie * 46b63f023SChao Xie * Copyright (C) 2012 Marvell 56b63f023SChao Xie * Chao Xie <xiechao.mail@gmail.com> 66b63f023SChao Xie * 76b63f023SChao Xie * This file is licensed under the terms of the GNU General Public 86b63f023SChao Xie * License version 2. This program is licensed "as is" without any 96b63f023SChao Xie * warranty of any kind, whether express or implied. 106b63f023SChao Xie */ 116b63f023SChao Xie 126b63f023SChao Xie #include <linux/clk-provider.h> 136b63f023SChao Xie #include <linux/slab.h> 146b63f023SChao Xie #include <linux/io.h> 156b63f023SChao Xie #include <linux/err.h> 166b63f023SChao Xie 176b63f023SChao Xie #include "clk.h" 186b63f023SChao Xie /* 196b63f023SChao Xie * It is M/N clock 206b63f023SChao Xie * 216b63f023SChao Xie * Fout from synthesizer can be given from two equations: 226b63f023SChao Xie * numerator/denominator = Fin / (Fout * factor) 236b63f023SChao Xie */ 246b63f023SChao Xie 252bd1e256SChao Xie #define to_clk_factor(hw) container_of(hw, struct mmp_clk_factor, hw) 266b63f023SChao Xie 276b63f023SChao Xie static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, 286b63f023SChao Xie unsigned long *prate) 296b63f023SChao Xie { 302bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 316b63f023SChao Xie unsigned long rate = 0, prev_rate; 326b63f023SChao Xie int i; 336b63f023SChao Xie 346b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 356b63f023SChao Xie prev_rate = rate; 36c45693a6SChao Xie rate = (((*prate / 10000) * factor->ftbl[i].den) / 37c45693a6SChao Xie (factor->ftbl[i].num * factor->masks->factor)) * 10000; 386b63f023SChao Xie if (rate > drate) 396b63f023SChao Xie break; 406b63f023SChao Xie } 415d26c15dSChao Xie if ((i == 0) || (i == factor->ftbl_cnt)) { 425d26c15dSChao Xie return rate; 435d26c15dSChao Xie } else { 445d26c15dSChao Xie if ((drate - prev_rate) > (rate - drate)) 456b63f023SChao Xie return rate; 466b63f023SChao Xie else 476b63f023SChao Xie return prev_rate; 486b63f023SChao Xie } 495d26c15dSChao Xie } 506b63f023SChao Xie 516b63f023SChao Xie static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, 526b63f023SChao Xie unsigned long parent_rate) 536b63f023SChao Xie { 542bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 552bd1e256SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 566b63f023SChao Xie unsigned int val, num, den; 576b63f023SChao Xie 586b63f023SChao Xie val = readl_relaxed(factor->base); 596b63f023SChao Xie 606b63f023SChao Xie /* calculate numerator */ 616b63f023SChao Xie num = (val >> masks->num_shift) & masks->num_mask; 626b63f023SChao Xie 636b63f023SChao Xie /* calculate denominator */ 647433ab43SChao Xie den = (val >> masks->den_shift) & masks->den_mask; 656b63f023SChao Xie 666b63f023SChao Xie if (!den) 676b63f023SChao Xie return 0; 686b63f023SChao Xie 696b63f023SChao Xie return (((parent_rate / 10000) * den) / 706b63f023SChao Xie (num * factor->masks->factor)) * 10000; 716b63f023SChao Xie } 726b63f023SChao Xie 736b63f023SChao Xie /* Configures new clock rate*/ 746b63f023SChao Xie static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, 756b63f023SChao Xie unsigned long prate) 766b63f023SChao Xie { 772bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 782bd1e256SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 796b63f023SChao Xie int i; 806b63f023SChao Xie unsigned long val; 816b63f023SChao Xie unsigned long prev_rate, rate = 0; 8261256133SChao Xie unsigned long flags = 0; 836b63f023SChao Xie 846b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 856b63f023SChao Xie prev_rate = rate; 86c45693a6SChao Xie rate = (((prate / 10000) * factor->ftbl[i].den) / 87c45693a6SChao Xie (factor->ftbl[i].num * factor->masks->factor)) * 10000; 886b63f023SChao Xie if (rate > drate) 896b63f023SChao Xie break; 906b63f023SChao Xie } 916b63f023SChao Xie if (i > 0) 926b63f023SChao Xie i--; 936b63f023SChao Xie 9461256133SChao Xie if (factor->lock) 9561256133SChao Xie spin_lock_irqsave(factor->lock, flags); 9661256133SChao Xie 976b63f023SChao Xie val = readl_relaxed(factor->base); 986b63f023SChao Xie 996b63f023SChao Xie val &= ~(masks->num_mask << masks->num_shift); 1006b63f023SChao Xie val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift; 1016b63f023SChao Xie 1026b63f023SChao Xie val &= ~(masks->den_mask << masks->den_shift); 1036b63f023SChao Xie val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift; 1046b63f023SChao Xie 1056b63f023SChao Xie writel_relaxed(val, factor->base); 1066b63f023SChao Xie 10761256133SChao Xie if (factor->lock) 10861256133SChao Xie spin_unlock_irqrestore(factor->lock, flags); 10961256133SChao Xie 1106b63f023SChao Xie return 0; 1116b63f023SChao Xie } 1126b63f023SChao Xie 11374fc23aaSWei Yongjun static void clk_factor_init(struct clk_hw *hw) 1140c4c11f3SChao Xie { 1150c4c11f3SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 1160c4c11f3SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 1170c4c11f3SChao Xie u32 val, num, den; 1180c4c11f3SChao Xie int i; 1190c4c11f3SChao Xie unsigned long flags = 0; 1200c4c11f3SChao Xie 1210c4c11f3SChao Xie if (factor->lock) 1220c4c11f3SChao Xie spin_lock_irqsave(factor->lock, flags); 1230c4c11f3SChao Xie 1240c4c11f3SChao Xie val = readl(factor->base); 1250c4c11f3SChao Xie 1260c4c11f3SChao Xie /* calculate numerator */ 1270c4c11f3SChao Xie num = (val >> masks->num_shift) & masks->num_mask; 1280c4c11f3SChao Xie 1290c4c11f3SChao Xie /* calculate denominator */ 1300c4c11f3SChao Xie den = (val >> masks->den_shift) & masks->den_mask; 1310c4c11f3SChao Xie 1320c4c11f3SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) 1330c4c11f3SChao Xie if (den == factor->ftbl[i].den && num == factor->ftbl[i].num) 1340c4c11f3SChao Xie break; 1350c4c11f3SChao Xie 1360c4c11f3SChao Xie if (i >= factor->ftbl_cnt) { 1370c4c11f3SChao Xie val &= ~(masks->num_mask << masks->num_shift); 1380c4c11f3SChao Xie val |= (factor->ftbl[0].num & masks->num_mask) << 1390c4c11f3SChao Xie masks->num_shift; 1400c4c11f3SChao Xie 1410c4c11f3SChao Xie val &= ~(masks->den_mask << masks->den_shift); 1420c4c11f3SChao Xie val |= (factor->ftbl[0].den & masks->den_mask) << 1430c4c11f3SChao Xie masks->den_shift; 1440c4c11f3SChao Xie 1450c4c11f3SChao Xie writel(val, factor->base); 1460c4c11f3SChao Xie } 1470c4c11f3SChao Xie 1480c4c11f3SChao Xie if (factor->lock) 1490c4c11f3SChao Xie spin_unlock_irqrestore(factor->lock, flags); 1500c4c11f3SChao Xie } 1510c4c11f3SChao Xie 152*e90a7da4SBhumika Goyal static const struct clk_ops clk_factor_ops = { 1536b63f023SChao Xie .recalc_rate = clk_factor_recalc_rate, 1546b63f023SChao Xie .round_rate = clk_factor_round_rate, 1556b63f023SChao Xie .set_rate = clk_factor_set_rate, 1560c4c11f3SChao Xie .init = clk_factor_init, 1576b63f023SChao Xie }; 1586b63f023SChao Xie 1596b63f023SChao Xie struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, 1606b63f023SChao Xie unsigned long flags, void __iomem *base, 1612bd1e256SChao Xie struct mmp_clk_factor_masks *masks, 1622bd1e256SChao Xie struct mmp_clk_factor_tbl *ftbl, 16361256133SChao Xie unsigned int ftbl_cnt, spinlock_t *lock) 1646b63f023SChao Xie { 1652bd1e256SChao Xie struct mmp_clk_factor *factor; 1666b63f023SChao Xie struct clk_init_data init; 1676b63f023SChao Xie struct clk *clk; 1686b63f023SChao Xie 1696b63f023SChao Xie if (!masks) { 1706b63f023SChao Xie pr_err("%s: must pass a clk_factor_mask\n", __func__); 1716b63f023SChao Xie return ERR_PTR(-EINVAL); 1726b63f023SChao Xie } 1736b63f023SChao Xie 1746b63f023SChao Xie factor = kzalloc(sizeof(*factor), GFP_KERNEL); 1756b63f023SChao Xie if (!factor) { 1766b63f023SChao Xie pr_err("%s: could not allocate factor clk\n", __func__); 1776b63f023SChao Xie return ERR_PTR(-ENOMEM); 1786b63f023SChao Xie } 1796b63f023SChao Xie 1806b63f023SChao Xie /* struct clk_aux assignments */ 1816b63f023SChao Xie factor->base = base; 1826b63f023SChao Xie factor->masks = masks; 1836b63f023SChao Xie factor->ftbl = ftbl; 1846b63f023SChao Xie factor->ftbl_cnt = ftbl_cnt; 1856b63f023SChao Xie factor->hw.init = &init; 18661256133SChao Xie factor->lock = lock; 1876b63f023SChao Xie 1886b63f023SChao Xie init.name = name; 1896b63f023SChao Xie init.ops = &clk_factor_ops; 1906b63f023SChao Xie init.flags = flags; 1916b63f023SChao Xie init.parent_names = &parent_name; 1926b63f023SChao Xie init.num_parents = 1; 1936b63f023SChao Xie 1946b63f023SChao Xie clk = clk_register(NULL, &factor->hw); 1956b63f023SChao Xie if (IS_ERR_OR_NULL(clk)) 1966b63f023SChao Xie kfree(factor); 1976b63f023SChao Xie 1986b63f023SChao Xie return clk; 1996b63f023SChao Xie } 200