0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/clk-provider.h>
0010 #include <linux/clkdev.h>
0011 #include <linux/err.h>
0012 #include <linux/io.h>
0013 #include <linux/platform_data/x86/clk-pmc-atom.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/slab.h>
0016
0017 #define PLT_CLK_NAME_BASE "pmc_plt_clk"
0018
0019 #define PMC_CLK_CTL_OFFSET 0x60
0020 #define PMC_CLK_CTL_SIZE 4
0021 #define PMC_CLK_NUM 6
0022 #define PMC_CLK_CTL_GATED_ON_D3 0x0
0023 #define PMC_CLK_CTL_FORCE_ON 0x1
0024 #define PMC_CLK_CTL_FORCE_OFF 0x2
0025 #define PMC_CLK_CTL_RESERVED 0x3
0026 #define PMC_MASK_CLK_CTL GENMASK(1, 0)
0027 #define PMC_MASK_CLK_FREQ BIT(2)
0028 #define PMC_CLK_FREQ_XTAL (0 << 2)
0029 #define PMC_CLK_FREQ_PLL (1 << 2)
0030
0031 struct clk_plt_fixed {
0032 struct clk_hw *clk;
0033 struct clk_lookup *lookup;
0034 };
0035
0036 struct clk_plt {
0037 struct clk_hw hw;
0038 void __iomem *reg;
0039 struct clk_lookup *lookup;
0040
0041 spinlock_t lock;
0042 };
0043
0044 #define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
0045
0046 struct clk_plt_data {
0047 struct clk_plt_fixed **parents;
0048 u8 nparents;
0049 struct clk_plt *clks[PMC_CLK_NUM];
0050 struct clk_lookup *mclk_lookup;
0051 struct clk_lookup *ether_clk_lookup;
0052 };
0053
0054
0055 static inline int plt_reg_to_parent(int reg)
0056 {
0057 switch (reg & PMC_MASK_CLK_FREQ) {
0058 default:
0059 case PMC_CLK_FREQ_XTAL:
0060 return 0;
0061 case PMC_CLK_FREQ_PLL:
0062 return 1;
0063 }
0064 }
0065
0066
0067 static inline int plt_parent_to_reg(int index)
0068 {
0069 switch (index) {
0070 default:
0071 case 0:
0072 return PMC_CLK_FREQ_XTAL;
0073 case 1:
0074 return PMC_CLK_FREQ_PLL;
0075 }
0076 }
0077
0078
0079 static inline int plt_reg_to_enabled(int reg)
0080 {
0081 switch (reg & PMC_MASK_CLK_CTL) {
0082 case PMC_CLK_CTL_GATED_ON_D3:
0083 case PMC_CLK_CTL_FORCE_ON:
0084 return 1;
0085 case PMC_CLK_CTL_FORCE_OFF:
0086 case PMC_CLK_CTL_RESERVED:
0087 default:
0088 return 0;
0089 }
0090 }
0091
0092 static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val)
0093 {
0094 u32 tmp;
0095 unsigned long flags;
0096
0097 spin_lock_irqsave(&clk->lock, flags);
0098
0099 tmp = readl(clk->reg);
0100 tmp = (tmp & ~mask) | (val & mask);
0101 writel(tmp, clk->reg);
0102
0103 spin_unlock_irqrestore(&clk->lock, flags);
0104 }
0105
0106 static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
0107 {
0108 struct clk_plt *clk = to_clk_plt(hw);
0109
0110 plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index));
0111
0112 return 0;
0113 }
0114
0115 static u8 plt_clk_get_parent(struct clk_hw *hw)
0116 {
0117 struct clk_plt *clk = to_clk_plt(hw);
0118 u32 value;
0119
0120 value = readl(clk->reg);
0121
0122 return plt_reg_to_parent(value);
0123 }
0124
0125 static int plt_clk_enable(struct clk_hw *hw)
0126 {
0127 struct clk_plt *clk = to_clk_plt(hw);
0128
0129 plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
0130
0131 return 0;
0132 }
0133
0134 static void plt_clk_disable(struct clk_hw *hw)
0135 {
0136 struct clk_plt *clk = to_clk_plt(hw);
0137
0138 plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
0139 }
0140
0141 static int plt_clk_is_enabled(struct clk_hw *hw)
0142 {
0143 struct clk_plt *clk = to_clk_plt(hw);
0144 u32 value;
0145
0146 value = readl(clk->reg);
0147
0148 return plt_reg_to_enabled(value);
0149 }
0150
0151 static const struct clk_ops plt_clk_ops = {
0152 .enable = plt_clk_enable,
0153 .disable = plt_clk_disable,
0154 .is_enabled = plt_clk_is_enabled,
0155 .get_parent = plt_clk_get_parent,
0156 .set_parent = plt_clk_set_parent,
0157 .determine_rate = __clk_mux_determine_rate,
0158 };
0159
0160 static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
0161 const struct pmc_clk_data *pmc_data,
0162 const char **parent_names,
0163 int num_parents)
0164 {
0165 struct clk_plt *pclk;
0166 struct clk_init_data init;
0167 int ret;
0168
0169 pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
0170 if (!pclk)
0171 return ERR_PTR(-ENOMEM);
0172
0173 init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
0174 init.ops = &plt_clk_ops;
0175 init.flags = 0;
0176 init.parent_names = parent_names;
0177 init.num_parents = num_parents;
0178
0179 pclk->hw.init = &init;
0180 pclk->reg = pmc_data->base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
0181 spin_lock_init(&pclk->lock);
0182
0183
0184
0185
0186
0187
0188 if (pmc_data->critical && plt_clk_is_enabled(&pclk->hw))
0189 init.flags |= CLK_IS_CRITICAL;
0190
0191 ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
0192 if (ret) {
0193 pclk = ERR_PTR(ret);
0194 goto err_free_init;
0195 }
0196
0197 pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL);
0198 if (!pclk->lookup) {
0199 pclk = ERR_PTR(-ENOMEM);
0200 goto err_free_init;
0201 }
0202
0203 err_free_init:
0204 kfree(init.name);
0205 return pclk;
0206 }
0207
0208 static void plt_clk_unregister(struct clk_plt *pclk)
0209 {
0210 clkdev_drop(pclk->lookup);
0211 }
0212
0213 static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
0214 const char *name,
0215 const char *parent_name,
0216 unsigned long fixed_rate)
0217 {
0218 struct clk_plt_fixed *pclk;
0219
0220 pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
0221 if (!pclk)
0222 return ERR_PTR(-ENOMEM);
0223
0224 pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name,
0225 0, fixed_rate);
0226 if (IS_ERR(pclk->clk))
0227 return ERR_CAST(pclk->clk);
0228
0229 pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL);
0230 if (!pclk->lookup) {
0231 clk_hw_unregister_fixed_rate(pclk->clk);
0232 return ERR_PTR(-ENOMEM);
0233 }
0234
0235 return pclk;
0236 }
0237
0238 static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
0239 {
0240 clkdev_drop(pclk->lookup);
0241 clk_hw_unregister_fixed_rate(pclk->clk);
0242 }
0243
0244 static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data,
0245 unsigned int i)
0246 {
0247 while (i--)
0248 plt_clk_unregister_fixed_rate(data->parents[i]);
0249 }
0250
0251 static void plt_clk_free_parent_names_loop(const char **parent_names,
0252 unsigned int i)
0253 {
0254 while (i--)
0255 kfree_const(parent_names[i]);
0256 kfree(parent_names);
0257 }
0258
0259 static void plt_clk_unregister_loop(struct clk_plt_data *data,
0260 unsigned int i)
0261 {
0262 while (i--)
0263 plt_clk_unregister(data->clks[i]);
0264 }
0265
0266 static const char **plt_clk_register_parents(struct platform_device *pdev,
0267 struct clk_plt_data *data,
0268 const struct pmc_clk *clks)
0269 {
0270 const char **parent_names;
0271 unsigned int i;
0272 int err;
0273 int nparents = 0;
0274
0275 data->nparents = 0;
0276 while (clks[nparents].name)
0277 nparents++;
0278
0279 data->parents = devm_kcalloc(&pdev->dev, nparents,
0280 sizeof(*data->parents), GFP_KERNEL);
0281 if (!data->parents)
0282 return ERR_PTR(-ENOMEM);
0283
0284 parent_names = kcalloc(nparents, sizeof(*parent_names),
0285 GFP_KERNEL);
0286 if (!parent_names)
0287 return ERR_PTR(-ENOMEM);
0288
0289 for (i = 0; i < nparents; i++) {
0290 data->parents[i] =
0291 plt_clk_register_fixed_rate(pdev, clks[i].name,
0292 clks[i].parent_name,
0293 clks[i].freq);
0294 if (IS_ERR(data->parents[i])) {
0295 err = PTR_ERR(data->parents[i]);
0296 goto err_unreg;
0297 }
0298 parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
0299 }
0300
0301 data->nparents = nparents;
0302 return parent_names;
0303
0304 err_unreg:
0305 plt_clk_unregister_fixed_rate_loop(data, i);
0306 plt_clk_free_parent_names_loop(parent_names, i);
0307 return ERR_PTR(err);
0308 }
0309
0310 static void plt_clk_unregister_parents(struct clk_plt_data *data)
0311 {
0312 plt_clk_unregister_fixed_rate_loop(data, data->nparents);
0313 }
0314
0315 static int plt_clk_probe(struct platform_device *pdev)
0316 {
0317 const struct pmc_clk_data *pmc_data;
0318 const char **parent_names;
0319 struct clk_plt_data *data;
0320 unsigned int i;
0321 int err;
0322
0323 pmc_data = dev_get_platdata(&pdev->dev);
0324 if (!pmc_data || !pmc_data->clks)
0325 return -EINVAL;
0326
0327 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
0328 if (!data)
0329 return -ENOMEM;
0330
0331 parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks);
0332 if (IS_ERR(parent_names))
0333 return PTR_ERR(parent_names);
0334
0335 for (i = 0; i < PMC_CLK_NUM; i++) {
0336 data->clks[i] = plt_clk_register(pdev, i, pmc_data,
0337 parent_names, data->nparents);
0338 if (IS_ERR(data->clks[i])) {
0339 err = PTR_ERR(data->clks[i]);
0340 goto err_unreg_clk_plt;
0341 }
0342 }
0343 data->mclk_lookup = clkdev_hw_create(&data->clks[3]->hw, "mclk", NULL);
0344 if (!data->mclk_lookup) {
0345 err = -ENOMEM;
0346 goto err_unreg_clk_plt;
0347 }
0348
0349 data->ether_clk_lookup = clkdev_hw_create(&data->clks[4]->hw,
0350 "ether_clk", NULL);
0351 if (!data->ether_clk_lookup) {
0352 err = -ENOMEM;
0353 goto err_drop_mclk;
0354 }
0355
0356 plt_clk_free_parent_names_loop(parent_names, data->nparents);
0357
0358 platform_set_drvdata(pdev, data);
0359 return 0;
0360
0361 err_drop_mclk:
0362 clkdev_drop(data->mclk_lookup);
0363 err_unreg_clk_plt:
0364 plt_clk_unregister_loop(data, i);
0365 plt_clk_unregister_parents(data);
0366 plt_clk_free_parent_names_loop(parent_names, data->nparents);
0367 return err;
0368 }
0369
0370 static int plt_clk_remove(struct platform_device *pdev)
0371 {
0372 struct clk_plt_data *data;
0373
0374 data = platform_get_drvdata(pdev);
0375
0376 clkdev_drop(data->ether_clk_lookup);
0377 clkdev_drop(data->mclk_lookup);
0378 plt_clk_unregister_loop(data, PMC_CLK_NUM);
0379 plt_clk_unregister_parents(data);
0380 return 0;
0381 }
0382
0383 static struct platform_driver plt_clk_driver = {
0384 .driver = {
0385 .name = "clk-pmc-atom",
0386 },
0387 .probe = plt_clk_probe,
0388 .remove = plt_clk_remove,
0389 };
0390 builtin_platform_driver(plt_clk_driver);