Blame view
drivers/clk/clk-s2mps11.c
6.72 KB
7cc560dea clk: s2mps11: Add... |
1 2 3 |
/* * clk-s2mps11.c - Clock driver for S2MPS11. * |
e8b60a45a clk: s2mps11: Add... |
4 |
* Copyright (C) 2013,2014 Samsung Electornics |
7cc560dea clk: s2mps11: Add... |
5 6 7 8 9 10 11 12 13 14 15 |
* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * |
7cc560dea clk: s2mps11: Add... |
16 17 18 19 20 21 22 23 24 25 |
*/ #include <linux/module.h> #include <linux/err.h> #include <linux/of.h> #include <linux/clkdev.h> #include <linux/regmap.h> #include <linux/clk-provider.h> #include <linux/platform_device.h> #include <linux/mfd/samsung/s2mps11.h> |
f928b53d7 clk: s2mps11: Add... |
26 |
#include <linux/mfd/samsung/s2mps13.h> |
e8b60a45a clk: s2mps11: Add... |
27 |
#include <linux/mfd/samsung/s2mps14.h> |
e8e6b840c clk: clk-s2mps11:... |
28 |
#include <linux/mfd/samsung/s5m8767.h> |
7cc560dea clk: s2mps11: Add... |
29 |
#include <linux/mfd/samsung/core.h> |
7cc560dea clk: s2mps11: Add... |
30 31 32 33 34 35 36 37 38 |
enum { S2MPS11_CLK_AP = 0, S2MPS11_CLK_CP, S2MPS11_CLK_BT, S2MPS11_CLKS_NUM, }; struct s2mps11_clk { struct sec_pmic_dev *iodev; |
bf416bd45 clk: s2mps11: Add... |
39 |
struct device_node *clk_np; |
7cc560dea clk: s2mps11: Add... |
40 41 42 43 |
struct clk_hw hw; struct clk *clk; struct clk_lookup *lookup; u32 mask; |
64d64c357 clk: clk-s2mps11:... |
44 |
unsigned int reg; |
7cc560dea clk: s2mps11: Add... |
45 46 47 48 49 50 51 52 53 54 |
}; static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw) { return container_of(hw, struct s2mps11_clk, hw); } static int s2mps11_clk_prepare(struct clk_hw *hw) { struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); |
7cc560dea clk: s2mps11: Add... |
55 |
|
7764d0cdc clk: s2mps11: Use... |
56 |
return regmap_update_bits(s2mps11->iodev->regmap_pmic, |
64d64c357 clk: clk-s2mps11:... |
57 |
s2mps11->reg, |
7cc560dea clk: s2mps11: Add... |
58 |
s2mps11->mask, s2mps11->mask); |
7cc560dea clk: s2mps11: Add... |
59 60 61 62 63 |
} static void s2mps11_clk_unprepare(struct clk_hw *hw) { struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); |
7cc560dea clk: s2mps11: Add... |
64 |
|
7764d0cdc clk: s2mps11: Use... |
65 |
regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg, |
7cc560dea clk: s2mps11: Add... |
66 |
s2mps11->mask, ~s2mps11->mask); |
7cc560dea clk: s2mps11: Add... |
67 |
} |
89ed7e6e6 clk: s2mps11: Fix... |
68 |
static int s2mps11_clk_is_prepared(struct clk_hw *hw) |
7cc560dea clk: s2mps11: Add... |
69 |
{ |
89ed7e6e6 clk: s2mps11: Fix... |
70 71 |
int ret; u32 val; |
7cc560dea clk: s2mps11: Add... |
72 |
struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); |
89ed7e6e6 clk: s2mps11: Fix... |
73 74 75 76 77 78 |
ret = regmap_read(s2mps11->iodev->regmap_pmic, s2mps11->reg, &val); if (ret < 0) return -EINVAL; return val & s2mps11->mask; |
7cc560dea clk: s2mps11: Add... |
79 80 81 82 83 |
} static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { |
89ed7e6e6 clk: s2mps11: Fix... |
84 |
return 32768; |
7cc560dea clk: s2mps11: Add... |
85 86 87 88 89 |
} static struct clk_ops s2mps11_clk_ops = { .prepare = s2mps11_clk_prepare, .unprepare = s2mps11_clk_unprepare, |
89ed7e6e6 clk: s2mps11: Fix... |
90 |
.is_prepared = s2mps11_clk_is_prepared, |
7cc560dea clk: s2mps11: Add... |
91 92 |
.recalc_rate = s2mps11_clk_recalc_rate, }; |
31ad0e2a9 clk: s2mps11: all... |
93 |
/* This s2mps11_clks_init tructure is common to s2mps11, s2mps13 and s2mps14 */ |
7cc560dea clk: s2mps11: Add... |
94 95 96 97 |
static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = { [S2MPS11_CLK_AP] = { .name = "s2mps11_ap", .ops = &s2mps11_clk_ops, |
7cc560dea clk: s2mps11: Add... |
98 99 100 101 |
}, [S2MPS11_CLK_CP] = { .name = "s2mps11_cp", .ops = &s2mps11_clk_ops, |
7cc560dea clk: s2mps11: Add... |
102 103 104 105 |
}, [S2MPS11_CLK_BT] = { .name = "s2mps11_bt", .ops = &s2mps11_clk_ops, |
7cc560dea clk: s2mps11: Add... |
106 107 |
}, }; |
e8b60a45a clk: s2mps11: Add... |
108 109 |
static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev, struct clk_init_data *clks_init) |
7cc560dea clk: s2mps11: Add... |
110 111 112 113 114 115 |
{ struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct device_node *clk_np; int i; if (!iodev->dev->of_node) |
238e14055 clk: s2mps11: Fix... |
116 |
return ERR_PTR(-EINVAL); |
7cc560dea clk: s2mps11: Add... |
117 |
|
afbd1a0c3 clk: s2mps11: Use... |
118 |
clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks"); |
7cc560dea clk: s2mps11: Add... |
119 120 121 122 123 |
if (!clk_np) { dev_err(&pdev->dev, "could not find clock sub-node "); return ERR_PTR(-EINVAL); } |
31ad0e2a9 clk: s2mps11: all... |
124 |
for (i = 0; i < S2MPS11_CLKS_NUM; i++) |
7cc560dea clk: s2mps11: Add... |
125 |
of_property_read_string_index(clk_np, "clock-output-names", i, |
e8b60a45a clk: s2mps11: Add... |
126 |
&clks_init[i].name); |
7cc560dea clk: s2mps11: Add... |
127 128 129 130 131 132 133 |
return clk_np; } static int s2mps11_clk_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); |
dedbb2724 clk: s2mps11: rem... |
134 |
struct s2mps11_clk *s2mps11_clks; |
b228fad50 clk: s2mps11: Mig... |
135 |
struct clk_hw_onecell_data *clk_data; |
64d64c357 clk: clk-s2mps11:... |
136 |
unsigned int s2mps11_reg; |
7cc560dea clk: s2mps11: Add... |
137 |
int i, ret = 0; |
31ad0e2a9 clk: s2mps11: all... |
138 |
enum sec_device_type hwid = platform_get_device_id(pdev)->driver_data; |
7cc560dea clk: s2mps11: Add... |
139 |
|
7764d0cdc clk: s2mps11: Use... |
140 |
s2mps11_clks = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM, |
dedbb2724 clk: s2mps11: rem... |
141 |
sizeof(*s2mps11_clks), GFP_KERNEL); |
7cc560dea clk: s2mps11: Add... |
142 143 |
if (!s2mps11_clks) return -ENOMEM; |
b228fad50 clk: s2mps11: Mig... |
144 145 146 |
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data) + sizeof(*clk_data->hws) * S2MPS11_CLKS_NUM, GFP_KERNEL); |
36da4def1 clk: s2mps11: rem... |
147 148 |
if (!clk_data) return -ENOMEM; |
31ad0e2a9 clk: s2mps11: all... |
149 |
switch (hwid) { |
64d64c357 clk: clk-s2mps11:... |
150 151 |
case S2MPS11X: s2mps11_reg = S2MPS11_REG_RTC_CTRL; |
e8b60a45a clk: s2mps11: Add... |
152 |
break; |
f928b53d7 clk: s2mps11: Add... |
153 154 |
case S2MPS13X: s2mps11_reg = S2MPS13_REG_RTCCTRL; |
f928b53d7 clk: s2mps11: Add... |
155 |
break; |
e8b60a45a clk: s2mps11: Add... |
156 157 |
case S2MPS14X: s2mps11_reg = S2MPS14_REG_RTCCTRL; |
64d64c357 clk: clk-s2mps11:... |
158 |
break; |
e8e6b840c clk: clk-s2mps11:... |
159 160 161 |
case S5M8767X: s2mps11_reg = S5M8767_REG_CTRL1; break; |
64d64c357 clk: clk-s2mps11:... |
162 163 164 165 |
default: dev_err(&pdev->dev, "Invalid device type "); return -EINVAL; |
250d07d1e clk: clk-s2mps11:... |
166 |
} |
64d64c357 clk: clk-s2mps11:... |
167 |
|
e8b60a45a clk: s2mps11: Add... |
168 |
/* Store clocks of_node in first element of s2mps11_clks array */ |
31ad0e2a9 clk: s2mps11: all... |
169 |
s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, s2mps11_clks_init); |
e8b60a45a clk: s2mps11: Add... |
170 171 |
if (IS_ERR(s2mps11_clks->clk_np)) return PTR_ERR(s2mps11_clks->clk_np); |
dedbb2724 clk: s2mps11: rem... |
172 |
for (i = 0; i < S2MPS11_CLKS_NUM; i++) { |
31ad0e2a9 clk: s2mps11: all... |
173 |
if (i == S2MPS11_CLK_CP && hwid == S2MPS14X) |
e8b60a45a clk: s2mps11: Add... |
174 |
continue; /* Skip clocks not present in some devices */ |
dedbb2724 clk: s2mps11: rem... |
175 176 177 178 179 180 181 182 |
s2mps11_clks[i].iodev = iodev; s2mps11_clks[i].hw.init = &s2mps11_clks_init[i]; s2mps11_clks[i].mask = 1 << i; s2mps11_clks[i].reg = s2mps11_reg; s2mps11_clks[i].clk = devm_clk_register(&pdev->dev, &s2mps11_clks[i].hw); if (IS_ERR(s2mps11_clks[i].clk)) { |
7cc560dea clk: s2mps11: Add... |
183 184 |
dev_err(&pdev->dev, "Fail to register : %s ", |
dedbb2724 clk: s2mps11: rem... |
185 186 |
s2mps11_clks_init[i].name); ret = PTR_ERR(s2mps11_clks[i].clk); |
7cc560dea clk: s2mps11: Add... |
187 188 |
goto err_reg; } |
b228fad50 clk: s2mps11: Mig... |
189 |
s2mps11_clks[i].lookup = clkdev_hw_create(&s2mps11_clks[i].hw, |
dedbb2724 clk: s2mps11: rem... |
190 191 |
s2mps11_clks_init[i].name, NULL); if (!s2mps11_clks[i].lookup) { |
7cc560dea clk: s2mps11: Add... |
192 |
ret = -ENOMEM; |
264e3b75d clk: s2mps11: Sim... |
193 |
goto err_reg; |
7cc560dea clk: s2mps11: Add... |
194 |
} |
b228fad50 clk: s2mps11: Mig... |
195 |
clk_data->hws[i] = &s2mps11_clks[i].hw; |
e8b60a45a clk: s2mps11: Add... |
196 |
} |
7cc560dea clk: s2mps11: Add... |
197 |
|
b228fad50 clk: s2mps11: Mig... |
198 199 200 |
clk_data->num = S2MPS11_CLKS_NUM; of_clk_add_hw_provider(s2mps11_clks->clk_np, of_clk_hw_onecell_get, clk_data); |
7cc560dea clk: s2mps11: Add... |
201 202 203 204 |
platform_set_drvdata(pdev, s2mps11_clks); return ret; |
264e3b75d clk: s2mps11: Sim... |
205 |
|
7cc560dea clk: s2mps11: Add... |
206 |
err_reg: |
264e3b75d clk: s2mps11: Sim... |
207 208 |
while (--i >= 0) clkdev_drop(s2mps11_clks[i].lookup); |
7cc560dea clk: s2mps11: Add... |
209 210 211 212 213 214 215 216 |
return ret; } static int s2mps11_clk_remove(struct platform_device *pdev) { struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev); int i; |
bf416bd45 clk: s2mps11: Add... |
217 218 219 |
of_clk_del_provider(s2mps11_clks[0].clk_np); /* Drop the reference obtained in s2mps11_clk_parse_dt */ of_node_put(s2mps11_clks[0].clk_np); |
e8b60a45a clk: s2mps11: Add... |
220 221 222 223 |
for (i = 0; i < S2MPS11_CLKS_NUM; i++) { /* Skip clocks not present on S2MPS14 */ if (!s2mps11_clks[i].lookup) continue; |
7cc560dea clk: s2mps11: Add... |
224 |
clkdev_drop(s2mps11_clks[i].lookup); |
e8b60a45a clk: s2mps11: Add... |
225 |
} |
7cc560dea clk: s2mps11: Add... |
226 227 228 229 230 |
return 0; } static const struct platform_device_id s2mps11_clk_id[] = { |
64d64c357 clk: clk-s2mps11:... |
231 |
{ "s2mps11-clk", S2MPS11X}, |
f928b53d7 clk: s2mps11: Add... |
232 |
{ "s2mps13-clk", S2MPS13X}, |
e8b60a45a clk: s2mps11: Add... |
233 |
{ "s2mps14-clk", S2MPS14X}, |
e8e6b840c clk: clk-s2mps11:... |
234 |
{ "s5m8767-clk", S5M8767X}, |
7cc560dea clk: s2mps11: Add... |
235 236 237 238 239 240 241 |
{ }, }; MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); static struct platform_driver s2mps11_clk_driver = { .driver = { .name = "s2mps11-clk", |
7cc560dea clk: s2mps11: Add... |
242 243 244 245 246 247 248 249 250 251 252 |
}, .probe = s2mps11_clk_probe, .remove = s2mps11_clk_remove, .id_table = s2mps11_clk_id, }; static int __init s2mps11_clk_init(void) { return platform_driver_register(&s2mps11_clk_driver); } subsys_initcall(s2mps11_clk_init); |
7764d0cdc clk: s2mps11: Use... |
253 |
static void __exit s2mps11_clk_cleanup(void) |
7cc560dea clk: s2mps11: Add... |
254 255 256 257 258 259 260 261 |
{ platform_driver_unregister(&s2mps11_clk_driver); } module_exit(s2mps11_clk_cleanup); MODULE_DESCRIPTION("S2MPS11 Clock Driver"); MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>"); MODULE_LICENSE("GPL"); |