162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Synopsys AXS10X SDP I2S PLL clock driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Synopsys 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/platform_device.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/clk-provider.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* PLL registers addresses */ 1962306a36Sopenharmony_ci#define PLL_IDIV_REG 0x0 2062306a36Sopenharmony_ci#define PLL_FBDIV_REG 0x4 2162306a36Sopenharmony_ci#define PLL_ODIV0_REG 0x8 2262306a36Sopenharmony_ci#define PLL_ODIV1_REG 0xC 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct i2s_pll_cfg { 2562306a36Sopenharmony_ci unsigned int rate; 2662306a36Sopenharmony_ci unsigned int idiv; 2762306a36Sopenharmony_ci unsigned int fbdiv; 2862306a36Sopenharmony_ci unsigned int odiv0; 2962306a36Sopenharmony_ci unsigned int odiv1; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic const struct i2s_pll_cfg i2s_pll_cfg_27m[] = { 3362306a36Sopenharmony_ci /* 27 Mhz */ 3462306a36Sopenharmony_ci { 1024000, 0x104, 0x451, 0x10E38, 0x2000 }, 3562306a36Sopenharmony_ci { 1411200, 0x104, 0x596, 0x10D35, 0x2000 }, 3662306a36Sopenharmony_ci { 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 }, 3762306a36Sopenharmony_ci { 2048000, 0x82, 0x451, 0x10E38, 0x2000 }, 3862306a36Sopenharmony_ci { 2822400, 0x82, 0x596, 0x10D35, 0x2000 }, 3962306a36Sopenharmony_ci { 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 }, 4062306a36Sopenharmony_ci { 2116800, 0x82, 0x3CF, 0x10C30, 0x2000 }, 4162306a36Sopenharmony_ci { 2304000, 0x104, 0x79E, 0x10B2C, 0x2000 }, 4262306a36Sopenharmony_ci { 0, 0, 0, 0, 0 }, 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const struct i2s_pll_cfg i2s_pll_cfg_28m[] = { 4662306a36Sopenharmony_ci /* 28.224 Mhz */ 4762306a36Sopenharmony_ci { 1024000, 0x82, 0x105, 0x107DF, 0x2000 }, 4862306a36Sopenharmony_ci { 1411200, 0x28A, 0x1, 0x10001, 0x2000 }, 4962306a36Sopenharmony_ci { 1536000, 0xA28, 0x187, 0x10042, 0x2000 }, 5062306a36Sopenharmony_ci { 2048000, 0x41, 0x105, 0x107DF, 0x2000 }, 5162306a36Sopenharmony_ci { 2822400, 0x145, 0x1, 0x10001, 0x2000 }, 5262306a36Sopenharmony_ci { 3072000, 0x514, 0x187, 0x10042, 0x2000 }, 5362306a36Sopenharmony_ci { 2116800, 0x514, 0x42, 0x10001, 0x2000 }, 5462306a36Sopenharmony_ci { 2304000, 0x619, 0x82, 0x10001, 0x2000 }, 5562306a36Sopenharmony_ci { 0, 0, 0, 0, 0 }, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct i2s_pll_clk { 5962306a36Sopenharmony_ci void __iomem *base; 6062306a36Sopenharmony_ci struct clk_hw hw; 6162306a36Sopenharmony_ci struct device *dev; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic inline void i2s_pll_write(struct i2s_pll_clk *clk, unsigned int reg, 6562306a36Sopenharmony_ci unsigned int val) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci writel_relaxed(val, clk->base + reg); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic inline unsigned int i2s_pll_read(struct i2s_pll_clk *clk, 7162306a36Sopenharmony_ci unsigned int reg) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return readl_relaxed(clk->base + reg); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline struct i2s_pll_clk *to_i2s_pll_clk(struct clk_hw *hw) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return container_of(hw, struct i2s_pll_clk, hw); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline unsigned int i2s_pll_get_value(unsigned int val) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return (val & 0x3F) + ((val >> 6) & 0x3F); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const struct i2s_pll_cfg *i2s_pll_get_cfg(unsigned long prate) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci switch (prate) { 8962306a36Sopenharmony_ci case 27000000: 9062306a36Sopenharmony_ci return i2s_pll_cfg_27m; 9162306a36Sopenharmony_ci case 28224000: 9262306a36Sopenharmony_ci return i2s_pll_cfg_28m; 9362306a36Sopenharmony_ci default: 9462306a36Sopenharmony_ci return NULL; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic unsigned long i2s_pll_recalc_rate(struct clk_hw *hw, 9962306a36Sopenharmony_ci unsigned long parent_rate) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 10262306a36Sopenharmony_ci unsigned int idiv, fbdiv, odiv; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci idiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_IDIV_REG)); 10562306a36Sopenharmony_ci fbdiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_FBDIV_REG)); 10662306a36Sopenharmony_ci odiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_ODIV0_REG)); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return ((parent_rate / idiv) * fbdiv) / odiv; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate, 11262306a36Sopenharmony_ci unsigned long *prate) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 11562306a36Sopenharmony_ci const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(*prate); 11662306a36Sopenharmony_ci int i; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!pll_cfg) { 11962306a36Sopenharmony_ci dev_err(clk->dev, "invalid parent rate=%ld\n", *prate); 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 0; pll_cfg[i].rate != 0; i++) 12462306a36Sopenharmony_ci if (pll_cfg[i].rate == rate) 12562306a36Sopenharmony_ci return rate; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return -EINVAL; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate, 13162306a36Sopenharmony_ci unsigned long parent_rate) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 13462306a36Sopenharmony_ci const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(parent_rate); 13562306a36Sopenharmony_ci int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!pll_cfg) { 13862306a36Sopenharmony_ci dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate); 13962306a36Sopenharmony_ci return -EINVAL; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci for (i = 0; pll_cfg[i].rate != 0; i++) { 14362306a36Sopenharmony_ci if (pll_cfg[i].rate == rate) { 14462306a36Sopenharmony_ci i2s_pll_write(clk, PLL_IDIV_REG, pll_cfg[i].idiv); 14562306a36Sopenharmony_ci i2s_pll_write(clk, PLL_FBDIV_REG, pll_cfg[i].fbdiv); 14662306a36Sopenharmony_ci i2s_pll_write(clk, PLL_ODIV0_REG, pll_cfg[i].odiv0); 14762306a36Sopenharmony_ci i2s_pll_write(clk, PLL_ODIV1_REG, pll_cfg[i].odiv1); 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, 15362306a36Sopenharmony_ci parent_rate); 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct clk_ops i2s_pll_ops = { 15862306a36Sopenharmony_ci .recalc_rate = i2s_pll_recalc_rate, 15962306a36Sopenharmony_ci .round_rate = i2s_pll_round_rate, 16062306a36Sopenharmony_ci .set_rate = i2s_pll_set_rate, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int i2s_pll_clk_probe(struct platform_device *pdev) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 16662306a36Sopenharmony_ci struct device_node *node = dev->of_node; 16762306a36Sopenharmony_ci const char *clk_name; 16862306a36Sopenharmony_ci const char *parent_name; 16962306a36Sopenharmony_ci struct clk *clk; 17062306a36Sopenharmony_ci struct i2s_pll_clk *pll_clk; 17162306a36Sopenharmony_ci struct clk_init_data init; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); 17462306a36Sopenharmony_ci if (!pll_clk) 17562306a36Sopenharmony_ci return -ENOMEM; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci pll_clk->base = devm_platform_ioremap_resource(pdev, 0); 17862306a36Sopenharmony_ci if (IS_ERR(pll_clk->base)) 17962306a36Sopenharmony_ci return PTR_ERR(pll_clk->base); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 18262306a36Sopenharmony_ci clk_name = node->name; 18362306a36Sopenharmony_ci init.name = clk_name; 18462306a36Sopenharmony_ci init.ops = &i2s_pll_ops; 18562306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(node, 0); 18662306a36Sopenharmony_ci init.parent_names = &parent_name; 18762306a36Sopenharmony_ci init.num_parents = 1; 18862306a36Sopenharmony_ci pll_clk->hw.init = &init; 18962306a36Sopenharmony_ci pll_clk->dev = dev; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci clk = devm_clk_register(dev, &pll_clk->hw); 19262306a36Sopenharmony_ci if (IS_ERR(clk)) { 19362306a36Sopenharmony_ci dev_err(dev, "failed to register %s clock (%ld)\n", 19462306a36Sopenharmony_ci clk_name, PTR_ERR(clk)); 19562306a36Sopenharmony_ci return PTR_ERR(clk); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return of_clk_add_provider(node, of_clk_src_simple_get, clk); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void i2s_pll_clk_remove(struct platform_device *pdev) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci of_clk_del_provider(pdev->dev.of_node); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct of_device_id i2s_pll_clk_id[] = { 20762306a36Sopenharmony_ci { .compatible = "snps,axs10x-i2s-pll-clock", }, 20862306a36Sopenharmony_ci { }, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2s_pll_clk_id); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic struct platform_driver i2s_pll_clk_driver = { 21362306a36Sopenharmony_ci .driver = { 21462306a36Sopenharmony_ci .name = "axs10x-i2s-pll-clock", 21562306a36Sopenharmony_ci .of_match_table = i2s_pll_clk_id, 21662306a36Sopenharmony_ci }, 21762306a36Sopenharmony_ci .probe = i2s_pll_clk_probe, 21862306a36Sopenharmony_ci .remove_new = i2s_pll_clk_remove, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_cimodule_platform_driver(i2s_pll_clk_driver); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciMODULE_AUTHOR("Jose Abreu <joabreu@synopsys.com>"); 22362306a36Sopenharmony_ciMODULE_DESCRIPTION("Synopsys AXS10X SDP I2S PLL Clock Driver"); 22462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 225