1*3bb16560SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26b63f023SChao Xie /* 36b63f023SChao Xie * mmp factor clock operation source file 46b63f023SChao Xie * 56b63f023SChao Xie * Copyright (C) 2012 Marvell 66b63f023SChao Xie * Chao Xie <xiechao.mail@gmail.com> 76b63f023SChao Xie */ 86b63f023SChao Xie 96b63f023SChao Xie #include <linux/clk-provider.h> 106b63f023SChao Xie #include <linux/slab.h> 116b63f023SChao Xie #include <linux/io.h> 126b63f023SChao Xie #include <linux/err.h> 136b63f023SChao Xie 146b63f023SChao Xie #include "clk.h" 156b63f023SChao Xie /* 166b63f023SChao Xie * It is M/N clock 176b63f023SChao Xie * 186b63f023SChao Xie * Fout from synthesizer can be given from two equations: 196b63f023SChao Xie * numerator/denominator = Fin / (Fout * factor) 206b63f023SChao Xie */ 216b63f023SChao Xie 222bd1e256SChao Xie #define to_clk_factor(hw) container_of(hw, struct mmp_clk_factor, hw) 236b63f023SChao Xie 246b63f023SChao Xie static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, 256b63f023SChao Xie unsigned long *prate) 266b63f023SChao Xie { 272bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 2806030c4eSLubomir Rintel u64 rate = 0, prev_rate; 296b63f023SChao Xie int i; 306b63f023SChao Xie 316b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 326b63f023SChao Xie prev_rate = rate; 3306030c4eSLubomir Rintel rate = *prate; 3406030c4eSLubomir Rintel rate *= factor->ftbl[i].den; 3506030c4eSLubomir Rintel do_div(rate, factor->ftbl[i].num * factor->masks->factor); 3606030c4eSLubomir Rintel 376b63f023SChao Xie if (rate > drate) 386b63f023SChao Xie break; 396b63f023SChao Xie } 405d26c15dSChao Xie if ((i == 0) || (i == factor->ftbl_cnt)) { 415d26c15dSChao Xie return rate; 425d26c15dSChao Xie } else { 435d26c15dSChao Xie if ((drate - prev_rate) > (rate - drate)) 446b63f023SChao Xie return rate; 456b63f023SChao Xie else 466b63f023SChao Xie return prev_rate; 476b63f023SChao Xie } 485d26c15dSChao Xie } 496b63f023SChao Xie 506b63f023SChao Xie static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, 516b63f023SChao Xie unsigned long parent_rate) 526b63f023SChao Xie { 532bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 542bd1e256SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 556b63f023SChao Xie unsigned int val, num, den; 5606030c4eSLubomir Rintel u64 rate; 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 6906030c4eSLubomir Rintel rate = parent_rate; 7006030c4eSLubomir Rintel rate *= den; 7106030c4eSLubomir Rintel do_div(rate, num * factor->masks->factor); 7206030c4eSLubomir Rintel 7306030c4eSLubomir Rintel return rate; 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 { 802bd1e256SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 812bd1e256SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 826b63f023SChao Xie int i; 836b63f023SChao Xie unsigned long val; 8461256133SChao Xie unsigned long flags = 0; 8506030c4eSLubomir Rintel u64 rate = 0; 866b63f023SChao Xie 876b63f023SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) { 8806030c4eSLubomir Rintel rate = prate; 8906030c4eSLubomir Rintel rate *= factor->ftbl[i].den; 9006030c4eSLubomir Rintel do_div(rate, factor->ftbl[i].num * factor->masks->factor); 9106030c4eSLubomir Rintel 926b63f023SChao Xie if (rate > drate) 936b63f023SChao Xie break; 946b63f023SChao Xie } 956b63f023SChao Xie if (i > 0) 966b63f023SChao Xie i--; 976b63f023SChao Xie 9861256133SChao Xie if (factor->lock) 9961256133SChao Xie spin_lock_irqsave(factor->lock, flags); 10061256133SChao Xie 1016b63f023SChao Xie val = readl_relaxed(factor->base); 1026b63f023SChao Xie 1036b63f023SChao Xie val &= ~(masks->num_mask << masks->num_shift); 1046b63f023SChao Xie val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift; 1056b63f023SChao Xie 1066b63f023SChao Xie val &= ~(masks->den_mask << masks->den_shift); 1076b63f023SChao Xie val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift; 1086b63f023SChao Xie 1096b63f023SChao Xie writel_relaxed(val, factor->base); 1106b63f023SChao Xie 11161256133SChao Xie if (factor->lock) 11261256133SChao Xie spin_unlock_irqrestore(factor->lock, flags); 11361256133SChao Xie 1146b63f023SChao Xie return 0; 1156b63f023SChao Xie } 1166b63f023SChao Xie 11789d079dcSJerome Brunet static int clk_factor_init(struct clk_hw *hw) 1180c4c11f3SChao Xie { 1190c4c11f3SChao Xie struct mmp_clk_factor *factor = to_clk_factor(hw); 1200c4c11f3SChao Xie struct mmp_clk_factor_masks *masks = factor->masks; 1210c4c11f3SChao Xie u32 val, num, den; 1220c4c11f3SChao Xie int i; 1230c4c11f3SChao Xie unsigned long flags = 0; 1240c4c11f3SChao Xie 1250c4c11f3SChao Xie if (factor->lock) 1260c4c11f3SChao Xie spin_lock_irqsave(factor->lock, flags); 1270c4c11f3SChao Xie 1280c4c11f3SChao Xie val = readl(factor->base); 1290c4c11f3SChao Xie 1300c4c11f3SChao Xie /* calculate numerator */ 1310c4c11f3SChao Xie num = (val >> masks->num_shift) & masks->num_mask; 1320c4c11f3SChao Xie 1330c4c11f3SChao Xie /* calculate denominator */ 1340c4c11f3SChao Xie den = (val >> masks->den_shift) & masks->den_mask; 1350c4c11f3SChao Xie 1360c4c11f3SChao Xie for (i = 0; i < factor->ftbl_cnt; i++) 1370c4c11f3SChao Xie if (den == factor->ftbl[i].den && num == factor->ftbl[i].num) 1380c4c11f3SChao Xie break; 1390c4c11f3SChao Xie 1400c4c11f3SChao Xie if (i >= factor->ftbl_cnt) { 1410c4c11f3SChao Xie val &= ~(masks->num_mask << masks->num_shift); 1420c4c11f3SChao Xie val |= (factor->ftbl[0].num & masks->num_mask) << 1430c4c11f3SChao Xie masks->num_shift; 1440c4c11f3SChao Xie 1450c4c11f3SChao Xie val &= ~(masks->den_mask << masks->den_shift); 1460c4c11f3SChao Xie val |= (factor->ftbl[0].den & masks->den_mask) << 1470c4c11f3SChao Xie masks->den_shift; 1485278acc4SLubomir Rintel } 1490c4c11f3SChao Xie 1505278acc4SLubomir Rintel if (!(val & masks->enable_mask) || i >= factor->ftbl_cnt) { 1515278acc4SLubomir Rintel val |= masks->enable_mask; 1520c4c11f3SChao Xie writel(val, factor->base); 1530c4c11f3SChao Xie } 1540c4c11f3SChao Xie 1550c4c11f3SChao Xie if (factor->lock) 1560c4c11f3SChao Xie spin_unlock_irqrestore(factor->lock, flags); 15789d079dcSJerome Brunet 15889d079dcSJerome Brunet return 0; 1590c4c11f3SChao Xie } 1600c4c11f3SChao Xie 161e90a7da4SBhumika Goyal static const struct clk_ops clk_factor_ops = { 1626b63f023SChao Xie .recalc_rate = clk_factor_recalc_rate, 1636b63f023SChao Xie .round_rate = clk_factor_round_rate, 1646b63f023SChao Xie .set_rate = clk_factor_set_rate, 1650c4c11f3SChao Xie .init = clk_factor_init, 1666b63f023SChao Xie }; 1676b63f023SChao Xie 1686b63f023SChao Xie struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, 1696b63f023SChao Xie unsigned long flags, void __iomem *base, 1702bd1e256SChao Xie struct mmp_clk_factor_masks *masks, 1712bd1e256SChao Xie struct mmp_clk_factor_tbl *ftbl, 17261256133SChao Xie unsigned int ftbl_cnt, spinlock_t *lock) 1736b63f023SChao Xie { 1742bd1e256SChao Xie struct mmp_clk_factor *factor; 1756b63f023SChao Xie struct clk_init_data init; 1766b63f023SChao Xie struct clk *clk; 1776b63f023SChao Xie 1786b63f023SChao Xie if (!masks) { 1796b63f023SChao Xie pr_err("%s: must pass a clk_factor_mask\n", __func__); 1806b63f023SChao Xie return ERR_PTR(-EINVAL); 1816b63f023SChao Xie } 1826b63f023SChao Xie 1836b63f023SChao Xie factor = kzalloc(sizeof(*factor), GFP_KERNEL); 1841cc36f73SMarkus Elfring if (!factor) 1856b63f023SChao Xie return ERR_PTR(-ENOMEM); 1866b63f023SChao Xie 1876b63f023SChao Xie /* struct clk_aux assignments */ 1886b63f023SChao Xie factor->base = base; 1896b63f023SChao Xie factor->masks = masks; 1906b63f023SChao Xie factor->ftbl = ftbl; 1916b63f023SChao Xie factor->ftbl_cnt = ftbl_cnt; 1926b63f023SChao Xie factor->hw.init = &init; 19361256133SChao Xie factor->lock = lock; 1946b63f023SChao Xie 1956b63f023SChao Xie init.name = name; 1966b63f023SChao Xie init.ops = &clk_factor_ops; 1976b63f023SChao Xie init.flags = flags; 1986b63f023SChao Xie init.parent_names = &parent_name; 1996b63f023SChao Xie init.num_parents = 1; 2006b63f023SChao Xie 2016b63f023SChao Xie clk = clk_register(NULL, &factor->hw); 2026b63f023SChao Xie if (IS_ERR_OR_NULL(clk)) 2036b63f023SChao Xie kfree(factor); 2046b63f023SChao Xie 2056b63f023SChao Xie return clk; 2066b63f023SChao Xie } 207