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 27 struct 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 35 static 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 48 static 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 61 struct i2s_pll_clk { 62 void __iomem *base; 63 struct clk_hw hw; 64 struct device *dev; 65 }; 66 67 static 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 73 static 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 79 static 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 84 static inline unsigned int i2s_pll_get_value(unsigned int val) 85 { 86 return (val & 0x3F) + ((val >> 6) & 0x3F); 87 } 88 89 static 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 101 static 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 114 static 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 133 static 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 160 static 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 166 static 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 struct resource *mem; 176 177 pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); 178 if (!pll_clk) 179 return -ENOMEM; 180 181 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 182 pll_clk->base = devm_ioremap_resource(dev, mem); 183 if (IS_ERR(pll_clk->base)) 184 return PTR_ERR(pll_clk->base); 185 186 memset(&init, 0, sizeof(init)); 187 clk_name = node->name; 188 init.name = clk_name; 189 init.ops = &i2s_pll_ops; 190 parent_name = of_clk_get_parent_name(node, 0); 191 init.parent_names = &parent_name; 192 init.num_parents = 1; 193 pll_clk->hw.init = &init; 194 pll_clk->dev = dev; 195 196 clk = devm_clk_register(dev, &pll_clk->hw); 197 if (IS_ERR(clk)) { 198 dev_err(dev, "failed to register %s clock (%ld)\n", 199 clk_name, PTR_ERR(clk)); 200 return PTR_ERR(clk); 201 } 202 203 return of_clk_add_provider(node, of_clk_src_simple_get, clk); 204 } 205 206 static int i2s_pll_clk_remove(struct platform_device *pdev) 207 { 208 of_clk_del_provider(pdev->dev.of_node); 209 return 0; 210 } 211 212 static const struct of_device_id i2s_pll_clk_id[] = { 213 { .compatible = "snps,axs10x-i2s-pll-clock", }, 214 { }, 215 }; 216 MODULE_DEVICE_TABLE(of, i2s_pll_clk_id); 217 218 static struct platform_driver i2s_pll_clk_driver = { 219 .driver = { 220 .name = "axs10x-i2s-pll-clock", 221 .of_match_table = i2s_pll_clk_id, 222 }, 223 .probe = i2s_pll_clk_probe, 224 .remove = i2s_pll_clk_remove, 225 }; 226 module_platform_driver(i2s_pll_clk_driver); 227 228 MODULE_AUTHOR("Jose Abreu <joabreu@synopsys.com>"); 229 MODULE_DESCRIPTION("Synopsys AXS10X SDP I2S PLL Clock Driver"); 230 MODULE_LICENSE("GPL v2"); 231