diff options
Diffstat (limited to 'drivers/clk/clk-max77686.c')
-rw-r--r-- | drivers/clk/clk-max77686.c | 184 |
1 files changed, 9 insertions, 175 deletions
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 3d7e8dd8fd58..86cdb3a28629 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c | |||
@@ -30,193 +30,38 @@ | |||
30 | #include <linux/mutex.h> | 30 | #include <linux/mutex.h> |
31 | #include <linux/clkdev.h> | 31 | #include <linux/clkdev.h> |
32 | 32 | ||
33 | enum { | 33 | #include <dt-bindings/clock/maxim,max77686.h> |
34 | MAX77686_CLK_AP = 0, | 34 | #include "clk-max-gen.h" |
35 | MAX77686_CLK_CP, | ||
36 | MAX77686_CLK_PMIC, | ||
37 | MAX77686_CLKS_NUM, | ||
38 | }; | ||
39 | |||
40 | struct max77686_clk { | ||
41 | struct max77686_dev *iodev; | ||
42 | u32 mask; | ||
43 | struct clk_hw hw; | ||
44 | struct clk_lookup *lookup; | ||
45 | }; | ||
46 | |||
47 | static struct max77686_clk *to_max77686_clk(struct clk_hw *hw) | ||
48 | { | ||
49 | return container_of(hw, struct max77686_clk, hw); | ||
50 | } | ||
51 | |||
52 | static int max77686_clk_prepare(struct clk_hw *hw) | ||
53 | { | ||
54 | struct max77686_clk *max77686 = to_max77686_clk(hw); | ||
55 | |||
56 | return regmap_update_bits(max77686->iodev->regmap, | ||
57 | MAX77686_REG_32KHZ, max77686->mask, | ||
58 | max77686->mask); | ||
59 | } | ||
60 | |||
61 | static void max77686_clk_unprepare(struct clk_hw *hw) | ||
62 | { | ||
63 | struct max77686_clk *max77686 = to_max77686_clk(hw); | ||
64 | |||
65 | regmap_update_bits(max77686->iodev->regmap, | ||
66 | MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask); | ||
67 | } | ||
68 | |||
69 | static int max77686_clk_is_prepared(struct clk_hw *hw) | ||
70 | { | ||
71 | struct max77686_clk *max77686 = to_max77686_clk(hw); | ||
72 | int ret; | ||
73 | u32 val; | ||
74 | |||
75 | ret = regmap_read(max77686->iodev->regmap, | ||
76 | MAX77686_REG_32KHZ, &val); | ||
77 | |||
78 | if (ret < 0) | ||
79 | return -EINVAL; | ||
80 | |||
81 | return val & max77686->mask; | ||
82 | } | ||
83 | |||
84 | static unsigned long max77686_recalc_rate(struct clk_hw *hw, | ||
85 | unsigned long parent_rate) | ||
86 | { | ||
87 | return 32768; | ||
88 | } | ||
89 | |||
90 | static struct clk_ops max77686_clk_ops = { | ||
91 | .prepare = max77686_clk_prepare, | ||
92 | .unprepare = max77686_clk_unprepare, | ||
93 | .is_prepared = max77686_clk_is_prepared, | ||
94 | .recalc_rate = max77686_recalc_rate, | ||
95 | }; | ||
96 | 35 | ||
97 | static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = { | 36 | static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = { |
98 | [MAX77686_CLK_AP] = { | 37 | [MAX77686_CLK_AP] = { |
99 | .name = "32khz_ap", | 38 | .name = "32khz_ap", |
100 | .ops = &max77686_clk_ops, | 39 | .ops = &max_gen_clk_ops, |
101 | .flags = CLK_IS_ROOT, | 40 | .flags = CLK_IS_ROOT, |
102 | }, | 41 | }, |
103 | [MAX77686_CLK_CP] = { | 42 | [MAX77686_CLK_CP] = { |
104 | .name = "32khz_cp", | 43 | .name = "32khz_cp", |
105 | .ops = &max77686_clk_ops, | 44 | .ops = &max_gen_clk_ops, |
106 | .flags = CLK_IS_ROOT, | 45 | .flags = CLK_IS_ROOT, |
107 | }, | 46 | }, |
108 | [MAX77686_CLK_PMIC] = { | 47 | [MAX77686_CLK_PMIC] = { |
109 | .name = "32khz_pmic", | 48 | .name = "32khz_pmic", |
110 | .ops = &max77686_clk_ops, | 49 | .ops = &max_gen_clk_ops, |
111 | .flags = CLK_IS_ROOT, | 50 | .flags = CLK_IS_ROOT, |
112 | }, | 51 | }, |
113 | }; | 52 | }; |
114 | 53 | ||
115 | static struct clk *max77686_clk_register(struct device *dev, | ||
116 | struct max77686_clk *max77686) | ||
117 | { | ||
118 | struct clk *clk; | ||
119 | struct clk_hw *hw = &max77686->hw; | ||
120 | |||
121 | clk = clk_register(dev, hw); | ||
122 | if (IS_ERR(clk)) | ||
123 | return clk; | ||
124 | |||
125 | max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL); | ||
126 | if (!max77686->lookup) | ||
127 | return ERR_PTR(-ENOMEM); | ||
128 | |||
129 | max77686->lookup->con_id = hw->init->name; | ||
130 | max77686->lookup->clk = clk; | ||
131 | |||
132 | clkdev_add(max77686->lookup); | ||
133 | |||
134 | return clk; | ||
135 | } | ||
136 | |||
137 | static int max77686_clk_probe(struct platform_device *pdev) | 54 | static int max77686_clk_probe(struct platform_device *pdev) |
138 | { | 55 | { |
139 | struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); | 56 | struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); |
140 | struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM]; | ||
141 | struct clk **clocks; | ||
142 | int i, ret; | ||
143 | |||
144 | clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *) | ||
145 | * MAX77686_CLKS_NUM, GFP_KERNEL); | ||
146 | if (!clocks) | ||
147 | return -ENOMEM; | ||
148 | |||
149 | for (i = 0; i < MAX77686_CLKS_NUM; i++) { | ||
150 | max77686_clks[i] = devm_kzalloc(&pdev->dev, | ||
151 | sizeof(struct max77686_clk), GFP_KERNEL); | ||
152 | if (!max77686_clks[i]) | ||
153 | return -ENOMEM; | ||
154 | } | ||
155 | |||
156 | for (i = 0; i < MAX77686_CLKS_NUM; i++) { | ||
157 | max77686_clks[i]->iodev = iodev; | ||
158 | max77686_clks[i]->mask = 1 << i; | ||
159 | max77686_clks[i]->hw.init = &max77686_clks_init[i]; | ||
160 | |||
161 | clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]); | ||
162 | if (IS_ERR(clocks[i])) { | ||
163 | ret = PTR_ERR(clocks[i]); | ||
164 | dev_err(&pdev->dev, "failed to register %s\n", | ||
165 | max77686_clks[i]->hw.init->name); | ||
166 | goto err_clocks; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | platform_set_drvdata(pdev, clocks); | ||
171 | |||
172 | if (iodev->dev->of_node) { | ||
173 | struct clk_onecell_data *of_data; | ||
174 | 57 | ||
175 | of_data = devm_kzalloc(&pdev->dev, | 58 | return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ, |
176 | sizeof(*of_data), GFP_KERNEL); | 59 | max77686_clks_init, MAX77686_CLKS_NUM); |
177 | if (!of_data) { | ||
178 | ret = -ENOMEM; | ||
179 | goto err_clocks; | ||
180 | } | ||
181 | |||
182 | of_data->clks = clocks; | ||
183 | of_data->clk_num = MAX77686_CLKS_NUM; | ||
184 | ret = of_clk_add_provider(iodev->dev->of_node, | ||
185 | of_clk_src_onecell_get, of_data); | ||
186 | if (ret) { | ||
187 | dev_err(&pdev->dev, "failed to register OF clock provider\n"); | ||
188 | goto err_clocks; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | return 0; | ||
193 | |||
194 | err_clocks: | ||
195 | for (--i; i >= 0; --i) { | ||
196 | clkdev_drop(max77686_clks[i]->lookup); | ||
197 | clk_unregister(max77686_clks[i]->hw.clk); | ||
198 | } | ||
199 | |||
200 | return ret; | ||
201 | } | 60 | } |
202 | 61 | ||
203 | static int max77686_clk_remove(struct platform_device *pdev) | 62 | static int max77686_clk_remove(struct platform_device *pdev) |
204 | { | 63 | { |
205 | struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); | 64 | return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM); |
206 | struct clk **clocks = platform_get_drvdata(pdev); | ||
207 | int i; | ||
208 | |||
209 | if (iodev->dev->of_node) | ||
210 | of_clk_del_provider(iodev->dev->of_node); | ||
211 | |||
212 | for (i = 0; i < MAX77686_CLKS_NUM; i++) { | ||
213 | struct clk_hw *hw = __clk_get_hw(clocks[i]); | ||
214 | struct max77686_clk *max77686 = to_max77686_clk(hw); | ||
215 | |||
216 | clkdev_drop(max77686->lookup); | ||
217 | clk_unregister(clocks[i]); | ||
218 | } | ||
219 | return 0; | ||
220 | } | 65 | } |
221 | 66 | ||
222 | static const struct platform_device_id max77686_clk_id[] = { | 67 | static const struct platform_device_id max77686_clk_id[] = { |
@@ -228,24 +73,13 @@ MODULE_DEVICE_TABLE(platform, max77686_clk_id); | |||
228 | static struct platform_driver max77686_clk_driver = { | 73 | static struct platform_driver max77686_clk_driver = { |
229 | .driver = { | 74 | .driver = { |
230 | .name = "max77686-clk", | 75 | .name = "max77686-clk", |
231 | .owner = THIS_MODULE, | ||
232 | }, | 76 | }, |
233 | .probe = max77686_clk_probe, | 77 | .probe = max77686_clk_probe, |
234 | .remove = max77686_clk_remove, | 78 | .remove = max77686_clk_remove, |
235 | .id_table = max77686_clk_id, | 79 | .id_table = max77686_clk_id, |
236 | }; | 80 | }; |
237 | 81 | ||
238 | static int __init max77686_clk_init(void) | 82 | module_platform_driver(max77686_clk_driver); |
239 | { | ||
240 | return platform_driver_register(&max77686_clk_driver); | ||
241 | } | ||
242 | subsys_initcall(max77686_clk_init); | ||
243 | |||
244 | static void __init max77686_clk_cleanup(void) | ||
245 | { | ||
246 | platform_driver_unregister(&max77686_clk_driver); | ||
247 | } | ||
248 | module_exit(max77686_clk_cleanup); | ||
249 | 83 | ||
250 | MODULE_DESCRIPTION("MAXIM 77686 Clock Driver"); | 84 | MODULE_DESCRIPTION("MAXIM 77686 Clock Driver"); |
251 | MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); | 85 | MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); |