History

Merge tag 'pwm/for-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm

Pull pwm updates from Thierry Reding: "The ZTE ZX platform is being removed, so the PWM driver is no longer needed and removed as well. Other than that this contains a small set of fixes and cleanups across a couple of drivers" * tag 'pwm/for-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: pwm: lpc18xx-sct: remove unneeded semicolon pwm: iqs620a: Correct a stale state variable pwm: iqs620a: Fix overflow and optimize calculations pwm: rockchip: Enable clock before calling clk_get_rate() pwm: rockchip: Eliminate potential race condition when probing pwm: rockchip: Replace "bus clk" with "PWM clk" pwm: rockchip: rockchip_pwm_probe(): Remove superfluous clk_unprepare() pwm: rockchip: Enable APB clock during register access while probing pwm: Remove ZTE ZX driver

Linus Torvalds committed 5 years ago

Commit 2c87f7a2 parents ffc1759 9a9dd7e7 files · +65 −374Browse files

Unified · Split

7 files
  1. Documentation/devicetree/bindings/pwm/pwm-zx.txt +0 −22
  2. drivers/pwm/Kconfig +0 −10
  3. drivers/pwm/Makefile +0 −1
  4. drivers/pwm/pwm-iqs620a.c +41 −53
  5. drivers/pwm/pwm-lpc18xx-sct.c +1 −1
  6. drivers/pwm/pwm-rockchip.c +23 −9
  7. drivers/pwm/pwm-zx.c +0 −278
