diff options
Diffstat (limited to 'drivers/pwm/pwm-berlin.c')
-rw-r--r-- | drivers/pwm/pwm-berlin.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index 65108129d505..01339c152ab0 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
18 | #include <linux/pwm.h> | 18 | #include <linux/pwm.h> |
19 | #include <linux/slab.h> | ||
19 | 20 | ||
20 | #define BERLIN_PWM_EN 0x0 | 21 | #define BERLIN_PWM_EN 0x0 |
21 | #define BERLIN_PWM_ENABLE BIT(0) | 22 | #define BERLIN_PWM_ENABLE BIT(0) |
@@ -27,6 +28,13 @@ | |||
27 | #define BERLIN_PWM_TCNT 0xc | 28 | #define BERLIN_PWM_TCNT 0xc |
28 | #define BERLIN_PWM_MAX_TCNT 65535 | 29 | #define BERLIN_PWM_MAX_TCNT 65535 |
29 | 30 | ||
31 | struct berlin_pwm_channel { | ||
32 | u32 enable; | ||
33 | u32 ctrl; | ||
34 | u32 duty; | ||
35 | u32 tcnt; | ||
36 | }; | ||
37 | |||
30 | struct berlin_pwm_chip { | 38 | struct berlin_pwm_chip { |
31 | struct pwm_chip chip; | 39 | struct pwm_chip chip; |
32 | struct clk *clk; | 40 | struct clk *clk; |
@@ -55,6 +63,25 @@ static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip, | |||
55 | writel_relaxed(value, chip->base + channel * 0x10 + offset); | 63 | writel_relaxed(value, chip->base + channel * 0x10 + offset); |
56 | } | 64 | } |
57 | 65 | ||
66 | static int berlin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | ||
67 | { | ||
68 | struct berlin_pwm_channel *channel; | ||
69 | |||
70 | channel = kzalloc(sizeof(*channel), GFP_KERNEL); | ||
71 | if (!channel) | ||
72 | return -ENOMEM; | ||
73 | |||
74 | return pwm_set_chip_data(pwm, channel); | ||
75 | } | ||
76 | |||
77 | static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | ||
78 | { | ||
79 | struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm); | ||
80 | |||
81 | pwm_set_chip_data(pwm, NULL); | ||
82 | kfree(channel); | ||
83 | } | ||
84 | |||
58 | static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev, | 85 | static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev, |
59 | int duty_ns, int period_ns) | 86 | int duty_ns, int period_ns) |
60 | { | 87 | { |
@@ -137,6 +164,8 @@ static void berlin_pwm_disable(struct pwm_chip *chip, | |||
137 | } | 164 | } |
138 | 165 | ||
139 | static const struct pwm_ops berlin_pwm_ops = { | 166 | static const struct pwm_ops berlin_pwm_ops = { |
167 | .request = berlin_pwm_request, | ||
168 | .free = berlin_pwm_free, | ||
140 | .config = berlin_pwm_config, | 169 | .config = berlin_pwm_config, |
141 | .set_polarity = berlin_pwm_set_polarity, | 170 | .set_polarity = berlin_pwm_set_polarity, |
142 | .enable = berlin_pwm_enable, | 171 | .enable = berlin_pwm_enable, |
@@ -204,12 +233,67 @@ static int berlin_pwm_remove(struct platform_device *pdev) | |||
204 | return ret; | 233 | return ret; |
205 | } | 234 | } |
206 | 235 | ||
236 | #ifdef CONFIG_PM_SLEEP | ||
237 | static int berlin_pwm_suspend(struct device *dev) | ||
238 | { | ||
239 | struct berlin_pwm_chip *pwm = dev_get_drvdata(dev); | ||
240 | unsigned int i; | ||
241 | |||
242 | for (i = 0; i < pwm->chip.npwm; i++) { | ||
243 | struct berlin_pwm_channel *channel; | ||
244 | |||
245 | channel = pwm_get_chip_data(&pwm->chip.pwms[i]); | ||
246 | if (!channel) | ||
247 | continue; | ||
248 | |||
249 | channel->enable = berlin_pwm_readl(pwm, i, BERLIN_PWM_ENABLE); | ||
250 | channel->ctrl = berlin_pwm_readl(pwm, i, BERLIN_PWM_CONTROL); | ||
251 | channel->duty = berlin_pwm_readl(pwm, i, BERLIN_PWM_DUTY); | ||
252 | channel->tcnt = berlin_pwm_readl(pwm, i, BERLIN_PWM_TCNT); | ||
253 | } | ||
254 | |||
255 | clk_disable_unprepare(pwm->clk); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int berlin_pwm_resume(struct device *dev) | ||
261 | { | ||
262 | struct berlin_pwm_chip *pwm = dev_get_drvdata(dev); | ||
263 | unsigned int i; | ||
264 | int ret; | ||
265 | |||
266 | ret = clk_prepare_enable(pwm->clk); | ||
267 | if (ret) | ||
268 | return ret; | ||
269 | |||
270 | for (i = 0; i < pwm->chip.npwm; i++) { | ||
271 | struct berlin_pwm_channel *channel; | ||
272 | |||
273 | channel = pwm_get_chip_data(&pwm->chip.pwms[i]); | ||
274 | if (!channel) | ||
275 | continue; | ||
276 | |||
277 | berlin_pwm_writel(pwm, i, channel->ctrl, BERLIN_PWM_CONTROL); | ||
278 | berlin_pwm_writel(pwm, i, channel->duty, BERLIN_PWM_DUTY); | ||
279 | berlin_pwm_writel(pwm, i, channel->tcnt, BERLIN_PWM_TCNT); | ||
280 | berlin_pwm_writel(pwm, i, channel->enable, BERLIN_PWM_ENABLE); | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | #endif | ||
286 | |||
287 | static SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend, | ||
288 | berlin_pwm_resume); | ||
289 | |||
207 | static struct platform_driver berlin_pwm_driver = { | 290 | static struct platform_driver berlin_pwm_driver = { |
208 | .probe = berlin_pwm_probe, | 291 | .probe = berlin_pwm_probe, |
209 | .remove = berlin_pwm_remove, | 292 | .remove = berlin_pwm_remove, |
210 | .driver = { | 293 | .driver = { |
211 | .name = "berlin-pwm", | 294 | .name = "berlin-pwm", |
212 | .of_match_table = berlin_pwm_match, | 295 | .of_match_table = berlin_pwm_match, |
296 | .pm = &berlin_pwm_pm_ops, | ||
213 | }, | 297 | }, |
214 | }; | 298 | }; |
215 | module_platform_driver(berlin_pwm_driver); | 299 | module_platform_driver(berlin_pwm_driver); |