18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Free Electrons 48c2ecf20Sopenharmony_ci * Copyright (C) 2016 NextThing Co 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/regmap.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "sun4i_tcon.h" 138c2ecf20Sopenharmony_ci#include "sun4i_dotclock.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct sun4i_dclk { 168c2ecf20Sopenharmony_ci struct clk_hw hw; 178c2ecf20Sopenharmony_ci struct regmap *regmap; 188c2ecf20Sopenharmony_ci struct sun4i_tcon *tcon; 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci return container_of(hw, struct sun4i_dclk, hw); 248c2ecf20Sopenharmony_ci} 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void sun4i_dclk_disable(struct clk_hw *hw) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, 318c2ecf20Sopenharmony_ci BIT(SUN4I_TCON0_DCLK_GATE_BIT), 0); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int sun4i_dclk_enable(struct clk_hw *hw) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, 398c2ecf20Sopenharmony_ci BIT(SUN4I_TCON0_DCLK_GATE_BIT), 408c2ecf20Sopenharmony_ci BIT(SUN4I_TCON0_DCLK_GATE_BIT)); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int sun4i_dclk_is_enabled(struct clk_hw *hw) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 468c2ecf20Sopenharmony_ci u32 val; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return val & BIT(SUN4I_TCON0_DCLK_GATE_BIT); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw, 548c2ecf20Sopenharmony_ci unsigned long parent_rate) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 578c2ecf20Sopenharmony_ci u32 val; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci val >>= SUN4I_TCON0_DCLK_DIV_SHIFT; 628c2ecf20Sopenharmony_ci val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (!val) 658c2ecf20Sopenharmony_ci val = 1; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return parent_rate / val; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, 718c2ecf20Sopenharmony_ci unsigned long *parent_rate) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 748c2ecf20Sopenharmony_ci struct sun4i_tcon *tcon = dclk->tcon; 758c2ecf20Sopenharmony_ci unsigned long best_parent = 0; 768c2ecf20Sopenharmony_ci u8 best_div = 1; 778c2ecf20Sopenharmony_ci int i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) { 808c2ecf20Sopenharmony_ci u64 ideal = (u64)rate * i; 818c2ecf20Sopenharmony_ci unsigned long rounded; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * ideal has overflowed the max value that can be stored in an 858c2ecf20Sopenharmony_ci * unsigned long, and every clk operation we might do on a 868c2ecf20Sopenharmony_ci * truncated u64 value will give us incorrect results. 878c2ecf20Sopenharmony_ci * Let's just stop there since bigger dividers will result in 888c2ecf20Sopenharmony_ci * the same overflow issue. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if (ideal > ULONG_MAX) 918c2ecf20Sopenharmony_ci goto out; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci rounded = clk_hw_round_rate(clk_hw_get_parent(hw), 948c2ecf20Sopenharmony_ci ideal); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (rounded == ideal) { 978c2ecf20Sopenharmony_ci best_parent = rounded; 988c2ecf20Sopenharmony_ci best_div = i; 998c2ecf20Sopenharmony_ci goto out; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (abs(rate - rounded / i) < 1038c2ecf20Sopenharmony_ci abs(rate - best_parent / best_div)) { 1048c2ecf20Sopenharmony_ci best_parent = rounded; 1058c2ecf20Sopenharmony_ci best_div = i; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ciout: 1108c2ecf20Sopenharmony_ci *parent_rate = best_parent; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return best_parent / best_div; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate, 1168c2ecf20Sopenharmony_ci unsigned long parent_rate) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 1198c2ecf20Sopenharmony_ci u8 div = parent_rate / rate; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, 1228c2ecf20Sopenharmony_ci GENMASK(6, 0), div); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int sun4i_dclk_get_phase(struct clk_hw *hw) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 1288c2ecf20Sopenharmony_ci u32 val; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci regmap_read(dclk->regmap, SUN4I_TCON0_IO_POL_REG, &val); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci val >>= 28; 1338c2ecf20Sopenharmony_ci val &= 3; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return val * 120; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int sun4i_dclk_set_phase(struct clk_hw *hw, int degrees) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk = hw_to_dclk(hw); 1418c2ecf20Sopenharmony_ci u32 val = degrees / 120; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci val <<= 28; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci regmap_update_bits(dclk->regmap, SUN4I_TCON0_IO_POL_REG, 1468c2ecf20Sopenharmony_ci GENMASK(29, 28), 1478c2ecf20Sopenharmony_ci val); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct clk_ops sun4i_dclk_ops = { 1538c2ecf20Sopenharmony_ci .disable = sun4i_dclk_disable, 1548c2ecf20Sopenharmony_ci .enable = sun4i_dclk_enable, 1558c2ecf20Sopenharmony_ci .is_enabled = sun4i_dclk_is_enabled, 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci .recalc_rate = sun4i_dclk_recalc_rate, 1588c2ecf20Sopenharmony_ci .round_rate = sun4i_dclk_round_rate, 1598c2ecf20Sopenharmony_ci .set_rate = sun4i_dclk_set_rate, 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci .get_phase = sun4i_dclk_get_phase, 1628c2ecf20Sopenharmony_ci .set_phase = sun4i_dclk_set_phase, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciint sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci const char *clk_name, *parent_name; 1688c2ecf20Sopenharmony_ci struct clk_init_data init; 1698c2ecf20Sopenharmony_ci struct sun4i_dclk *dclk; 1708c2ecf20Sopenharmony_ci int ret; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci parent_name = __clk_get_name(tcon->sclk0); 1738c2ecf20Sopenharmony_ci ret = of_property_read_string_index(dev->of_node, 1748c2ecf20Sopenharmony_ci "clock-output-names", 0, 1758c2ecf20Sopenharmony_ci &clk_name); 1768c2ecf20Sopenharmony_ci if (ret) 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); 1808c2ecf20Sopenharmony_ci if (!dclk) 1818c2ecf20Sopenharmony_ci return -ENOMEM; 1828c2ecf20Sopenharmony_ci dclk->tcon = tcon; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci init.name = clk_name; 1858c2ecf20Sopenharmony_ci init.ops = &sun4i_dclk_ops; 1868c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1878c2ecf20Sopenharmony_ci init.num_parents = 1; 1888c2ecf20Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci dclk->regmap = tcon->regs; 1918c2ecf20Sopenharmony_ci dclk->hw.init = &init; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci tcon->dclk = clk_register(dev, &dclk->hw); 1948c2ecf20Sopenharmony_ci if (IS_ERR(tcon->dclk)) 1958c2ecf20Sopenharmony_ci return PTR_ERR(tcon->dclk); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_dclk_create); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ciint sun4i_dclk_free(struct sun4i_tcon *tcon) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci clk_unregister(tcon->dclk); 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_dclk_free); 207