Documentation/devicetree/bindings/pwm/pwm-zx.txt+0−22
@@ -1,22 +0,0 @@
1ZTE ZX PWM controller
2
3Required properties:
4 - compatible: Should be "zte,zx296718-pwm".
5 - reg: Physical base address and length of the controller's registers.
6 - clocks : The phandle and specifier referencing the controller's clocks.
7 - clock-names: "pclk" for PCLK, "wclk" for WCLK to the PWM controller. The
8 PCLK is for register access, while WCLK is the reference clock for
9 calculating period and duty cycles.
10 - #pwm-cells: Should be 3. See pwm.yaml in this directory for a description of
11 the cells format.
12
13Example:
14
15 pwm: pwm@1439000 {
16 compatible = "zte,zx296718-pwm";
17 reg = <0x1439000 0x1000>;
18 clocks = <&lsp1crm LSP1_PWM_PCLK>,
19 <&lsp1crm LSP1_PWM_WCLK>;
20 clock-names = "pclk", "wclk";
21 #pwm-cells = <3>;
22 };
drivers/pwm/Kconfig+0−10
@@ -611,14 +611,4 @@ config PWM_VT8500 · 610 unmodified lines
611 To compile this driver as a module, choose M here: the module611 To compile this driver as a module, choose M here: the module
612 will be called pwm-vt8500.612 will be called pwm-vt8500.
613 613
614config PWM_ZX
615 tristate "ZTE ZX PWM support"
616 depends on ARCH_ZX || COMPILE_TEST
617 depends on HAS_IOMEM
618 help
619 Generic PWM framework driver for ZTE ZX family SoCs.
620
621 To compile this driver as a module, choose M here: the module
622 will be called pwm-zx.
623
624endif614endif
drivers/pwm/Makefile+0−1
@@ -57,4 +57,3 @@ obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o · 56 unmodified lines
57obj-$(CONFIG_PWM_TWL) += pwm-twl.o57obj-$(CONFIG_PWM_TWL) += pwm-twl.o
58obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o58obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o
59obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o59obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
60obj-$(CONFIG_PWM_ZX) += pwm-zx.o
drivers/pwm/pwm-iqs620a.c+41−53
@@ -37,16 +37,34 @@ struct iqs620_pwm_private { · 36 unmodified lines
37 struct pwm_chip chip;37 struct pwm_chip chip;
38 struct notifier_block notifier;38 struct notifier_block notifier;
39 struct mutex lock;39 struct mutex lock;
40 bool out_en;40 unsigned int duty_scale;
41 u8 duty_val;
42};41};
43 42
43static int iqs620_pwm_init(struct iqs620_pwm_private *iqs620_pwm,
44 unsigned int duty_scale)
45{
46 struct iqs62x_core *iqs62x = iqs620_pwm->iqs62x;
47 int ret;
48
49 if (!duty_scale)
50 return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
51 IQS620_PWR_SETTINGS_PWM_OUT, 0);
52
53 ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
54 duty_scale - 1);
55 if (ret)
56 return ret;
57
58 return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
59 IQS620_PWR_SETTINGS_PWM_OUT, 0xff);
60}
61
44static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,62static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
45 const struct pwm_state *state)63 const struct pwm_state *state)
46{64{
47 struct iqs620_pwm_private *iqs620_pwm;65 struct iqs620_pwm_private *iqs620_pwm;
48 struct iqs62x_core *iqs62x;66 unsigned int duty_cycle;
49 u64 duty_scale;67 unsigned int duty_scale;
50 int ret;68 int ret;
51 69
52 if (state->polarity != PWM_POLARITY_NORMAL)70 if (state->polarity != PWM_POLARITY_NORMAL)
@@ -56,7 +74,6 @@ static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, · 3 unmodified lines
56 return -EINVAL;74 return -EINVAL;
57 75
58 iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);76 iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);
59 iqs62x = iqs620_pwm->iqs62x;
60 77
61 /*78 /*
62 * The duty cycle generated by the device is calculated as follows:79 * The duty cycle generated by the device is calculated as follows:
@@ -70,38 +87,18 @@ static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, · 7 unmodified lines
70 * For lower duty cycles (e.g. 0), the PWM output is simply disabled to87 * For lower duty cycles (e.g. 0), the PWM output is simply disabled to
71 * allow an external pull-down resistor to hold the GPIO3/LTX pin low.88 * allow an external pull-down resistor to hold the GPIO3/LTX pin low.
72 */89 */
73 duty_scale = div_u64(state->duty_cycle * 256, IQS620_PWM_PERIOD_NS);90 duty_cycle = min_t(u64, state->duty_cycle, IQS620_PWM_PERIOD_NS);
74 91 duty_scale = duty_cycle * 256 / IQS620_PWM_PERIOD_NS;
75 mutex_lock(&iqs620_pwm->lock);
76
77 if (!state->enabled || !duty_scale) {
78 ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
79 IQS620_PWR_SETTINGS_PWM_OUT, 0);
80 if (ret)
81 goto err_mutex;
82 }
83 92
84 if (duty_scale) {93 if (!state->enabled)
85 u8 duty_val = min_t(u64, duty_scale - 1, 0xff);94 duty_scale = 0;
86 95
87 ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,96 mutex_lock(&iqs620_pwm->lock);
88 duty_val);
89 if (ret)
90 goto err_mutex;
91
92 iqs620_pwm->duty_val = duty_val;
93 }
94
95 if (state->enabled && duty_scale) {
96 ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
97 IQS620_PWR_SETTINGS_PWM_OUT, 0xff);
98 if (ret)
99 goto err_mutex;
100 }
101 97
102 iqs620_pwm->out_en = state->enabled;98 ret = iqs620_pwm_init(iqs620_pwm, duty_scale);
99 if (!ret)
100 iqs620_pwm->duty_scale = duty_scale;
103 101
104err_mutex:
105 mutex_unlock(&iqs620_pwm->lock);102 mutex_unlock(&iqs620_pwm->lock);
106 103
107 return ret;104 return ret;
@@ -119,12 +116,11 @@ static void iqs620_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, · 11 unmodified lines
119 /*116 /*
120 * Since the device cannot generate a 0% duty cycle, requests to do so117 * Since the device cannot generate a 0% duty cycle, requests to do so
121 * cause subsequent calls to iqs620_pwm_get_state to report the output118 * cause subsequent calls to iqs620_pwm_get_state to report the output
122 * as disabled with duty cycle equal to that which was in use prior to119 * as disabled. This is not ideal, but is the best compromise based on
123 * the request. This is not ideal, but is the best compromise based on
124 * the capabilities of the device.120 * the capabilities of the device.
125 */121 */
126 state->enabled = iqs620_pwm->out_en;122 state->enabled = iqs620_pwm->duty_scale > 0;
127 state->duty_cycle = DIV_ROUND_UP((iqs620_pwm->duty_val + 1) *123 state->duty_cycle = DIV_ROUND_UP(iqs620_pwm->duty_scale *
128 IQS620_PWM_PERIOD_NS, 256);124 IQS620_PWM_PERIOD_NS, 256);
129 125
130 mutex_unlock(&iqs620_pwm->lock);126 mutex_unlock(&iqs620_pwm->lock);
@@ -136,7 +132,6 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier, · 5 unmodified lines
136 unsigned long event_flags, void *context)132 unsigned long event_flags, void *context)
137{133{
138 struct iqs620_pwm_private *iqs620_pwm;134 struct iqs620_pwm_private *iqs620_pwm;
139 struct iqs62x_core *iqs62x;
140 int ret;135 int ret;
141 136
142 if (!(event_flags & BIT(IQS62X_EVENT_SYS_RESET)))137 if (!(event_flags & BIT(IQS62X_EVENT_SYS_RESET)))
@@ -144,7 +139,6 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier, · 1 unmodified line
144 139
145 iqs620_pwm = container_of(notifier, struct iqs620_pwm_private,140 iqs620_pwm = container_of(notifier, struct iqs620_pwm_private,
146 notifier);141 notifier);
147 iqs62x = iqs620_pwm->iqs62x;
148 142
149 mutex_lock(&iqs620_pwm->lock);143 mutex_lock(&iqs620_pwm->lock);
150 144
@@ -153,16 +147,8 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier, · 2 unmodified lines
153 * of a device reset, so nothing else is printed here unless there is147 * of a device reset, so nothing else is printed here unless there is
154 * an additional failure.148 * an additional failure.
155 */149 */
156 ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,150 ret = iqs620_pwm_init(iqs620_pwm, iqs620_pwm->duty_scale);
157 iqs620_pwm->duty_val);
158 if (ret)
159 goto err_mutex;
160 151
161 ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
162 IQS620_PWR_SETTINGS_PWM_OUT,
163 iqs620_pwm->out_en ? 0xff : 0);
164
165err_mutex:
166 mutex_unlock(&iqs620_pwm->lock);152 mutex_unlock(&iqs620_pwm->lock);
167 153
168 if (ret) {154 if (ret) {
@@ -209,12 +195,14 @@ static int iqs620_pwm_probe(struct platform_device *pdev) · 40 unmodified lines
209 ret = regmap_read(iqs62x->regmap, IQS620_PWR_SETTINGS, &val);195 ret = regmap_read(iqs62x->regmap, IQS620_PWR_SETTINGS, &val);
210 if (ret)196 if (ret)
211 return ret;197 return ret;
212 iqs620_pwm->out_en = val & IQS620_PWR_SETTINGS_PWM_OUT;
213 198
214 ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val);199 if (val & IQS620_PWR_SETTINGS_PWM_OUT) {
215 if (ret)200 ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val);
216 return ret;201 if (ret)
217 iqs620_pwm->duty_val = val;202 return ret;
203
204 iqs620_pwm->duty_scale = val + 1;
205 }
218 206
219 iqs620_pwm->chip.dev = &pdev->dev;207 iqs620_pwm->chip.dev = &pdev->dev;
220 iqs620_pwm->chip.ops = &iqs620_pwm_ops;208 iqs620_pwm->chip.ops = &iqs620_pwm_ops;
drivers/pwm/pwm-lpc18xx-sct.c+1−1
@@ -289,7 +289,7 @@ static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) · 288 unmodified lines
289 dev_err(lpc18xx_pwm->dev,289 dev_err(lpc18xx_pwm->dev,
290 "maximum number of simultaneous channels reached\n");290 "maximum number of simultaneous channels reached\n");
291 return -EBUSY;291 return -EBUSY;
292 };292 }
293 293
294 set_bit(event, &lpc18xx_pwm->event_map);294 set_bit(event, &lpc18xx_pwm->event_map);
295 lpc18xx_data->duty_event = event;295 lpc18xx_data->duty_event = event;
drivers/pwm/pwm-rockchip.c+23−9
@@ -72,6 +72,10 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, · 71 unmodified lines
72 if (ret)72 if (ret)
73 return;73 return;
74 74
75 ret = clk_enable(pc->clk);
76 if (ret)
77 return;
78
75 clk_rate = clk_get_rate(pc->clk);79 clk_rate = clk_get_rate(pc->clk);
76 80
77 tmp = readl_relaxed(pc->base + pc->data->regs.period);81 tmp = readl_relaxed(pc->base + pc->data->regs.period);
@@ -90,6 +94,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, · 12 unmodified lines
90 else94 else
91 state->polarity = PWM_POLARITY_NORMAL;95 state->polarity = PWM_POLARITY_NORMAL;
92 96
97 clk_disable(pc->clk);
93 clk_disable(pc->pclk);98 clk_disable(pc->pclk);
94}99}
95 100
@@ -189,6 +194,10 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, · 93 unmodified lines
189 if (ret)194 if (ret)
190 return ret;195 return ret;
191 196
197 ret = clk_enable(pc->clk);
198 if (ret)
199 return ret;
200
192 pwm_get_state(pwm, &curstate);201 pwm_get_state(pwm, &curstate);
193 enabled = curstate.enabled;202 enabled = curstate.enabled;
194 203
@@ -208,6 +217,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, · 13 unmodified lines
208 }217 }
209 218
210out:219out:
220 clk_disable(pc->clk);
211 clk_disable(pc->pclk);221 clk_disable(pc->pclk);
212 222
213 return ret;223 return ret;
@@ -288,6 +298,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) · 74 unmodified lines
288 const struct of_device_id *id;298 const struct of_device_id *id;
289 struct rockchip_pwm_chip *pc;299 struct rockchip_pwm_chip *pc;
290 u32 enable_conf, ctrl;300 u32 enable_conf, ctrl;
301 bool enabled;
291 int ret, count;302 int ret, count;
292 303
293 id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);304 id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
@@ -307,7 +318,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) · 13 unmodified lines
307 pc->clk = devm_clk_get(&pdev->dev, NULL);318 pc->clk = devm_clk_get(&pdev->dev, NULL);
308 if (IS_ERR(pc->clk))319 if (IS_ERR(pc->clk))
309 return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),320 return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
310 "Can't get bus clk\n");321 "Can't get PWM clk\n");
311 }322 }
312 323
313 count = of_count_phandle_with_args(pdev->dev.of_node,324 count = of_count_phandle_with_args(pdev->dev.of_node,
@@ -326,13 +337,13 @@ static int rockchip_pwm_probe(struct platform_device *pdev) · 12 unmodified lines
326 337
327 ret = clk_prepare_enable(pc->clk);338 ret = clk_prepare_enable(pc->clk);
328 if (ret) {339 if (ret) {
329 dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret);340 dev_err(&pdev->dev, "Can't prepare enable PWM clk: %d\n", ret);
330 return ret;341 return ret;
331 }342 }
332 343
333 ret = clk_prepare(pc->pclk);344 ret = clk_prepare_enable(pc->pclk);
334 if (ret) {345 if (ret) {
335 dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret);346 dev_err(&pdev->dev, "Can't prepare enable APB clk: %d\n", ret);
336 goto err_clk;347 goto err_clk;
337 }348 }
338 349
@@ -349,23 +360,26 @@ static int rockchip_pwm_probe(struct platform_device *pdev) · 10 unmodified lines
349 pc->chip.of_pwm_n_cells = 3;360 pc->chip.of_pwm_n_cells = 3;
350 }361 }
351 362
363 enable_conf = pc->data->enable_conf;
364 ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
365 enabled = (ctrl & enable_conf) == enable_conf;
366
352 ret = pwmchip_add(&pc->chip);367 ret = pwmchip_add(&pc->chip);
353 if (ret < 0) {368 if (ret < 0) {
354 clk_unprepare(pc->clk);
355 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);369 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
356 goto err_pclk;370 goto err_pclk;
357 }371 }
358 372
359 /* Keep the PWM clk enabled if the PWM appears to be up and running. */373 /* Keep the PWM clk enabled if the PWM appears to be up and running. */
360 enable_conf = pc->data->enable_conf;374 if (!enabled)
361 ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
362 if ((ctrl & enable_conf) != enable_conf)
363 clk_disable(pc->clk);375 clk_disable(pc->clk);
364 376
377 clk_disable(pc->pclk);
378
365 return 0;379 return 0;
366 380
367err_pclk:381err_pclk:
368 clk_unprepare(pc->pclk);382 clk_disable_unprepare(pc->pclk);
369err_clk:383err_clk:
370 clk_disable_unprepare(pc->clk);384 clk_disable_unprepare(pc->clk);
371 385
drivers/pwm/pwm-zx.c+0−278
@@ -1,278 +0,0 @@
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2017 Sanechips Technology Co., Ltd.
4 * Copyright 2017 Linaro Ltd.
5 */
6
7#include <linux/clk.h>
8#include <linux/err.h>
9#include <linux/io.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/pwm.h>
14#include <linux/slab.h>
15
16#define ZX_PWM_MODE 0x0
17#define ZX_PWM_CLKDIV_SHIFT 2
18#define ZX_PWM_CLKDIV_MASK GENMASK(11, 2)
19#define ZX_PWM_CLKDIV(x) (((x) << ZX_PWM_CLKDIV_SHIFT) & \
20 ZX_PWM_CLKDIV_MASK)
21#define ZX_PWM_POLAR BIT(1)
22#define ZX_PWM_EN BIT(0)
23#define ZX_PWM_PERIOD 0x4
24#define ZX_PWM_DUTY 0x8
25
26#define ZX_PWM_CLKDIV_MAX 1023
27#define ZX_PWM_PERIOD_MAX 65535
28
29struct zx_pwm_chip {
30 struct pwm_chip chip;
31 struct clk *pclk;
32 struct clk *wclk;
33 void __iomem *base;
34};
35
36static inline struct zx_pwm_chip *to_zx_pwm_chip(struct pwm_chip *chip)
37{
38 return container_of(chip, struct zx_pwm_chip, chip);
39}
40
41static inline u32 zx_pwm_readl(struct zx_pwm_chip *zpc, unsigned int hwpwm,
42 unsigned int offset)
43{
44 return readl(zpc->base + (hwpwm + 1) * 0x10 + offset);
45}
46
47static inline void zx_pwm_writel(struct zx_pwm_chip *zpc, unsigned int hwpwm,
48 unsigned int offset, u32 value)
49{
50 writel(value, zpc->base + (hwpwm + 1) * 0x10 + offset);
51}
52
53static void zx_pwm_set_mask(struct zx_pwm_chip *zpc, unsigned int hwpwm,
54 unsigned int offset, u32 mask, u32 value)
55{
56 u32 data;
57
58 data = zx_pwm_readl(zpc, hwpwm, offset);
59 data &= ~mask;
60 data |= value & mask;
61 zx_pwm_writel(zpc, hwpwm, offset, data);
62}
63
64static void zx_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
65 struct pwm_state *state)
66{
67 struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
68 unsigned long rate;
69 unsigned int div;
70 u32 value;
71 u64 tmp;
72
73 value = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_MODE);
74
75 if (value & ZX_PWM_POLAR)
76 state->polarity = PWM_POLARITY_NORMAL;
77 else
78 state->polarity = PWM_POLARITY_INVERSED;
79
80 if (value & ZX_PWM_EN)
81 state->enabled = true;
82 else
83 state->enabled = false;
84
85 div = (value & ZX_PWM_CLKDIV_MASK) >> ZX_PWM_CLKDIV_SHIFT;
86 rate = clk_get_rate(zpc->wclk);
87
88 tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_PERIOD);
89 tmp *= div * NSEC_PER_SEC;
90 state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
91
92 tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_DUTY);
93 tmp *= div * NSEC_PER_SEC;
94 state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
95}
96
97static int zx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
98 unsigned int duty_ns, unsigned int period_ns)
99{
100 struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
101 unsigned int period_cycles, duty_cycles;
102 unsigned long long c;
103 unsigned int div = 1;
104 unsigned long rate;
105
106 /* Find out the best divider */
107 rate = clk_get_rate(zpc->wclk);
108
109 while (1) {
110 c = rate / div;
111 c = c * period_ns;
112 do_div(c, NSEC_PER_SEC);
113
114 if (c < ZX_PWM_PERIOD_MAX)
115 break;
116
117 div++;
118
119 if (div > ZX_PWM_CLKDIV_MAX)
120 return -ERANGE;
121 }
122
123 /* Calculate duty cycles */
124 period_cycles = c;
125 c *= duty_ns;
126 do_div(c, period_ns);
127 duty_cycles = c;
128
129 /*
130 * If the PWM is being enabled, we have to temporarily disable it
131 * before configuring the registers.
132 */
133 if (pwm_is_enabled(pwm))
134 zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_EN, 0);
135
136 /* Set up registers */
137 zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_CLKDIV_MASK,
138 ZX_PWM_CLKDIV(div));
139 zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_PERIOD, period_cycles);
140 zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_DUTY, duty_cycles);
141
142 /* Re-enable the PWM if needed */
143 if (pwm_is_enabled(pwm))
144 zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
145 ZX_PWM_EN, ZX_PWM_EN);
146
147 return 0;
148}
149
150static int zx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
151 const struct pwm_state *state)
152{
153 struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
154 struct pwm_state cstate;
155 int ret;
156
157 pwm_get_state(pwm, &cstate);
158
159 if (state->polarity != cstate.polarity)
160 zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_POLAR,
161 (state->polarity == PWM_POLARITY_INVERSED) ?
162 0 : ZX_PWM_POLAR);
163
164 if (state->period != cstate.period ||
165 state->duty_cycle != cstate.duty_cycle) {
166 ret = zx_pwm_config(chip, pwm, state->duty_cycle,
167 state->period);
168 if (ret)
169 return ret;
170 }
171
172 if (state->enabled != cstate.enabled) {
173 if (state->enabled) {
174 ret = clk_prepare_enable(zpc->wclk);
175 if (ret)
176 return ret;
177
178 zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
179 ZX_PWM_EN, ZX_PWM_EN);
180 } else {
181 zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
182 ZX_PWM_EN, 0);
183 clk_disable_unprepare(zpc->wclk);
184 }
185 }
186
187 return 0;
188}
189
190static const struct pwm_ops zx_pwm_ops = {
191 .apply = zx_pwm_apply,
192 .get_state = zx_pwm_get_state,
193 .owner = THIS_MODULE,
194};
195
196static int zx_pwm_probe(struct platform_device *pdev)
197{
198 struct zx_pwm_chip *zpc;
199 unsigned int i;
200 int ret;
201
202 zpc = devm_kzalloc(&pdev->dev, sizeof(*zpc), GFP_KERNEL);
203 if (!zpc)
204 return -ENOMEM;
205
206 zpc->base = devm_platform_ioremap_resource(pdev, 0);
207 if (IS_ERR(zpc->base))
208 return PTR_ERR(zpc->base);
209
210 zpc->pclk = devm_clk_get(&pdev->dev, "pclk");
211 if (IS_ERR(zpc->pclk))
212 return PTR_ERR(zpc->pclk);
213
214 zpc->wclk = devm_clk_get(&pdev->dev, "wclk");
215 if (IS_ERR(zpc->wclk))
216 return PTR_ERR(zpc->wclk);
217
218 ret = clk_prepare_enable(zpc->pclk);
219 if (ret)
220 return ret;
221
222 zpc->chip.dev = &pdev->dev;
223 zpc->chip.ops = &zx_pwm_ops;
224 zpc->chip.base = -1;
225 zpc->chip.npwm = 4;
226 zpc->chip.of_xlate = of_pwm_xlate_with_flags;
227 zpc->chip.of_pwm_n_cells = 3;
228
229 /*
230 * PWM devices may be enabled by firmware, and let's disable all of
231 * them initially to save power.
232 */
233 for (i = 0; i < zpc->chip.npwm; i++)
234 zx_pwm_set_mask(zpc, i, ZX_PWM_MODE, ZX_PWM_EN, 0);
235
236 ret = pwmchip_add(&zpc->chip);
237 if (ret < 0) {
238 dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
239 clk_disable_unprepare(zpc->pclk);
240 return ret;
241 }
242
243 platform_set_drvdata(pdev, zpc);
244
245 return 0;
246}
247
248static int zx_pwm_remove(struct platform_device *pdev)
249{
250 struct zx_pwm_chip *zpc = platform_get_drvdata(pdev);
251 int ret;
252
253 ret = pwmchip_remove(&zpc->chip);
254 clk_disable_unprepare(zpc->pclk);
255
256 return ret;
257}
258
259static const struct of_device_id zx_pwm_dt_ids[] = {
260 { .compatible = "zte,zx296718-pwm", },
261 { /* sentinel */ }
262};
263MODULE_DEVICE_TABLE(of, zx_pwm_dt_ids);
264
265static struct platform_driver zx_pwm_driver = {
266 .driver = {
267 .name = "zx-pwm",
268 .of_match_table = zx_pwm_dt_ids,
269 },
270 .probe = zx_pwm_probe,
271 .remove = zx_pwm_remove,
272};
273module_platform_driver(zx_pwm_driver);
274
275MODULE_ALIAS("platform:zx-pwm");
276MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
277MODULE_DESCRIPTION("ZTE ZX PWM Driver");
278MODULE_LICENSE("GPL v2");

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t