i2s_pll_clock.c (5833B)
1/* 2 * Synopsys AXS10X SDP I2S PLL clock driver 3 * 4 * Copyright (C) 2016 Synopsys 5 * 6 * This file is licensed under the terms of the GNU General Public 7 * License version 2. This program is licensed "as is" without any 8 * warranty of any kind, whether express or implied. 9 */ 10 11#include <linux/platform_device.h> 12#include <linux/module.h> 13#include <linux/clk-provider.h> 14#include <linux/err.h> 15#include <linux/device.h> 16#include <linux/io.h> 17#include <linux/of_address.h> 18#include <linux/slab.h> 19#include <linux/of.h> 20 21/* PLL registers addresses */ 22#define PLL_IDIV_REG 0x0 23#define PLL_FBDIV_REG 0x4 24#define PLL_ODIV0_REG 0x8 25#define PLL_ODIV1_REG 0xC 26 27struct i2s_pll_cfg { 28 unsigned int rate; 29 unsigned int idiv; 30 unsigned int fbdiv; 31 unsigned int odiv0; 32 unsigned int odiv1; 33}; 34 35static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = { 36 /* 27 Mhz */ 37 { 1024000, 0x104, 0x451, 0x10E38, 0x2000 }, 38 { 1411200, 0x104, 0x596, 0x10D35, 0x2000 }, 39 { 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 }, 40 { 2048000, 0x82, 0x451, 0x10E38, 0x2000 }, 41 { 2822400, 0x82, 0x596, 0x10D35, 0x2000 }, 42 { 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 }, 43 { 2116800, 0x82, 0x3CF, 0x10C30, 0x2000 }, 44 { 2304000, 0x104, 0x79E, 0x10B2C, 0x2000 }, 45 { 0, 0, 0, 0, 0 }, 46}; 47 48static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = { 49 /* 28.224 Mhz */ 50 { 1024000, 0x82, 0x105, 0x107DF, 0x2000 }, 51 { 1411200, 0x28A, 0x1, 0x10001, 0x2000 }, 52 { 1536000, 0xA28, 0x187, 0x10042, 0x2000 }, 53 { 2048000, 0x41, 0x105, 0x107DF, 0x2000 }, 54 { 2822400, 0x145, 0x1, 0x10001, 0x2000 }, 55 { 3072000, 0x514, 0x187, 0x10042, 0x2000 }, 56 { 2116800, 0x514, 0x42, 0x10001, 0x2000 }, 57 { 2304000, 0x619, 0x82, 0x10001, 0x2000 }, 58 { 0, 0, 0, 0, 0 }, 59}; 60 61struct i2s_pll_clk { 62 void __iomem *base; 63 struct clk_hw hw; 64 struct device *dev; 65}; 66 67static inline void i2s_pll_write(struct i2s_pll_clk *clk, unsigned int reg, 68 unsigned int val) 69{ 70 writel_relaxed(val, clk->base + reg); 71} 72 73static inline unsigned int i2s_pll_read(struct i2s_pll_clk *clk, 74 unsigned int reg) 75{ 76 return readl_relaxed(clk->base + reg); 77} 78 79static inline struct i2s_pll_clk *to_i2s_pll_clk(struct clk_hw *hw) 80{ 81 return container_of(hw, struct i2s_pll_clk, hw); 82} 83 84static inline unsigned int i2s_pll_get_value(unsigned int val) 85{ 86 return (val & 0x3F) + ((val >> 6) & 0x3F); 87} 88 89static const struct i2s_pll_cfg *i2s_pll_get_cfg(unsigned long prate) 90{ 91 switch (prate) { 92 case 27000000: 93 return i2s_pll_cfg_27m; 94 case 28224000: 95 return i2s_pll_cfg_28m; 96 default: 97 return NULL; 98 } 99} 100 101static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw, 102 unsigned long parent_rate) 103{ 104 struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 105 unsigned int idiv, fbdiv, odiv; 106 107 idiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_IDIV_REG)); 108 fbdiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_FBDIV_REG)); 109 odiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_ODIV0_REG)); 110 111 return ((parent_rate / idiv) * fbdiv) / odiv; 112} 113 114static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate, 115 unsigned long *prate) 116{ 117 struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 118 const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(*prate); 119 int i; 120 121 if (!pll_cfg) { 122 dev_err(clk->dev, "invalid parent rate=%ld\n", *prate); 123 return -EINVAL; 124 } 125 126 for (i = 0; pll_cfg[i].rate != 0; i++) 127 if (pll_cfg[i].rate == rate) 128 return rate; 129 130 return -EINVAL; 131} 132 133static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate, 134 unsigned long parent_rate) 135{ 136 struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); 137 const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(parent_rate); 138 int i; 139 140 if (!pll_cfg) { 141 dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate); 142 return -EINVAL; 143 } 144 145 for (i = 0; pll_cfg[i].rate != 0; i++) { 146 if (pll_cfg[i].rate == rate) { 147 i2s_pll_write(clk, PLL_IDIV_REG, pll_cfg[i].idiv); 148 i2s_pll_write(clk, PLL_FBDIV_REG, pll_cfg[i].fbdiv); 149 i2s_pll_write(clk, PLL_ODIV0_REG, pll_cfg[i].odiv0); 150 i2s_pll_write(clk, PLL_ODIV1_REG, pll_cfg[i].odiv1); 151 return 0; 152 } 153 } 154 155 dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, 156 parent_rate); 157 return -EINVAL; 158} 159 160static const struct clk_ops i2s_pll_ops = { 161 .recalc_rate = i2s_pll_recalc_rate, 162 .round_rate = i2s_pll_round_rate, 163 .set_rate = i2s_pll_set_rate, 164}; 165 166static int i2s_pll_clk_probe(struct platform_device *pdev) 167{ 168 struct device *dev = &pdev->dev; 169 struct device_node *node = dev->of_node; 170 const char *clk_name; 171 const char *parent_name; 172 struct clk *clk; 173 struct i2s_pll_clk *pll_clk; 174 struct clk_init_data init; 175 176 pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); 177 if (!pll_clk) 178 return -ENOMEM; 179 180 pll_clk->base = devm_platform_ioremap_resource(pdev, 0); 181 if (IS_ERR(pll_clk->base)) 182 return PTR_ERR(pll_clk->base); 183 184 memset(&init, 0, sizeof(init)); 185 clk_name = node->name; 186 init.name = clk_name; 187 init.ops = &i2s_pll_ops; 188 parent_name = of_clk_get_parent_name(node, 0); 189 init.parent_names = &parent_name; 190 init.num_parents = 1; 191 pll_clk->hw.init = &init; 192 pll_clk->dev = dev; 193 194 clk = devm_clk_register(dev, &pll_clk->hw); 195 if (IS_ERR(clk)) { 196 dev_err(dev, "failed to register %s clock (%ld)\n", 197 clk_name, PTR_ERR(clk)); 198 return PTR_ERR(clk); 199 } 200 201 return of_clk_add_provider(node, of_clk_src_simple_get, clk); 202} 203 204static int i2s_pll_clk_remove(struct platform_device *pdev) 205{ 206 of_clk_del_provider(pdev->dev.of_node); 207 return 0; 208} 209 210static const struct of_device_id i2s_pll_clk_id[] = { 211 { .compatible = "snps,axs10x-i2s-pll-clock", }, 212 { }, 213}; 214MODULE_DEVICE_TABLE(of, i2s_pll_clk_id); 215 216static struct platform_driver i2s_pll_clk_driver = { 217 .driver = { 218 .name = "axs10x-i2s-pll-clock", 219 .of_match_table = i2s_pll_clk_id, 220 }, 221 .probe = i2s_pll_clk_probe, 222 .remove = i2s_pll_clk_remove, 223}; 224module_platform_driver(i2s_pll_clk_driver); 225 226MODULE_AUTHOR("Jose Abreu <joabreu@synopsys.com>"); 227MODULE_DESCRIPTION("Synopsys AXS10X SDP I2S PLL Clock Driver"); 228MODULE_LICENSE("GPL v2");