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