blob: 3cd5c054ad9aff82c7f8b7465419d9682fbce753 [file] [log] [blame]
Thomas Gleixnera912e802019-05-27 08:55:00 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Thierry Redingf6b8a572012-08-22 10:01:24 +02002/*
3 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
4 * JZ4740 platform PWM support
Uwe Kleine-König3b442c62019-07-30 14:32:29 +02005 *
6 * Limitations:
7 * - The .apply callback doesn't complete the currently running period before
8 * reconfiguring the hardware.
9 * - Each period starts with the inactive part.
Thierry Redingf6b8a572012-08-22 10:01:24 +020010 */
11
12#include <linux/clk.h>
13#include <linux/err.h>
14#include <linux/gpio.h>
15#include <linux/kernel.h>
Paul Cercueilc2693512020-03-23 15:24:20 +010016#include <linux/mfd/ingenic-tcu.h>
17#include <linux/mfd/syscon.h>
Thierry Redingf6b8a572012-08-22 10:01:24 +020018#include <linux/module.h>
Paul Cercueilcc201732018-01-06 17:58:42 +010019#include <linux/of_device.h>
Thierry Redingf6b8a572012-08-22 10:01:24 +020020#include <linux/platform_device.h>
21#include <linux/pwm.h>
Paul Cercueilc2693512020-03-23 15:24:20 +010022#include <linux/regmap.h>
Thierry Redingf6b8a572012-08-22 10:01:24 +020023
24#define NUM_PWM 8
25
Thierry Redingf6b8a572012-08-22 10:01:24 +020026struct jz4740_pwm_chip {
27 struct pwm_chip chip;
Paul Cercueilc2693512020-03-23 15:24:20 +010028 struct regmap *map;
Thierry Redingf6b8a572012-08-22 10:01:24 +020029};
30
31static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
32{
33 return container_of(chip, struct jz4740_pwm_chip, chip);
34}
35
Paul Cercueila2005fc2020-03-23 15:24:21 +010036static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz,
37 unsigned int channel)
38{
39 /* Enable all TCU channels for PWM use by default except channels 0/1 */
40 u32 pwm_channels_mask = GENMASK(NUM_PWM - 1, 2);
41
42 device_property_read_u32(jz->chip.dev->parent,
43 "ingenic,pwm-channels-mask",
44 &pwm_channels_mask);
45
46 return !!(pwm_channels_mask & BIT(channel));
47}
48
Thierry Redingf6b8a572012-08-22 10:01:24 +020049static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
50{
Paul Cercueilce1f9ce2020-03-23 15:24:18 +010051 struct jz4740_pwm_chip *jz = to_jz4740(chip);
52 struct clk *clk;
53 char name[16];
54 int err;
55
Paul Cercueila2005fc2020-03-23 15:24:21 +010056 if (!jz4740_pwm_can_use_chn(jz, pwm->hwpwm))
Thierry Redingf6b8a572012-08-22 10:01:24 +020057 return -EBUSY;
58
Paul Cercueilce1f9ce2020-03-23 15:24:18 +010059 snprintf(name, sizeof(name), "timer%u", pwm->hwpwm);
60
61 clk = clk_get(chip->dev, name);
62 if (IS_ERR(clk)) {
63 if (PTR_ERR(clk) != -EPROBE_DEFER)
64 dev_err(chip->dev, "Failed to get clock: %pe", clk);
65
66 return PTR_ERR(clk);
67 }
68
69 err = clk_prepare_enable(clk);
70 if (err < 0) {
71 clk_put(clk);
72 return err;
73 }
74
75 pwm_set_chip_data(pwm, clk);
Thierry Redingf6b8a572012-08-22 10:01:24 +020076
77 return 0;
78}
79
80static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
81{
Paul Cercueilce1f9ce2020-03-23 15:24:18 +010082 struct clk *clk = pwm_get_chip_data(pwm);
Thierry Redingf6b8a572012-08-22 10:01:24 +020083
Paul Cercueilce1f9ce2020-03-23 15:24:18 +010084 clk_disable_unprepare(clk);
85 clk_put(clk);
Thierry Redingf6b8a572012-08-22 10:01:24 +020086}
87
88static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
89{
Paul Cercueilc2693512020-03-23 15:24:20 +010090 struct jz4740_pwm_chip *jz = to_jz4740(chip);
Thierry Redingf6b8a572012-08-22 10:01:24 +020091
Paul Cercueilc2693512020-03-23 15:24:20 +010092 /* Enable PWM output */
93 regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
94 TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN);
95
96 /* Start counter */
97 regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
Thierry Redingf6b8a572012-08-22 10:01:24 +020098
99 return 0;
100}
101
102static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
103{
Paul Cercueilc2693512020-03-23 15:24:20 +0100104 struct jz4740_pwm_chip *jz = to_jz4740(chip);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200105
Paul Cercueil6580fd12019-06-07 17:44:09 +0200106 /*
107 * Set duty > period. This trick allows the TCU channels in TCU2 mode to
108 * properly return to their init level.
109 */
Paul Cercueilc2693512020-03-23 15:24:20 +0100110 regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff);
111 regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0);
Paul Cercueil6580fd12019-06-07 17:44:09 +0200112
113 /*
114 * Disable PWM output.
Maarten ter Huurnedf56b172018-01-06 17:58:40 +0100115 * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
116 * counter is stopped, while in TCU1 mode the order does not matter.
117 */
Paul Cercueilc2693512020-03-23 15:24:20 +0100118 regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
119 TCU_TCSR_PWM_EN, 0);
Maarten ter Huurnedf56b172018-01-06 17:58:40 +0100120
121 /* Stop counter */
Paul Cercueilc2693512020-03-23 15:24:20 +0100122 regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm));
Thierry Redingf6b8a572012-08-22 10:01:24 +0200123}
124
Paul Cercueil1ac99c52019-06-07 17:44:07 +0200125static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
Uwe Kleine-König71523d12019-08-24 17:37:07 +0200126 const struct pwm_state *state)
Thierry Redingf6b8a572012-08-22 10:01:24 +0200127{
128 struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
Paul Cercueil485b56f2020-03-23 15:24:19 +0100129 unsigned long long tmp = 0xffffull * NSEC_PER_SEC;
130 struct clk *clk = pwm_get_chip_data(pwm);
131 unsigned long period, duty;
Paul Cercueil485b56f2020-03-23 15:24:19 +0100132 long rate;
Paul Cercueilce1f9ce2020-03-23 15:24:18 +0100133 int err;
Thierry Redingf6b8a572012-08-22 10:01:24 +0200134
Paul Cercueil485b56f2020-03-23 15:24:19 +0100135 /*
136 * Limit the clock to a maximum rate that still gives us a period value
137 * which fits in 16 bits.
138 */
139 do_div(tmp, state->period);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200140
Paul Cercueil485b56f2020-03-23 15:24:19 +0100141 /*
142 * /!\ IMPORTANT NOTE:
143 * -------------------
144 * This code relies on the fact that clk_round_rate() will always round
145 * down, which is not a valid assumption given by the clk API, but only
146 * happens to be true with the clk drivers used for Ingenic SoCs.
147 *
148 * Right now, there is no alternative as the clk API does not have a
149 * round-down function (and won't have one for a while), but if it ever
150 * comes to light, a round-down function should be used instead.
151 */
152 rate = clk_round_rate(clk, tmp);
153 if (rate < 0) {
154 dev_err(chip->dev, "Unable to round rate: %ld", rate);
155 return rate;
Thierry Redingf6b8a572012-08-22 10:01:24 +0200156 }
157
Paul Cercueil485b56f2020-03-23 15:24:19 +0100158 /* Calculate period value */
159 tmp = (unsigned long long)rate * state->period;
160 do_div(tmp, NSEC_PER_SEC);
161 period = (unsigned long)tmp;
Thierry Redingf6b8a572012-08-22 10:01:24 +0200162
Paul Cercueil485b56f2020-03-23 15:24:19 +0100163 /* Calculate duty value */
Paul Cercueil1ac99c52019-06-07 17:44:07 +0200164 tmp = (unsigned long long)period * state->duty_cycle;
165 do_div(tmp, state->period);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200166 duty = period - tmp;
167
168 if (duty >= period)
169 duty = period - 1;
170
Paul Cercueil1ac99c52019-06-07 17:44:07 +0200171 jz4740_pwm_disable(chip, pwm);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200172
Paul Cercueilce1f9ce2020-03-23 15:24:18 +0100173 err = clk_set_rate(clk, rate);
174 if (err) {
175 dev_err(chip->dev, "Unable to set rate: %d", err);
176 return err;
177 }
178
Paul Cercueilc2693512020-03-23 15:24:20 +0100179 /* Reset counter to 0 */
180 regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200181
Paul Cercueilc2693512020-03-23 15:24:20 +0100182 /* Set duty */
183 regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200184
Paul Cercueilc2693512020-03-23 15:24:20 +0100185 /* Set period */
186 regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);
187
188 /* Set abrupt shutdown */
189 regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
190 TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
191
192 /* Set polarity */
Paul Cercueil1ac99c52019-06-07 17:44:07 +0200193 switch (state->polarity) {
Paul Cercueil174dcc82018-01-06 17:58:41 +0100194 case PWM_POLARITY_NORMAL:
Paul Cercueilc2693512020-03-23 15:24:20 +0100195 regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
196 TCU_TCSR_PWM_INITL_HIGH, 0);
Paul Cercueil174dcc82018-01-06 17:58:41 +0100197 break;
198 case PWM_POLARITY_INVERSED:
Paul Cercueilc2693512020-03-23 15:24:20 +0100199 regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
200 TCU_TCSR_PWM_INITL_HIGH,
201 TCU_TCSR_PWM_INITL_HIGH);
Paul Cercueil174dcc82018-01-06 17:58:41 +0100202 break;
203 }
204
Paul Cercueil1ac99c52019-06-07 17:44:07 +0200205 if (state->enabled)
206 jz4740_pwm_enable(chip, pwm);
207
Paul Cercueil174dcc82018-01-06 17:58:41 +0100208 return 0;
209}
210
Thierry Redingf6b8a572012-08-22 10:01:24 +0200211static const struct pwm_ops jz4740_pwm_ops = {
212 .request = jz4740_pwm_request,
213 .free = jz4740_pwm_free,
Paul Cercueil1ac99c52019-06-07 17:44:07 +0200214 .apply = jz4740_pwm_apply,
Thierry Redingf6b8a572012-08-22 10:01:24 +0200215 .owner = THIS_MODULE,
216};
217
Bill Pemberton3e9fe832012-11-19 13:23:14 -0500218static int jz4740_pwm_probe(struct platform_device *pdev)
Thierry Redingf6b8a572012-08-22 10:01:24 +0200219{
Paul Cercueilc2693512020-03-23 15:24:20 +0100220 struct device *dev = &pdev->dev;
Thierry Redingf6b8a572012-08-22 10:01:24 +0200221 struct jz4740_pwm_chip *jz4740;
Thierry Redingf6b8a572012-08-22 10:01:24 +0200222
Paul Cercueilc2693512020-03-23 15:24:20 +0100223 jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200224 if (!jz4740)
225 return -ENOMEM;
226
Paul Cercueilc2693512020-03-23 15:24:20 +0100227 jz4740->map = device_node_to_regmap(dev->parent->of_node);
228 if (IS_ERR(jz4740->map)) {
229 dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz4740->map));
230 return PTR_ERR(jz4740->map);
231 }
232
233 jz4740->chip.dev = dev;
Thierry Redingf6b8a572012-08-22 10:01:24 +0200234 jz4740->chip.ops = &jz4740_pwm_ops;
235 jz4740->chip.npwm = NUM_PWM;
236 jz4740->chip.base = -1;
Paul Cercueilcc201732018-01-06 17:58:42 +0100237 jz4740->chip.of_xlate = of_pwm_xlate_with_flags;
238 jz4740->chip.of_pwm_n_cells = 3;
Thierry Redingf6b8a572012-08-22 10:01:24 +0200239
Thierry Redingf6b8a572012-08-22 10:01:24 +0200240 platform_set_drvdata(pdev, jz4740);
241
Lars-Peter Clausen0dc11352013-12-07 18:13:16 +0100242 return pwmchip_add(&jz4740->chip);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200243}
244
Bill Pemberton77f37912012-11-19 13:26:09 -0500245static int jz4740_pwm_remove(struct platform_device *pdev)
Thierry Redingf6b8a572012-08-22 10:01:24 +0200246{
247 struct jz4740_pwm_chip *jz4740 = platform_get_drvdata(pdev);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200248
Lars-Peter Clausen0dc11352013-12-07 18:13:16 +0100249 return pwmchip_remove(&jz4740->chip);
Thierry Redingf6b8a572012-08-22 10:01:24 +0200250}
251
Paul Cercueilcc201732018-01-06 17:58:42 +0100252#ifdef CONFIG_OF
253static const struct of_device_id jz4740_pwm_dt_ids[] = {
254 { .compatible = "ingenic,jz4740-pwm", },
Paul Cercueilcc201732018-01-06 17:58:42 +0100255 {},
256};
257MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids);
258#endif
259
Thierry Redingf6b8a572012-08-22 10:01:24 +0200260static struct platform_driver jz4740_pwm_driver = {
261 .driver = {
262 .name = "jz4740-pwm",
Paul Cercueilcc201732018-01-06 17:58:42 +0100263 .of_match_table = of_match_ptr(jz4740_pwm_dt_ids),
Thierry Redingf6b8a572012-08-22 10:01:24 +0200264 },
265 .probe = jz4740_pwm_probe,
Bill Pembertonfd109112012-11-19 13:21:28 -0500266 .remove = jz4740_pwm_remove,
Thierry Redingf6b8a572012-08-22 10:01:24 +0200267};
268module_platform_driver(jz4740_pwm_driver);
269
270MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
271MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver");
272MODULE_ALIAS("platform:jz4740-pwm");
273MODULE_LICENSE("GPL");