]>
Commit | Line | Data |
---|---|---|
74ba9207 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
8e0cb05b PA |
2 | /* |
3 | * ECAP PWM driver | |
4 | * | |
216a094d | 5 | * Copyright (C) 2012 Texas Instruments, Inc. - https://www.ti.com/ |
8e0cb05b PA |
6 | */ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/platform_device.h> | |
10 | #include <linux/io.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/clk.h> | |
13 | #include <linux/pm_runtime.h> | |
14 | #include <linux/pwm.h> | |
333b08ee PA |
15 | #include <linux/of_device.h> |
16 | ||
8e0cb05b PA |
17 | /* ECAP registers and bits definitions */ |
18 | #define CAP1 0x08 | |
19 | #define CAP2 0x0C | |
20 | #define CAP3 0x10 | |
21 | #define CAP4 0x14 | |
22 | #define ECCTL2 0x2A | |
454870a4 | 23 | #define ECCTL2_APWM_POL_LOW BIT(10) |
8e0cb05b PA |
24 | #define ECCTL2_APWM_MODE BIT(9) |
25 | #define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) | |
26 | #define ECCTL2_TSCTR_FREERUN BIT(4) | |
27 | ||
0d75c203 | 28 | struct ecap_context { |
53c7972d TR |
29 | u32 cap3; |
30 | u32 cap4; | |
31 | u16 ecctl2; | |
0d75c203 PA |
32 | }; |
33 | ||
8e0cb05b | 34 | struct ecap_pwm_chip { |
53c7972d TR |
35 | struct pwm_chip chip; |
36 | unsigned int clk_rate; | |
37 | void __iomem *mmio_base; | |
0d75c203 | 38 | struct ecap_context ctx; |
8e0cb05b PA |
39 | }; |
40 | ||
41 | static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip) | |
42 | { | |
43 | return container_of(chip, struct ecap_pwm_chip, chip); | |
44 | } | |
45 | ||
46 | /* | |
47 | * period_ns = 10^9 * period_cycles / PWM_CLK_RATE | |
48 | * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE | |
49 | */ | |
50 | static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |
0ca7acd8 | 51 | int duty_ns, int period_ns, int enabled) |
8e0cb05b PA |
52 | { |
53 | struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); | |
53c7972d | 54 | u32 period_cycles, duty_cycles; |
8e0cb05b | 55 | unsigned long long c; |
53c7972d | 56 | u16 value; |
8e0cb05b | 57 | |
8e0cb05b PA |
58 | c = pc->clk_rate; |
59 | c = c * period_ns; | |
60 | do_div(c, NSEC_PER_SEC); | |
53c7972d | 61 | period_cycles = (u32)c; |
8e0cb05b PA |
62 | |
63 | if (period_cycles < 1) { | |
64 | period_cycles = 1; | |
65 | duty_cycles = 1; | |
66 | } else { | |
67 | c = pc->clk_rate; | |
68 | c = c * duty_ns; | |
69 | do_div(c, NSEC_PER_SEC); | |
53c7972d | 70 | duty_cycles = (u32)c; |
8e0cb05b PA |
71 | } |
72 | ||
73 | pm_runtime_get_sync(pc->chip.dev); | |
74 | ||
53c7972d | 75 | value = readw(pc->mmio_base + ECCTL2); |
8e0cb05b PA |
76 | |
77 | /* Configure APWM mode & disable sync option */ | |
53c7972d | 78 | value |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA; |
8e0cb05b | 79 | |
53c7972d | 80 | writew(value, pc->mmio_base + ECCTL2); |
8e0cb05b | 81 | |
0ca7acd8 | 82 | if (!enabled) { |
8e0cb05b PA |
83 | /* Update active registers if not running */ |
84 | writel(duty_cycles, pc->mmio_base + CAP2); | |
85 | writel(period_cycles, pc->mmio_base + CAP1); | |
86 | } else { | |
87 | /* | |
88 | * Update shadow registers to configure period and | |
89 | * compare values. This helps current PWM period to | |
90 | * complete on reconfiguring | |
91 | */ | |
92 | writel(duty_cycles, pc->mmio_base + CAP4); | |
93 | writel(period_cycles, pc->mmio_base + CAP3); | |
94 | } | |
95 | ||
0ca7acd8 | 96 | if (!enabled) { |
53c7972d | 97 | value = readw(pc->mmio_base + ECCTL2); |
c06fad9d | 98 | /* Disable APWM mode to put APWM output Low */ |
53c7972d TR |
99 | value &= ~ECCTL2_APWM_MODE; |
100 | writew(value, pc->mmio_base + ECCTL2); | |
c06fad9d PA |
101 | } |
102 | ||
8e0cb05b | 103 | pm_runtime_put_sync(pc->chip.dev); |
53c7972d | 104 | |
8e0cb05b PA |
105 | return 0; |
106 | } | |
107 | ||
454870a4 | 108 | static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, |
53c7972d | 109 | enum pwm_polarity polarity) |
454870a4 PA |
110 | { |
111 | struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); | |
53c7972d | 112 | u16 value; |
454870a4 PA |
113 | |
114 | pm_runtime_get_sync(pc->chip.dev); | |
53c7972d TR |
115 | |
116 | value = readw(pc->mmio_base + ECCTL2); | |
117 | ||
454870a4 PA |
118 | if (polarity == PWM_POLARITY_INVERSED) |
119 | /* Duty cycle defines LOW period of PWM */ | |
53c7972d | 120 | value |= ECCTL2_APWM_POL_LOW; |
454870a4 PA |
121 | else |
122 | /* Duty cycle defines HIGH period of PWM */ | |
53c7972d TR |
123 | value &= ~ECCTL2_APWM_POL_LOW; |
124 | ||
125 | writew(value, pc->mmio_base + ECCTL2); | |
454870a4 | 126 | |
454870a4 | 127 | pm_runtime_put_sync(pc->chip.dev); |
53c7972d | 128 | |
454870a4 PA |
129 | return 0; |
130 | } | |
131 | ||
8e0cb05b PA |
132 | static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
133 | { | |
134 | struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); | |
53c7972d | 135 | u16 value; |
8e0cb05b PA |
136 | |
137 | /* Leave clock enabled on enabling PWM */ | |
138 | pm_runtime_get_sync(pc->chip.dev); | |
139 | ||
140 | /* | |
141 | * Enable 'Free run Time stamp counter mode' to start counter | |
142 | * and 'APWM mode' to enable APWM output | |
143 | */ | |
53c7972d TR |
144 | value = readw(pc->mmio_base + ECCTL2); |
145 | value |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE; | |
146 | writew(value, pc->mmio_base + ECCTL2); | |
147 | ||
8e0cb05b PA |
148 | return 0; |
149 | } | |
150 | ||
151 | static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |
152 | { | |
153 | struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); | |
53c7972d | 154 | u16 value; |
8e0cb05b PA |
155 | |
156 | /* | |
157 | * Disable 'Free run Time stamp counter mode' to stop counter | |
158 | * and 'APWM mode' to put APWM output to low | |
159 | */ | |
53c7972d TR |
160 | value = readw(pc->mmio_base + ECCTL2); |
161 | value &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE); | |
162 | writew(value, pc->mmio_base + ECCTL2); | |
8e0cb05b PA |
163 | |
164 | /* Disable clock on PWM disable */ | |
165 | pm_runtime_put_sync(pc->chip.dev); | |
166 | } | |
167 | ||
0ca7acd8 UKK |
168 | static int ecap_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
169 | const struct pwm_state *state) | |
170 | { | |
171 | int err; | |
172 | int enabled = pwm->state.enabled; | |
173 | ||
174 | if (state->polarity != pwm->state.polarity) { | |
175 | ||
176 | if (enabled) { | |
177 | ecap_pwm_disable(chip, pwm); | |
178 | enabled = false; | |
179 | } | |
180 | ||
181 | err = ecap_pwm_set_polarity(chip, pwm, state->polarity); | |
182 | if (err) | |
183 | return err; | |
184 | } | |
185 | ||
186 | if (!state->enabled) { | |
187 | if (enabled) | |
188 | ecap_pwm_disable(chip, pwm); | |
189 | return 0; | |
190 | } | |
191 | ||
25f70b8f UKK |
192 | if (state->period > NSEC_PER_SEC) |
193 | return -ERANGE; | |
0ca7acd8 | 194 | |
25f70b8f UKK |
195 | err = ecap_pwm_config(chip, pwm, state->duty_cycle, |
196 | state->period, enabled); | |
197 | if (err) | |
198 | return err; | |
0ca7acd8 UKK |
199 | |
200 | if (!enabled) | |
201 | return ecap_pwm_enable(chip, pwm); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
8e0cb05b | 206 | static const struct pwm_ops ecap_pwm_ops = { |
0ca7acd8 | 207 | .apply = ecap_pwm_apply, |
53c7972d | 208 | .owner = THIS_MODULE, |
8e0cb05b PA |
209 | }; |
210 | ||
333b08ee | 211 | static const struct of_device_id ecap_of_match[] = { |
ae5200d2 | 212 | { .compatible = "ti,am3352-ecap" }, |
333b08ee PA |
213 | { .compatible = "ti,am33xx-ecap" }, |
214 | {}, | |
215 | }; | |
216 | MODULE_DEVICE_TABLE(of, ecap_of_match); | |
217 | ||
3e9fe83d | 218 | static int ecap_pwm_probe(struct platform_device *pdev) |
8e0cb05b | 219 | { |
ae5200d2 | 220 | struct device_node *np = pdev->dev.of_node; |
53c7972d | 221 | struct ecap_pwm_chip *pc; |
8e0cb05b | 222 | struct clk *clk; |
53c7972d | 223 | int ret; |
8e0cb05b PA |
224 | |
225 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | |
c10d5063 | 226 | if (!pc) |
8e0cb05b | 227 | return -ENOMEM; |
8e0cb05b PA |
228 | |
229 | clk = devm_clk_get(&pdev->dev, "fck"); | |
ae5200d2 CJF |
230 | if (IS_ERR(clk)) { |
231 | if (of_device_is_compatible(np, "ti,am33xx-ecap")) { | |
232 | dev_warn(&pdev->dev, "Binding is obsolete.\n"); | |
233 | clk = devm_clk_get(pdev->dev.parent, "fck"); | |
234 | } | |
235 | } | |
236 | ||
8e0cb05b PA |
237 | if (IS_ERR(clk)) { |
238 | dev_err(&pdev->dev, "failed to get clock\n"); | |
239 | return PTR_ERR(clk); | |
240 | } | |
241 | ||
242 | pc->clk_rate = clk_get_rate(clk); | |
243 | if (!pc->clk_rate) { | |
244 | dev_err(&pdev->dev, "failed to get clock rate\n"); | |
245 | return -EINVAL; | |
246 | } | |
247 | ||
248 | pc->chip.dev = &pdev->dev; | |
249 | pc->chip.ops = &ecap_pwm_ops; | |
8e0cb05b PA |
250 | pc->chip.npwm = 1; |
251 | ||
1dcf0523 | 252 | pc->mmio_base = devm_platform_ioremap_resource(pdev, 0); |
6d4294d1 TR |
253 | if (IS_ERR(pc->mmio_base)) |
254 | return PTR_ERR(pc->mmio_base); | |
8e0cb05b | 255 | |
a64a5853 | 256 | ret = devm_pwmchip_add(&pdev->dev, &pc->chip); |
8e0cb05b PA |
257 | if (ret < 0) { |
258 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); | |
259 | return ret; | |
260 | } | |
261 | ||
23f373e6 | 262 | platform_set_drvdata(pdev, pc); |
8e0cb05b | 263 | pm_runtime_enable(&pdev->dev); |
333b08ee | 264 | |
8e0cb05b PA |
265 | return 0; |
266 | } | |
267 | ||
77f37917 | 268 | static int ecap_pwm_remove(struct platform_device *pdev) |
8e0cb05b | 269 | { |
8e0cb05b | 270 | pm_runtime_disable(&pdev->dev); |
53c7972d | 271 | |
a64a5853 | 272 | return 0; |
8e0cb05b PA |
273 | } |
274 | ||
3943a650 | 275 | #ifdef CONFIG_PM_SLEEP |
a38c9898 | 276 | static void ecap_pwm_save_context(struct ecap_pwm_chip *pc) |
0d75c203 PA |
277 | { |
278 | pm_runtime_get_sync(pc->chip.dev); | |
279 | pc->ctx.ecctl2 = readw(pc->mmio_base + ECCTL2); | |
280 | pc->ctx.cap4 = readl(pc->mmio_base + CAP4); | |
281 | pc->ctx.cap3 = readl(pc->mmio_base + CAP3); | |
282 | pm_runtime_put_sync(pc->chip.dev); | |
283 | } | |
284 | ||
a38c9898 | 285 | static void ecap_pwm_restore_context(struct ecap_pwm_chip *pc) |
0d75c203 PA |
286 | { |
287 | writel(pc->ctx.cap3, pc->mmio_base + CAP3); | |
288 | writel(pc->ctx.cap4, pc->mmio_base + CAP4); | |
289 | writew(pc->ctx.ecctl2, pc->mmio_base + ECCTL2); | |
290 | } | |
291 | ||
292 | static int ecap_pwm_suspend(struct device *dev) | |
293 | { | |
294 | struct ecap_pwm_chip *pc = dev_get_drvdata(dev); | |
295 | struct pwm_device *pwm = pc->chip.pwms; | |
296 | ||
297 | ecap_pwm_save_context(pc); | |
298 | ||
299 | /* Disable explicitly if PWM is running */ | |
5c31252c | 300 | if (pwm_is_enabled(pwm)) |
0d75c203 PA |
301 | pm_runtime_put_sync(dev); |
302 | ||
303 | return 0; | |
304 | } | |
305 | ||
306 | static int ecap_pwm_resume(struct device *dev) | |
307 | { | |
308 | struct ecap_pwm_chip *pc = dev_get_drvdata(dev); | |
309 | struct pwm_device *pwm = pc->chip.pwms; | |
310 | ||
311 | /* Enable explicitly if PWM was running */ | |
5c31252c | 312 | if (pwm_is_enabled(pwm)) |
0d75c203 PA |
313 | pm_runtime_get_sync(dev); |
314 | ||
315 | ecap_pwm_restore_context(pc); | |
316 | return 0; | |
317 | } | |
b78f5fc9 | 318 | #endif |
0d75c203 PA |
319 | |
320 | static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); | |
321 | ||
8e0cb05b PA |
322 | static struct platform_driver ecap_pwm_driver = { |
323 | .driver = { | |
53c7972d | 324 | .name = "ecap", |
333b08ee | 325 | .of_match_table = ecap_of_match, |
53c7972d | 326 | .pm = &ecap_pwm_pm_ops, |
8e0cb05b PA |
327 | }, |
328 | .probe = ecap_pwm_probe, | |
fd109112 | 329 | .remove = ecap_pwm_remove, |
8e0cb05b | 330 | }; |
8e0cb05b PA |
331 | module_platform_driver(ecap_pwm_driver); |
332 | ||
333 | MODULE_DESCRIPTION("ECAP PWM driver"); | |
334 | MODULE_AUTHOR("Texas Instruments"); | |
335 | MODULE_LICENSE("GPL"); |