18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Synopsys AXS10X SDP I2S PLL clock driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2016 Synopsys 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 78c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* PLL registers addresses */ 228c2ecf20Sopenharmony_ci#define PLL_IDIV_REG 0x0 238c2ecf20Sopenharmony_ci#define PLL_FBDIV_REG 0x4 248c2ecf20Sopenharmony_ci#define PLL_ODIV0_REG 0x8 258c2ecf20Sopenharmony_ci#define PLL_ODIV1_REG 0xC 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct i2s_pll_cfg { 288c2ecf20Sopenharmony_ci unsigned int rate; 298c2ecf20Sopenharmony_ci unsigned int idiv; 308c2ecf20Sopenharmony_ci unsigned int fbdiv; 318c2ecf20Sopenharmony_ci unsigned int odiv0; 328c2ecf20Sopenharmony_ci unsigned int odiv1; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct i2s_pll_cfg i2s_pll_cfg_27m[] = { 368c2ecf20Sopenharmony_ci /* 27 Mhz */ 378c2ecf20Sopenharmony_ci { 1024000, 0x104, 0x451, 0x10E38, 0x2000 }, 388c2ecf20Sopenharmony_ci { 1411200, 0x104, 0x596, 0x10D35, 0x2000 }, 398c2ecf20Sopenharmony_ci { 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 }, 408c2ecf20Sopenharmony_ci { 2048000, 0x82, 0x451, 0x10E38, 0x2000 }, 418c2ecf20Sopenharmony_ci { 2822400, 0x82, 0x596, 0x10D35, 0x2000 }, 428c2ecf20Sopenharmony_ci { 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 }, 438c2ecf20Sopenharmony_ci { 2116800, 0x82, 0x3CF, 0x10C30, 0x2000 }, 448c2ecf20Sopenharmony_ci { 2304000, 0x104, 0x79E, 0x10B2C, 0x2000 }, 458c2ecf20Sopenharmony_ci { 0, 0, 0, 0, 0 }, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const struct i2s_pll_cfg i2s_pll_cfg_28m[] = { 498c2ecf20Sopenharmony_ci /* 28.224 Mhz */ 508c2ecf20Sopenharmony_ci { 1024000, 0x82, 0x105, 0x107DF, 0x2000 }, 518c2ecf20Sopenharmony_ci { 1411200, 0x28A, 0x1, 0x10001, 0x2000 }, 528c2ecf20Sopenharmony_ci { 1536000, 0xA28, 0x187, 0x10042, 0x2000 }, 538c2ecf20Sopenharmony_ci { 2048000, 0x41, 0x105, 0x107DF, 0x2000 }, 548c2ecf20Sopenharmony_ci { 2822400, 0x145, 0x1, 0x10001, 0x2000 }, 558c2ecf20Sopenharmony_ci { 3072000, 0x514, 0x187, 0x10042, 0x2000 }, 568c2ecf20Sopenharmony_ci { 2116800, 0x514, 0x42, 0x10001, 0x2000 }, 578c2ecf20Sopenharmony_ci { 2304000, 0x619, 0x82, 0x10001, 0x2000 }, 588c2ecf20Sopenharmony_ci { 0, 0, 0, 0, 0 }, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct i2s_pll_clk { 628c2ecf20Sopenharmony_ci void __iomem *base; 638c2ecf20Sopenharmony_ci struct clk_hw hw; 648c2ecf20Sopenharmony_ci struct device *dev; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline void i2s_pll_write(struct i2s_pll_clk *clk, unsigned int reg, 688c2ecf20Sopenharmony_ci unsigned int val) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci writel_relaxed(val, clk->base + reg); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline unsigned int i2s_pll_read(struct i2s_pll_clk *clk, 748c2ecf20Sopenharmony_ci unsigned int reg) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci return readl_relaxed(clk->base + reg); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic inline struct i2s_pll_clk *to_i2s_pll_clk(struct clk_hw *hw) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return container_of(hw, struct i2s_pll_clk, hw); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic inline unsigned int i2s_pll_get_value(unsigned int val) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci return (val & 0x3F) + ((val >> 6) & 0x3F); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const struct i2s_pll_cfg *i2s_pll_get_cfg(unsigned long prate) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci switch (prate) { 928c2ecf20Sopenharmony_ci case 27000000: 938c2ecf20Sopenharmony_ci return i2s_pll_cfg_27m; 948c2ecf20Sopenharmony_ci case 28224000: 958c2ecf20Sopenharmony_ci return i2s_pll_cfg_28m; 968c2ecf20Sopenharmony_ci default: 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic unsigned long i2s_pll_recalc_rate(struct clk_hw *hw, 1028c2ecf20Sopenharmony_ci unsigned long parent_rate) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 1058c2ecf20Sopenharmony_ci unsigned int idiv, fbdiv, odiv; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci idiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_IDIV_REG)); 1088c2ecf20Sopenharmony_ci fbdiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_FBDIV_REG)); 1098c2ecf20Sopenharmony_ci odiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_ODIV0_REG)); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return ((parent_rate / idiv) * fbdiv) / odiv; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate, 1158c2ecf20Sopenharmony_ci unsigned long *prate) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 1188c2ecf20Sopenharmony_ci const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(*prate); 1198c2ecf20Sopenharmony_ci int i; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!pll_cfg) { 1228c2ecf20Sopenharmony_ci dev_err(clk->dev, "invalid parent rate=%ld\n", *prate); 1238c2ecf20Sopenharmony_ci return -EINVAL; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (i = 0; pll_cfg[i].rate != 0; i++) 1278c2ecf20Sopenharmony_ci if (pll_cfg[i].rate == rate) 1288c2ecf20Sopenharmony_ci return rate; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return -EINVAL; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1348c2ecf20Sopenharmony_ci unsigned long parent_rate) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 1378c2ecf20Sopenharmony_ci const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(parent_rate); 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!pll_cfg) { 1418c2ecf20Sopenharmony_ci dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate); 1428c2ecf20Sopenharmony_ci return -EINVAL; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci for (i = 0; pll_cfg[i].rate != 0; i++) { 1468c2ecf20Sopenharmony_ci if (pll_cfg[i].rate == rate) { 1478c2ecf20Sopenharmony_ci i2s_pll_write(clk, PLL_IDIV_REG, pll_cfg[i].idiv); 1488c2ecf20Sopenharmony_ci i2s_pll_write(clk, PLL_FBDIV_REG, pll_cfg[i].fbdiv); 1498c2ecf20Sopenharmony_ci i2s_pll_write(clk, PLL_ODIV0_REG, pll_cfg[i].odiv0); 1508c2ecf20Sopenharmony_ci i2s_pll_write(clk, PLL_ODIV1_REG, pll_cfg[i].odiv1); 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, 1568c2ecf20Sopenharmony_ci parent_rate); 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct clk_ops i2s_pll_ops = { 1618c2ecf20Sopenharmony_ci .recalc_rate = i2s_pll_recalc_rate, 1628c2ecf20Sopenharmony_ci .round_rate = i2s_pll_round_rate, 1638c2ecf20Sopenharmony_ci .set_rate = i2s_pll_set_rate, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int i2s_pll_clk_probe(struct platform_device *pdev) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1698c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 1708c2ecf20Sopenharmony_ci const char *clk_name; 1718c2ecf20Sopenharmony_ci const char *parent_name; 1728c2ecf20Sopenharmony_ci struct clk *clk; 1738c2ecf20Sopenharmony_ci struct i2s_pll_clk *pll_clk; 1748c2ecf20Sopenharmony_ci struct clk_init_data init; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); 1778c2ecf20Sopenharmony_ci if (!pll_clk) 1788c2ecf20Sopenharmony_ci return -ENOMEM; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci pll_clk->base = devm_platform_ioremap_resource(pdev, 0); 1818c2ecf20Sopenharmony_ci if (IS_ERR(pll_clk->base)) 1828c2ecf20Sopenharmony_ci return PTR_ERR(pll_clk->base); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci memset(&init, 0, sizeof(init)); 1858c2ecf20Sopenharmony_ci clk_name = node->name; 1868c2ecf20Sopenharmony_ci init.name = clk_name; 1878c2ecf20Sopenharmony_ci init.ops = &i2s_pll_ops; 1888c2ecf20Sopenharmony_ci parent_name = of_clk_get_parent_name(node, 0); 1898c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1908c2ecf20Sopenharmony_ci init.num_parents = 1; 1918c2ecf20Sopenharmony_ci pll_clk->hw.init = &init; 1928c2ecf20Sopenharmony_ci pll_clk->dev = dev; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci clk = devm_clk_register(dev, &pll_clk->hw); 1958c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1968c2ecf20Sopenharmony_ci dev_err(dev, "failed to register %s clock (%ld)\n", 1978c2ecf20Sopenharmony_ci clk_name, PTR_ERR(clk)); 1988c2ecf20Sopenharmony_ci return PTR_ERR(clk); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return of_clk_add_provider(node, of_clk_src_simple_get, clk); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int i2s_pll_clk_remove(struct platform_device *pdev) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci of_clk_del_provider(pdev->dev.of_node); 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct of_device_id i2s_pll_clk_id[] = { 2118c2ecf20Sopenharmony_ci { .compatible = "snps,axs10x-i2s-pll-clock", }, 2128c2ecf20Sopenharmony_ci { }, 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2s_pll_clk_id); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic struct platform_driver i2s_pll_clk_driver = { 2178c2ecf20Sopenharmony_ci .driver = { 2188c2ecf20Sopenharmony_ci .name = "axs10x-i2s-pll-clock", 2198c2ecf20Sopenharmony_ci .of_match_table = i2s_pll_clk_id, 2208c2ecf20Sopenharmony_ci }, 2218c2ecf20Sopenharmony_ci .probe = i2s_pll_clk_probe, 2228c2ecf20Sopenharmony_ci .remove = i2s_pll_clk_remove, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_cimodule_platform_driver(i2s_pll_clk_driver); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jose Abreu <joabreu@synopsys.com>"); 2278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys AXS10X SDP I2S PLL Clock Driver"); 2288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 229