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) 262bd1e256SChao Xie struct mmp_clk_factor { 276b63f023SChao Xie struct clk_hw hw; 286b63f023SChao Xie void __iomem *base; 292bd1e256SChao Xie struct mmp_clk_factor_masks *masks; 302bd1e256SChao Xie struct mmp_clk_factor_tbl *ftbl; 316b63f023SChao Xie unsigned int ftbl_cnt; 3261256133SChao Xie spinlock_t *lock; 336b63f023SChao Xie }; 346b63f023SChao Xie 356b63f023SChao Xie static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, 366b63f023SChao Xie unsigned long *prate) 376b63f023SChao Xie { 382bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 396b63f023SChao Xie unsigned long rate = 0, prev_rate; 406b63f023SChao Xie int i; 416b63f023SChao Xie 426b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 436b63f023SChao Xie prev_rate = rate; 44c45693a6SChao Xie rate = (((*prate / 10000) * factor->ftbl[i].den) / 45c45693a6SChao Xie (factor->ftbl[i].num * factor->masks->factor)) * 10000; 466b63f023SChao Xie if (rate > drate) 476b63f023SChao Xie break; 486b63f023SChao Xie } 495d26c15dSChao Xie if ((i == 0) || (i == factor->ftbl_cnt)) { 505d26c15dSChao Xie return rate; 515d26c15dSChao Xie } else { 525d26c15dSChao Xie if ((drate - prev_rate) > (rate - drate)) 536b63f023SChao Xie return rate; 546b63f023SChao Xie else 556b63f023SChao Xie return prev_rate; 566b63f023SChao Xie } 575d26c15dSChao Xie } 586b63f023SChao Xie 596b63f023SChao Xie static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, 606b63f023SChao Xie unsigned long parent_rate) 616b63f023SChao Xie { 622bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 632bd1e256SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 646b63f023SChao Xie unsigned int val, num, den; 656b63f023SChao Xie 666b63f023SChao Xie val = readl_relaxed(factor->base); 676b63f023SChao Xie 686b63f023SChao Xie /* calculate numerator */ 696b63f023SChao Xie num = (val >> masks->num_shift) & masks->num_mask; 706b63f023SChao Xie 716b63f023SChao Xie /* calculate denominator */ 727433ab43SChao Xie den = (val >> masks->den_shift) & masks->den_mask; 736b63f023SChao Xie 746b63f023SChao Xie if (!den) 756b63f023SChao Xie return 0; 766b63f023SChao Xie 776b63f023SChao Xie return (((parent_rate / 10000) * den) / 786b63f023SChao Xie (num * factor->masks->factor)) * 10000; 796b63f023SChao Xie } 806b63f023SChao Xie 816b63f023SChao Xie /* Configures new clock rate*/ 826b63f023SChao Xie static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, 836b63f023SChao Xie unsigned long prate) 846b63f023SChao Xie { 852bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 862bd1e256SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 876b63f023SChao Xie int i; 886b63f023SChao Xie unsigned long val; 896b63f023SChao Xie unsigned long prev_rate, rate = 0; 9061256133SChao Xie unsigned long flags = 0; 916b63f023SChao Xie 926b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 936b63f023SChao Xie prev_rate = rate; 94c45693a6SChao Xie rate = (((prate / 10000) * factor->ftbl[i].den) / 95c45693a6SChao Xie (factor->ftbl[i].num * factor->masks->factor)) * 10000; 966b63f023SChao Xie if (rate > drate) 976b63f023SChao Xie break; 986b63f023SChao Xie } 996b63f023SChao Xie if (i > 0) 1006b63f023SChao Xie i--; 1016b63f023SChao Xie 10261256133SChao Xie if (factor->lock) 10361256133SChao Xie spin_lock_irqsave(factor->lock, flags); 10461256133SChao Xie 1056b63f023SChao Xie val = readl_relaxed(factor->base); 1066b63f023SChao Xie 1076b63f023SChao Xie val &= ~(masks->num_mask << masks->num_shift); 1086b63f023SChao Xie val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift; 1096b63f023SChao Xie 1106b63f023SChao Xie val &= ~(masks->den_mask << masks->den_shift); 1116b63f023SChao Xie val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift; 1126b63f023SChao Xie 1136b63f023SChao Xie writel_relaxed(val, factor->base); 1146b63f023SChao Xie 11561256133SChao Xie if (factor->lock) 11661256133SChao Xie spin_unlock_irqrestore(factor->lock, flags); 11761256133SChao Xie 1186b63f023SChao Xie return 0; 1196b63f023SChao Xie } 1206b63f023SChao Xie 121*0c4c11f3SChao Xie void clk_factor_init(struct clk_hw *hw) 122*0c4c11f3SChao Xie { 123*0c4c11f3SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 124*0c4c11f3SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 125*0c4c11f3SChao Xie u32 val, num, den; 126*0c4c11f3SChao Xie int i; 127*0c4c11f3SChao Xie unsigned long flags = 0; 128*0c4c11f3SChao Xie 129*0c4c11f3SChao Xie if (factor->lock) 130*0c4c11f3SChao Xie spin_lock_irqsave(factor->lock, flags); 131*0c4c11f3SChao Xie 132*0c4c11f3SChao Xie val = readl(factor->base); 133*0c4c11f3SChao Xie 134*0c4c11f3SChao Xie /* calculate numerator */ 135*0c4c11f3SChao Xie num = (val >> masks->num_shift) & masks->num_mask; 136*0c4c11f3SChao Xie 137*0c4c11f3SChao Xie /* calculate denominator */ 138*0c4c11f3SChao Xie den = (val >> masks->den_shift) & masks->den_mask; 139*0c4c11f3SChao Xie 140*0c4c11f3SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) 141*0c4c11f3SChao Xie if (den == factor->ftbl[i].den && num == factor->ftbl[i].num) 142*0c4c11f3SChao Xie break; 143*0c4c11f3SChao Xie 144*0c4c11f3SChao Xie if (i >= factor->ftbl_cnt) { 145*0c4c11f3SChao Xie val &= ~(masks->num_mask << masks->num_shift); 146*0c4c11f3SChao Xie val |= (factor->ftbl[0].num & masks->num_mask) << 147*0c4c11f3SChao Xie masks->num_shift; 148*0c4c11f3SChao Xie 149*0c4c11f3SChao Xie val &= ~(masks->den_mask << masks->den_shift); 150*0c4c11f3SChao Xie val |= (factor->ftbl[0].den & masks->den_mask) << 151*0c4c11f3SChao Xie masks->den_shift; 152*0c4c11f3SChao Xie 153*0c4c11f3SChao Xie writel(val, factor->base); 154*0c4c11f3SChao Xie } 155*0c4c11f3SChao Xie 156*0c4c11f3SChao Xie if (factor->lock) 157*0c4c11f3SChao Xie spin_unlock_irqrestore(factor->lock, flags); 158*0c4c11f3SChao Xie } 159*0c4c11f3SChao Xie 1606b63f023SChao Xie static struct clk_ops clk_factor_ops = { 1616b63f023SChao Xie .recalc_rate = clk_factor_recalc_rate, 1626b63f023SChao Xie .round_rate = clk_factor_round_rate, 1636b63f023SChao Xie .set_rate = clk_factor_set_rate, 164*0c4c11f3SChao Xie .init = clk_factor_init, 1656b63f023SChao Xie }; 1666b63f023SChao Xie 1676b63f023SChao Xie struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, 1686b63f023SChao Xie unsigned long flags, void __iomem *base, 1692bd1e256SChao Xie struct mmp_clk_factor_masks *masks, 1702bd1e256SChao Xie struct mmp_clk_factor_tbl *ftbl, 17161256133SChao Xie unsigned int ftbl_cnt, spinlock_t *lock) 1726b63f023SChao Xie { 1732bd1e256SChao Xie struct mmp_clk_factor *factor; 1746b63f023SChao Xie struct clk_init_data init; 1756b63f023SChao Xie struct clk *clk; 1766b63f023SChao Xie 1776b63f023SChao Xie if (!masks) { 1786b63f023SChao Xie pr_err("%s: must pass a clk_factor_mask\n", __func__); 1796b63f023SChao Xie return ERR_PTR(-EINVAL); 1806b63f023SChao Xie } 1816b63f023SChao Xie 1826b63f023SChao Xie factor = kzalloc(sizeof(*factor), GFP_KERNEL); 1836b63f023SChao Xie if (!factor) { 1846b63f023SChao Xie pr_err("%s: could not allocate factor clk\n", __func__); 1856b63f023SChao Xie return ERR_PTR(-ENOMEM); 1866b63f023SChao Xie } 1876b63f023SChao Xie 1886b63f023SChao Xie /* struct clk_aux assignments */ 1896b63f023SChao Xie factor->base = base; 1906b63f023SChao Xie factor->masks = masks; 1916b63f023SChao Xie factor->ftbl = ftbl; 1926b63f023SChao Xie factor->ftbl_cnt = ftbl_cnt; 1936b63f023SChao Xie factor->hw.init = &init; 19461256133SChao Xie factor->lock = lock; 1956b63f023SChao Xie 1966b63f023SChao Xie init.name = name; 1976b63f023SChao Xie init.ops = &clk_factor_ops; 1986b63f023SChao Xie init.flags = flags; 1996b63f023SChao Xie init.parent_names = &parent_name; 2006b63f023SChao Xie init.num_parents = 1; 2016b63f023SChao Xie 2026b63f023SChao Xie clk = clk_register(NULL, &factor->hw); 2036b63f023SChao Xie if (IS_ERR_OR_NULL(clk)) 2046b63f023SChao Xie kfree(factor); 2056b63f023SChao Xie 2066b63f023SChao Xie return clk; 2076b63f023SChao Xie } 208