274 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// clk-s2mps11.c - Clock driver for S2MPS11.4//5// Copyright (C) 2013,2014 Samsung Electornics6 7#include <linux/module.h>8#include <linux/err.h>9#include <linux/of.h>10#include <linux/clkdev.h>11#include <linux/regmap.h>12#include <linux/clk-provider.h>13#include <linux/platform_device.h>14#include <linux/mfd/samsung/s2mps11.h>15#include <linux/mfd/samsung/s2mps13.h>16#include <linux/mfd/samsung/s2mps14.h>17#include <linux/mfd/samsung/s5m8767.h>18#include <linux/mfd/samsung/core.h>19 20#include <dt-bindings/clock/samsung,s2mps11.h>21 22struct s2mps11_clk {23 struct sec_pmic_dev *iodev;24 struct device_node *clk_np;25 struct clk_hw hw;26 struct clk *clk;27 struct clk_lookup *lookup;28 u32 mask;29 unsigned int reg;30};31 32static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)33{34 return container_of(hw, struct s2mps11_clk, hw);35}36 37static int s2mps11_clk_prepare(struct clk_hw *hw)38{39 struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);40 41 return regmap_update_bits(s2mps11->iodev->regmap_pmic,42 s2mps11->reg,43 s2mps11->mask, s2mps11->mask);44}45 46static void s2mps11_clk_unprepare(struct clk_hw *hw)47{48 struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);49 50 regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,51 s2mps11->mask, ~s2mps11->mask);52}53 54static int s2mps11_clk_is_prepared(struct clk_hw *hw)55{56 int ret;57 u32 val;58 struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);59 60 ret = regmap_read(s2mps11->iodev->regmap_pmic,61 s2mps11->reg, &val);62 if (ret < 0)63 return -EINVAL;64 65 return val & s2mps11->mask;66}67 68static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,69 unsigned long parent_rate)70{71 return 32768;72}73 74static const struct clk_ops s2mps11_clk_ops = {75 .prepare = s2mps11_clk_prepare,76 .unprepare = s2mps11_clk_unprepare,77 .is_prepared = s2mps11_clk_is_prepared,78 .recalc_rate = s2mps11_clk_recalc_rate,79};80 81/* This s2mps11_clks_init tructure is common to s2mps11, s2mps13 and s2mps14 */82static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {83 [S2MPS11_CLK_AP] = {84 .name = "s2mps11_ap",85 .ops = &s2mps11_clk_ops,86 },87 [S2MPS11_CLK_CP] = {88 .name = "s2mps11_cp",89 .ops = &s2mps11_clk_ops,90 },91 [S2MPS11_CLK_BT] = {92 .name = "s2mps11_bt",93 .ops = &s2mps11_clk_ops,94 },95};96 97static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,98 struct clk_init_data *clks_init)99{100 struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);101 struct device_node *clk_np;102 int i;103 104 if (!iodev->dev->of_node)105 return ERR_PTR(-EINVAL);106 107 clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");108 if (!clk_np) {109 dev_err(&pdev->dev, "could not find clock sub-node\n");110 return ERR_PTR(-EINVAL);111 }112 113 for (i = 0; i < S2MPS11_CLKS_NUM; i++)114 of_property_read_string_index(clk_np, "clock-output-names", i,115 &clks_init[i].name);116 117 return clk_np;118}119 120static int s2mps11_clk_probe(struct platform_device *pdev)121{122 struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);123 struct s2mps11_clk *s2mps11_clks;124 struct clk_hw_onecell_data *clk_data;125 unsigned int s2mps11_reg;126 int i, ret = 0;127 enum sec_device_type hwid = platform_get_device_id(pdev)->driver_data;128 129 s2mps11_clks = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM,130 sizeof(*s2mps11_clks), GFP_KERNEL);131 if (!s2mps11_clks)132 return -ENOMEM;133 134 clk_data = devm_kzalloc(&pdev->dev,135 struct_size(clk_data, hws, S2MPS11_CLKS_NUM),136 GFP_KERNEL);137 if (!clk_data)138 return -ENOMEM;139 140 switch (hwid) {141 case S2MPS11X:142 s2mps11_reg = S2MPS11_REG_RTC_CTRL;143 break;144 case S2MPS13X:145 s2mps11_reg = S2MPS13_REG_RTCCTRL;146 break;147 case S2MPS14X:148 s2mps11_reg = S2MPS14_REG_RTCCTRL;149 break;150 case S5M8767X:151 s2mps11_reg = S5M8767_REG_CTRL1;152 break;153 default:154 dev_err(&pdev->dev, "Invalid device type\n");155 return -EINVAL;156 }157 158 /* Store clocks of_node in first element of s2mps11_clks array */159 s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, s2mps11_clks_init);160 if (IS_ERR(s2mps11_clks->clk_np))161 return PTR_ERR(s2mps11_clks->clk_np);162 163 for (i = 0; i < S2MPS11_CLKS_NUM; i++) {164 if (i == S2MPS11_CLK_CP && hwid == S2MPS14X)165 continue; /* Skip clocks not present in some devices */166 s2mps11_clks[i].iodev = iodev;167 s2mps11_clks[i].hw.init = &s2mps11_clks_init[i];168 s2mps11_clks[i].mask = 1 << i;169 s2mps11_clks[i].reg = s2mps11_reg;170 171 s2mps11_clks[i].clk = devm_clk_register(&pdev->dev,172 &s2mps11_clks[i].hw);173 if (IS_ERR(s2mps11_clks[i].clk)) {174 dev_err(&pdev->dev, "Fail to register : %s\n",175 s2mps11_clks_init[i].name);176 ret = PTR_ERR(s2mps11_clks[i].clk);177 goto err_reg;178 }179 180 s2mps11_clks[i].lookup = clkdev_hw_create(&s2mps11_clks[i].hw,181 s2mps11_clks_init[i].name, NULL);182 if (!s2mps11_clks[i].lookup) {183 ret = -ENOMEM;184 goto err_reg;185 }186 clk_data->hws[i] = &s2mps11_clks[i].hw;187 }188 189 clk_data->num = S2MPS11_CLKS_NUM;190 of_clk_add_hw_provider(s2mps11_clks->clk_np, of_clk_hw_onecell_get,191 clk_data);192 193 platform_set_drvdata(pdev, s2mps11_clks);194 195 return ret;196 197err_reg:198 of_node_put(s2mps11_clks[0].clk_np);199 while (--i >= 0)200 clkdev_drop(s2mps11_clks[i].lookup);201 202 return ret;203}204 205static void s2mps11_clk_remove(struct platform_device *pdev)206{207 struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);208 int i;209 210 of_clk_del_provider(s2mps11_clks[0].clk_np);211 /* Drop the reference obtained in s2mps11_clk_parse_dt */212 of_node_put(s2mps11_clks[0].clk_np);213 214 for (i = 0; i < S2MPS11_CLKS_NUM; i++) {215 /* Skip clocks not present on S2MPS14 */216 if (!s2mps11_clks[i].lookup)217 continue;218 clkdev_drop(s2mps11_clks[i].lookup);219 }220}221 222static const struct platform_device_id s2mps11_clk_id[] = {223 { "s2mps11-clk", S2MPS11X},224 { "s2mps13-clk", S2MPS13X},225 { "s2mps14-clk", S2MPS14X},226 { "s5m8767-clk", S5M8767X},227 { },228};229MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);230 231#ifdef CONFIG_OF232/*233 * Device is instantiated through parent MFD device and device matching is done234 * through platform_device_id.235 *236 * However if device's DT node contains proper clock compatible and driver is237 * built as a module, then the *module* matching will be done trough DT aliases.238 * This requires of_device_id table. In the same time this will not change the239 * actual *device* matching so do not add .of_match_table.240 */241static const struct of_device_id s2mps11_dt_match[] __used = {242 {243 .compatible = "samsung,s2mps11-clk",244 .data = (void *)S2MPS11X,245 }, {246 .compatible = "samsung,s2mps13-clk",247 .data = (void *)S2MPS13X,248 }, {249 .compatible = "samsung,s2mps14-clk",250 .data = (void *)S2MPS14X,251 }, {252 .compatible = "samsung,s5m8767-clk",253 .data = (void *)S5M8767X,254 }, {255 /* Sentinel */256 },257};258MODULE_DEVICE_TABLE(of, s2mps11_dt_match);259#endif260 261static struct platform_driver s2mps11_clk_driver = {262 .driver = {263 .name = "s2mps11-clk",264 },265 .probe = s2mps11_clk_probe,266 .remove = s2mps11_clk_remove,267 .id_table = s2mps11_clk_id,268};269module_platform_driver(s2mps11_clk_driver);270 271MODULE_DESCRIPTION("S2MPS11 Clock Driver");272MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");273MODULE_LICENSE("GPL");274