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 256b63f023SChao Xie #define to_clk_factor(hw) container_of(hw, struct clk_factor, hw) 266b63f023SChao Xie struct clk_factor { 276b63f023SChao Xie struct clk_hw hw; 286b63f023SChao Xie void __iomem *base; 296b63f023SChao Xie struct clk_factor_masks *masks; 306b63f023SChao Xie struct clk_factor_tbl *ftbl; 316b63f023SChao Xie unsigned int ftbl_cnt; 326b63f023SChao Xie }; 336b63f023SChao Xie 346b63f023SChao Xie static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, 356b63f023SChao Xie unsigned long *prate) 366b63f023SChao Xie { 376b63f023SChao Xie struct clk_factor *factor = to_clk_factor(hw); 386b63f023SChao Xie unsigned long rate = 0, prev_rate; 396b63f023SChao Xie int i; 406b63f023SChao Xie 416b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 426b63f023SChao Xie prev_rate = rate; 43*c45693a6SChao Xie rate = (((*prate / 10000) * factor->ftbl[i].den) / 44*c45693a6SChao Xie (factor->ftbl[i].num * factor->masks->factor)) * 10000; 456b63f023SChao Xie if (rate > drate) 466b63f023SChao Xie break; 476b63f023SChao Xie } 48*c45693a6SChao Xie if ((i == 0) || (i == factor->ftbl_cnt)) 496b63f023SChao Xie return rate; 506b63f023SChao Xie else 516b63f023SChao Xie return prev_rate; 526b63f023SChao Xie } 536b63f023SChao Xie 546b63f023SChao Xie static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, 556b63f023SChao Xie unsigned long parent_rate) 566b63f023SChao Xie { 576b63f023SChao Xie struct clk_factor *factor = to_clk_factor(hw); 586b63f023SChao Xie struct clk_factor_masks *masks = factor->masks; 596b63f023SChao Xie unsigned int val, num, den; 606b63f023SChao Xie 616b63f023SChao Xie val = readl_relaxed(factor->base); 626b63f023SChao Xie 636b63f023SChao Xie /* calculate numerator */ 646b63f023SChao Xie num = (val >> masks->num_shift) & masks->num_mask; 656b63f023SChao Xie 666b63f023SChao Xie /* calculate denominator */ 677433ab43SChao Xie den = (val >> masks->den_shift) & masks->den_mask; 686b63f023SChao Xie 696b63f023SChao Xie if (!den) 706b63f023SChao Xie return 0; 716b63f023SChao Xie 726b63f023SChao Xie return (((parent_rate / 10000) * den) / 736b63f023SChao Xie (num * factor->masks->factor)) * 10000; 746b63f023SChao Xie } 756b63f023SChao Xie 766b63f023SChao Xie /* Configures new clock rate*/ 776b63f023SChao Xie static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, 786b63f023SChao Xie unsigned long prate) 796b63f023SChao Xie { 806b63f023SChao Xie struct clk_factor *factor = to_clk_factor(hw); 816b63f023SChao Xie struct clk_factor_masks *masks = factor->masks; 826b63f023SChao Xie int i; 836b63f023SChao Xie unsigned long val; 846b63f023SChao Xie unsigned long prev_rate, rate = 0; 856b63f023SChao Xie 866b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 876b63f023SChao Xie prev_rate = rate; 88*c45693a6SChao Xie rate = (((prate / 10000) * factor->ftbl[i].den) / 89*c45693a6SChao Xie (factor->ftbl[i].num * factor->masks->factor)) * 10000; 906b63f023SChao Xie if (rate > drate) 916b63f023SChao Xie break; 926b63f023SChao Xie } 936b63f023SChao Xie if (i > 0) 946b63f023SChao Xie i--; 956b63f023SChao Xie 966b63f023SChao Xie val = readl_relaxed(factor->base); 976b63f023SChao Xie 986b63f023SChao Xie val &= ~(masks->num_mask << masks->num_shift); 996b63f023SChao Xie val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift; 1006b63f023SChao Xie 1016b63f023SChao Xie val &= ~(masks->den_mask << masks->den_shift); 1026b63f023SChao Xie val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift; 1036b63f023SChao Xie 1046b63f023SChao Xie writel_relaxed(val, factor->base); 1056b63f023SChao Xie 1066b63f023SChao Xie return 0; 1076b63f023SChao Xie } 1086b63f023SChao Xie 1096b63f023SChao Xie static struct clk_ops clk_factor_ops = { 1106b63f023SChao Xie .recalc_rate = clk_factor_recalc_rate, 1116b63f023SChao Xie .round_rate = clk_factor_round_rate, 1126b63f023SChao Xie .set_rate = clk_factor_set_rate, 1136b63f023SChao Xie }; 1146b63f023SChao Xie 1156b63f023SChao Xie struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, 1166b63f023SChao Xie unsigned long flags, void __iomem *base, 1176b63f023SChao Xie struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl, 1186b63f023SChao Xie unsigned int ftbl_cnt) 1196b63f023SChao Xie { 1206b63f023SChao Xie struct clk_factor *factor; 1216b63f023SChao Xie struct clk_init_data init; 1226b63f023SChao Xie struct clk *clk; 1236b63f023SChao Xie 1246b63f023SChao Xie if (!masks) { 1256b63f023SChao Xie pr_err("%s: must pass a clk_factor_mask\n", __func__); 1266b63f023SChao Xie return ERR_PTR(-EINVAL); 1276b63f023SChao Xie } 1286b63f023SChao Xie 1296b63f023SChao Xie factor = kzalloc(sizeof(*factor), GFP_KERNEL); 1306b63f023SChao Xie if (!factor) { 1316b63f023SChao Xie pr_err("%s: could not allocate factor clk\n", __func__); 1326b63f023SChao Xie return ERR_PTR(-ENOMEM); 1336b63f023SChao Xie } 1346b63f023SChao Xie 1356b63f023SChao Xie /* struct clk_aux assignments */ 1366b63f023SChao Xie factor->base = base; 1376b63f023SChao Xie factor->masks = masks; 1386b63f023SChao Xie factor->ftbl = ftbl; 1396b63f023SChao Xie factor->ftbl_cnt = ftbl_cnt; 1406b63f023SChao Xie factor->hw.init = &init; 1416b63f023SChao Xie 1426b63f023SChao Xie init.name = name; 1436b63f023SChao Xie init.ops = &clk_factor_ops; 1446b63f023SChao Xie init.flags = flags; 1456b63f023SChao Xie init.parent_names = &parent_name; 1466b63f023SChao Xie init.num_parents = 1; 1476b63f023SChao Xie 1486b63f023SChao Xie clk = clk_register(NULL, &factor->hw); 1496b63f023SChao Xie if (IS_ERR_OR_NULL(clk)) 1506b63f023SChao Xie kfree(factor); 1516b63f023SChao Xie 1526b63f023SChao Xie return clk; 1536b63f023SChao Xie } 154