1 /* 2 * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs 3 * 4 * Copyright (C) 2016, Intel Corporation 5 * Author: Irina Tirdea <irina.tirdea@intel.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms and conditions of the GNU General Public License, 9 * version 2, as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 */ 16 17 #include <linux/clk-provider.h> 18 #include <linux/clkdev.h> 19 #include <linux/err.h> 20 #include <linux/platform_data/x86/clk-pmc-atom.h> 21 #include <linux/platform_device.h> 22 #include <linux/slab.h> 23 24 #define PLT_CLK_NAME_BASE "pmc_plt_clk" 25 26 #define PMC_CLK_CTL_OFFSET 0x60 27 #define PMC_CLK_CTL_SIZE 4 28 #define PMC_CLK_NUM 6 29 #define PMC_CLK_CTL_GATED_ON_D3 0x0 30 #define PMC_CLK_CTL_FORCE_ON 0x1 31 #define PMC_CLK_CTL_FORCE_OFF 0x2 32 #define PMC_CLK_CTL_RESERVED 0x3 33 #define PMC_MASK_CLK_CTL GENMASK(1, 0) 34 #define PMC_MASK_CLK_FREQ BIT(2) 35 #define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */ 36 #define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */ 37 38 struct clk_plt_fixed { 39 struct clk_hw *clk; 40 struct clk_lookup *lookup; 41 }; 42 43 struct clk_plt { 44 struct clk_hw hw; 45 void __iomem *reg; 46 struct clk_lookup *lookup; 47 /* protect access to PMC registers */ 48 spinlock_t lock; 49 }; 50 51 #define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw) 52 53 struct clk_plt_data { 54 struct clk_plt_fixed **parents; 55 u8 nparents; 56 struct clk_plt *clks[PMC_CLK_NUM]; 57 }; 58 59 /* Return an index in parent table */ 60 static inline int plt_reg_to_parent(int reg) 61 { 62 switch (reg & PMC_MASK_CLK_FREQ) { 63 default: 64 case PMC_CLK_FREQ_XTAL: 65 return 0; 66 case PMC_CLK_FREQ_PLL: 67 return 1; 68 } 69 } 70 71 /* Return clk index of parent */ 72 static inline int plt_parent_to_reg(int index) 73 { 74 switch (index) { 75 default: 76 case 0: 77 return PMC_CLK_FREQ_XTAL; 78 case 1: 79 return PMC_CLK_FREQ_PLL; 80 } 81 } 82 83 /* Abstract status in simpler enabled/disabled value */ 84 static inline int plt_reg_to_enabled(int reg) 85 { 86 switch (reg & PMC_MASK_CLK_CTL) { 87 case PMC_CLK_CTL_GATED_ON_D3: 88 case PMC_CLK_CTL_FORCE_ON: 89 return 1; /* enabled */ 90 case PMC_CLK_CTL_FORCE_OFF: 91 case PMC_CLK_CTL_RESERVED: 92 default: 93 return 0; /* disabled */ 94 } 95 } 96 97 static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val) 98 { 99 u32 tmp; 100 unsigned long flags; 101 102 spin_lock_irqsave(&clk->lock, flags); 103 104 tmp = readl(clk->reg); 105 tmp = (tmp & ~mask) | (val & mask); 106 writel(tmp, clk->reg); 107 108 spin_unlock_irqrestore(&clk->lock, flags); 109 } 110 111 static int plt_clk_set_parent(struct clk_hw *hw, u8 index) 112 { 113 struct clk_plt *clk = to_clk_plt(hw); 114 115 plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index)); 116 117 return 0; 118 } 119 120 static u8 plt_clk_get_parent(struct clk_hw *hw) 121 { 122 struct clk_plt *clk = to_clk_plt(hw); 123 u32 value; 124 125 value = readl(clk->reg); 126 127 return plt_reg_to_parent(value); 128 } 129 130 static int plt_clk_enable(struct clk_hw *hw) 131 { 132 struct clk_plt *clk = to_clk_plt(hw); 133 134 plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON); 135 136 return 0; 137 } 138 139 static void plt_clk_disable(struct clk_hw *hw) 140 { 141 struct clk_plt *clk = to_clk_plt(hw); 142 143 plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF); 144 } 145 146 static int plt_clk_is_enabled(struct clk_hw *hw) 147 { 148 struct clk_plt *clk = to_clk_plt(hw); 149 u32 value; 150 151 value = readl(clk->reg); 152 153 return plt_reg_to_enabled(value); 154 } 155 156 static const struct clk_ops plt_clk_ops = { 157 .enable = plt_clk_enable, 158 .disable = plt_clk_disable, 159 .is_enabled = plt_clk_is_enabled, 160 .get_parent = plt_clk_get_parent, 161 .set_parent = plt_clk_set_parent, 162 .determine_rate = __clk_mux_determine_rate, 163 }; 164 165 static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, 166 void __iomem *base, 167 const char **parent_names, 168 int num_parents) 169 { 170 struct clk_plt *pclk; 171 struct clk_init_data init; 172 int ret; 173 174 pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); 175 if (!pclk) 176 return ERR_PTR(-ENOMEM); 177 178 init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id); 179 init.ops = &plt_clk_ops; 180 init.flags = 0; 181 init.parent_names = parent_names; 182 init.num_parents = num_parents; 183 184 pclk->hw.init = &init; 185 pclk->reg = base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE; 186 spin_lock_init(&pclk->lock); 187 188 ret = devm_clk_hw_register(&pdev->dev, &pclk->hw); 189 if (ret) { 190 pclk = ERR_PTR(ret); 191 goto err_free_init; 192 } 193 194 pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL); 195 if (!pclk->lookup) { 196 pclk = ERR_PTR(-ENOMEM); 197 goto err_free_init; 198 } 199 200 err_free_init: 201 kfree(init.name); 202 return pclk; 203 } 204 205 static void plt_clk_unregister(struct clk_plt *pclk) 206 { 207 clkdev_drop(pclk->lookup); 208 } 209 210 static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev, 211 const char *name, 212 const char *parent_name, 213 unsigned long fixed_rate) 214 { 215 struct clk_plt_fixed *pclk; 216 217 pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); 218 if (!pclk) 219 return ERR_PTR(-ENOMEM); 220 221 pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name, 222 0, fixed_rate); 223 if (IS_ERR(pclk->clk)) 224 return ERR_CAST(pclk->clk); 225 226 pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL); 227 if (!pclk->lookup) { 228 clk_hw_unregister_fixed_rate(pclk->clk); 229 return ERR_PTR(-ENOMEM); 230 } 231 232 return pclk; 233 } 234 235 static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk) 236 { 237 clkdev_drop(pclk->lookup); 238 clk_hw_unregister_fixed_rate(pclk->clk); 239 } 240 241 static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data, 242 unsigned int i) 243 { 244 while (i--) 245 plt_clk_unregister_fixed_rate(data->parents[i]); 246 } 247 248 static void plt_clk_free_parent_names_loop(const char **parent_names, 249 unsigned int i) 250 { 251 while (i--) 252 kfree_const(parent_names[i]); 253 kfree(parent_names); 254 } 255 256 static void plt_clk_unregister_loop(struct clk_plt_data *data, 257 unsigned int i) 258 { 259 while (i--) 260 plt_clk_unregister(data->clks[i]); 261 } 262 263 static const char **plt_clk_register_parents(struct platform_device *pdev, 264 struct clk_plt_data *data, 265 const struct pmc_clk *clks) 266 { 267 const char **parent_names; 268 unsigned int i; 269 int err; 270 int nparents = 0; 271 272 data->nparents = 0; 273 while (clks[nparents].name) 274 nparents++; 275 276 data->parents = devm_kcalloc(&pdev->dev, nparents, 277 sizeof(*data->parents), GFP_KERNEL); 278 if (!data->parents) 279 return ERR_PTR(-ENOMEM); 280 281 parent_names = kcalloc(nparents, sizeof(*parent_names), 282 GFP_KERNEL); 283 if (!parent_names) 284 return ERR_PTR(-ENOMEM); 285 286 for (i = 0; i < nparents; i++) { 287 data->parents[i] = 288 plt_clk_register_fixed_rate(pdev, clks[i].name, 289 clks[i].parent_name, 290 clks[i].freq); 291 if (IS_ERR(data->parents[i])) { 292 err = PTR_ERR(data->parents[i]); 293 goto err_unreg; 294 } 295 parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL); 296 } 297 298 data->nparents = nparents; 299 return parent_names; 300 301 err_unreg: 302 plt_clk_unregister_fixed_rate_loop(data, i); 303 plt_clk_free_parent_names_loop(parent_names, i); 304 return ERR_PTR(err); 305 } 306 307 static void plt_clk_unregister_parents(struct clk_plt_data *data) 308 { 309 plt_clk_unregister_fixed_rate_loop(data, data->nparents); 310 } 311 312 static int plt_clk_probe(struct platform_device *pdev) 313 { 314 const struct pmc_clk_data *pmc_data; 315 const char **parent_names; 316 struct clk_plt_data *data; 317 unsigned int i; 318 int err; 319 320 pmc_data = dev_get_platdata(&pdev->dev); 321 if (!pmc_data || !pmc_data->clks) 322 return -EINVAL; 323 324 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 325 if (!data) 326 return -ENOMEM; 327 328 parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks); 329 if (IS_ERR(parent_names)) 330 return PTR_ERR(parent_names); 331 332 for (i = 0; i < PMC_CLK_NUM; i++) { 333 data->clks[i] = plt_clk_register(pdev, i, pmc_data->base, 334 parent_names, data->nparents); 335 if (IS_ERR(data->clks[i])) { 336 err = PTR_ERR(data->clks[i]); 337 goto err_unreg_clk_plt; 338 } 339 } 340 341 plt_clk_free_parent_names_loop(parent_names, data->nparents); 342 343 platform_set_drvdata(pdev, data); 344 return 0; 345 346 err_unreg_clk_plt: 347 plt_clk_unregister_loop(data, i); 348 plt_clk_unregister_parents(data); 349 plt_clk_free_parent_names_loop(parent_names, data->nparents); 350 return err; 351 } 352 353 static int plt_clk_remove(struct platform_device *pdev) 354 { 355 struct clk_plt_data *data; 356 357 data = platform_get_drvdata(pdev); 358 359 plt_clk_unregister_loop(data, PMC_CLK_NUM); 360 plt_clk_unregister_parents(data); 361 return 0; 362 } 363 364 static struct platform_driver plt_clk_driver = { 365 .driver = { 366 .name = "clk-pmc-atom", 367 }, 368 .probe = plt_clk_probe, 369 .remove = plt_clk_remove, 370 }; 371 builtin_platform_driver(plt_clk_driver); 372