| 1 | |
| 2 | |
| 3 | * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
|
| 4 | * JZ4740 platform PWM support
|
| 5 | *
|
| 6 | * Limitations:
|
| 7 | * - The .apply callback doesn't complete the currently running period before
|
| 8 | * reconfiguring the hardware.
|
| 9 |
|
| 10 |
|
| 11 | #include <linux/clk.h>
|
| 12 | #include <linux/err.h>
|
| 13 | #include <linux/kernel.h>
|
| 14 | #include <linux/mfd/ingenic-tcu.h>
|
| 15 | #include <linux/mfd/syscon.h>
|
| 16 | #include <linux/module.h>
|
| 17 | #include <linux/of.h>
|
| 18 | #include <linux/platform_device.h>
|
| 19 | #include <linux/pwm.h>
|
| 20 | #include <linux/regmap.h>
|
| 21 |
|
| 22 | struct soc_info {
|
| 23 | unsigned int num_pwms;
|
| 24 | };
|
| 25 |
|
| 26 | struct jz4740_pwm_chip {
|
| 27 | struct regmap *map;
|
| 28 | struct clk *clk[];
|
| 29 | };
|
| 30 |
|
| 31 | static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
|
| 32 | {
|
| 33 | return pwmchip_get_drvdata(chip);
|
| 34 | }
|
| 35 |
|
| 36 | static bool jz4740_pwm_can_use_chn(struct pwm_chip *chip, unsigned int channel)
|
| 37 | {
|
| 38 |
|
| 39 | u32 pwm_channels_mask = GENMASK(chip->npwm - 1, 2);
|
| 40 |
|
| 41 | device_property_read_u32(pwmchip_parent(chip)->parent,
|
| 42 | "ingenic,pwm-channels-mask",
|
| 43 | &pwm_channels_mask);
|
| 44 |
|
| 45 | return !!(pwm_channels_mask & BIT(channel));
|
| 46 | }
|
| 47 |
|
| 48 | static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
| 49 | {
|
| 50 | struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
| 51 | struct clk *clk;
|
| 52 | char name[16];
|
| 53 | int err;
|
| 54 |
|
| 55 | if (!jz4740_pwm_can_use_chn(chip, pwm->hwpwm))
|
| 56 | return -EBUSY;
|
| 57 |
|
| 58 | snprintf(name, sizeof(name), "timer%u", pwm->hwpwm);
|
| 59 |
|
| 60 | clk = clk_get(pwmchip_parent(chip), name);
|
| 61 | if (IS_ERR(clk)) {
|
| 62 | dev_err(pwmchip_parent(chip),
|
| 63 | "error %pe: Failed to get clock\n", clk);
|
| 64 | return PTR_ERR(clk);
|
| 65 | }
|
| 66 |
|
| 67 | err = clk_prepare_enable(clk);
|
| 68 | if (err < 0) {
|
| 69 | clk_put(clk);
|
| 70 | return err;
|
| 71 | }
|
| 72 |
|
| 73 | jz->clk[pwm->hwpwm] = clk;
|
| 74 |
|
| 75 | return 0;
|
| 76 | }
|
| 77 |
|
| 78 | static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
| 79 | {
|
| 80 | struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
| 81 | struct clk *clk = jz->clk[pwm->hwpwm];
|
| 82 |
|
| 83 | clk_disable_unprepare(clk);
|
| 84 | clk_put(clk);
|
| 85 | }
|
| 86 |
|
| 87 | static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
| 88 | {
|
| 89 | struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
| 90 |
|
| 91 |
|
| 92 | regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), TCU_TCSR_PWM_EN);
|
| 93 |
|
| 94 |
|
| 95 | regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
|
| 96 |
|
| 97 | return 0;
|
| 98 | }
|
| 99 |
|
| 100 | static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
| 101 | {
|
| 102 | struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
| 103 |
|
| 104 | |
| 105 | * Set duty > period. This trick allows the TCU channels in TCU2 mode to
|
| 106 | * properly return to their init level.
|
| 107 |
|
| 108 | regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff);
|
| 109 | regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0);
|
| 110 |
|
| 111 | |
| 112 | * Disable PWM output.
|
| 113 | * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
|
| 114 | * counter is stopped, while in TCU1 mode the order does not matter.
|
| 115 |
|
| 116 | regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), TCU_TCSR_PWM_EN);
|
| 117 |
|
| 118 |
|
| 119 | regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm));
|
| 120 | }
|
| 121 |
|
| 122 | static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
| 123 | const struct pwm_state *state)
|
| 124 | {
|
| 125 | struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
| 126 | unsigned long long tmp = 0xffffull * NSEC_PER_SEC;
|
| 127 | struct clk *clk = jz->clk[pwm->hwpwm];
|
| 128 | unsigned long period, duty;
|
| 129 | long rate;
|
| 130 | int err;
|
| 131 |
|
| 132 | |
| 133 | * Limit the clock to a maximum rate that still gives us a period value
|
| 134 | * which fits in 16 bits.
|
| 135 |
|
| 136 | do_div(tmp, state->period);
|
| 137 |
|
| 138 | |
| 139 | * /!\ IMPORTANT NOTE:
|
| 140 | * -------------------
|
| 141 | * This code relies on the fact that clk_round_rate() will always round
|
| 142 | * down, which is not a valid assumption given by the clk API, but only
|
| 143 | * happens to be true with the clk drivers used for Ingenic SoCs.
|
| 144 | *
|
| 145 | * Right now, there is no alternative as the clk API does not have a
|
| 146 | * round-down function (and won't have one for a while), but if it ever
|
| 147 | * comes to light, a round-down function should be used instead.
|
| 148 |
|
| 149 | rate = clk_round_rate(clk, tmp);
|
| 150 | if (rate < 0) {
|
| 151 | dev_err(pwmchip_parent(chip), "Unable to round rate: %ld\n", rate);
|
| 152 | return rate;
|
| 153 | }
|
| 154 |
|
| 155 |
|
| 156 | tmp = (unsigned long long)rate * state->period;
|
| 157 | do_div(tmp, NSEC_PER_SEC);
|
| 158 | period = tmp;
|
| 159 |
|
| 160 |
|
| 161 | tmp = (unsigned long long)rate * state->duty_cycle;
|
| 162 | do_div(tmp, NSEC_PER_SEC);
|
| 163 | duty = tmp;
|
| 164 |
|
| 165 | if (duty >= period)
|
| 166 | duty = period - 1;
|
| 167 |
|
| 168 | jz4740_pwm_disable(chip, pwm);
|
| 169 |
|
| 170 | err = clk_set_rate(clk, rate);
|
| 171 | if (err) {
|
| 172 | dev_err(pwmchip_parent(chip), "Unable to set rate: %d\n", err);
|
| 173 | return err;
|
| 174 | }
|
| 175 |
|
| 176 |
|
| 177 | regmap_write(jz->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
|
| 178 |
|
| 179 |
|
| 180 | regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
|
| 181 |
|
| 182 |
|
| 183 | regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), period);
|
| 184 |
|
| 185 |
|
| 186 | regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
|
| 187 | TCU_TCSR_PWM_SD);
|
| 188 |
|
| 189 | |
| 190 | * Set polarity.
|
| 191 | *
|
| 192 | * The PWM starts in inactive state until the internal timer reaches the
|
| 193 | * duty value, then becomes active until the timer reaches the period
|
| 194 | * value. In theory, we should then use (period - duty) as the real duty
|
| 195 | * value, as a high duty value would otherwise result in the PWM pin
|
| 196 | * being inactive most of the time.
|
| 197 | *
|
| 198 | * Here, we don't do that, and instead invert the polarity of the PWM
|
| 199 | * when it is active. This trick makes the PWM start with its active
|
| 200 | * state instead of its inactive state.
|
| 201 |
|
| 202 | if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
|
| 203 | regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
|
| 204 | TCU_TCSR_PWM_INITL_HIGH);
|
| 205 | else
|
| 206 | regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
|
| 207 | TCU_TCSR_PWM_INITL_HIGH);
|
| 208 |
|
| 209 | if (state->enabled)
|
| 210 | jz4740_pwm_enable(chip, pwm);
|
| 211 |
|
| 212 | return 0;
|
| 213 | }
|
| 214 |
|
| 215 | static const struct pwm_ops jz4740_pwm_ops = {
|
| 216 | .request = jz4740_pwm_request,
|
| 217 | .free = jz4740_pwm_free,
|
| 218 | .apply = jz4740_pwm_apply,
|
| 219 | };
|
| 220 |
|
| 221 | static int jz4740_pwm_probe(struct platform_device *pdev)
|
| 222 | {
|
| 223 | struct device *dev = &pdev->dev;
|
| 224 | struct pwm_chip *chip;
|
| 225 | struct jz4740_pwm_chip *jz;
|
| 226 | const struct soc_info *info;
|
| 227 |
|
| 228 | info = device_get_match_data(dev);
|
| 229 | if (!info)
|
| 230 | return -EINVAL;
|
| 231 |
|
| 232 | chip = devm_pwmchip_alloc(dev, info->num_pwms, struct_size(jz, clk, info->num_pwms));
|
| 233 | if (IS_ERR(chip))
|
| 234 | return PTR_ERR(chip);
|
| 235 | jz = to_jz4740(chip);
|
| 236 |
|
| 237 | jz->map = device_node_to_regmap(dev->parent->of_node);
|
| 238 | if (IS_ERR(jz->map)) {
|
| 239 | dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz->map));
|
| 240 | return PTR_ERR(jz->map);
|
| 241 | }
|
| 242 |
|
| 243 | chip->ops = &jz4740_pwm_ops;
|
| 244 |
|
| 245 | return devm_pwmchip_add(dev, chip);
|
| 246 | }
|
| 247 |
|
| 248 | static const struct soc_info jz4740_soc_info = {
|
| 249 | .num_pwms = 8,
|
| 250 | };
|
| 251 |
|
| 252 | static const struct soc_info jz4725b_soc_info = {
|
| 253 | .num_pwms = 6,
|
| 254 | };
|
| 255 |
|
| 256 | static const struct soc_info x1000_soc_info = {
|
| 257 | .num_pwms = 5,
|
| 258 | };
|
| 259 |
|
| 260 | static const struct of_device_id jz4740_pwm_dt_ids[] = {
|
| 261 | { .compatible = "ingenic,jz4740-pwm", .data = &jz4740_soc_info },
|
| 262 | { .compatible = "ingenic,jz4725b-pwm", .data = &jz4725b_soc_info },
|
| 263 | { .compatible = "ingenic,x1000-pwm", .data = &x1000_soc_info },
|
| 264 | { }
|
| 265 | };
|
| 266 | MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids);
|
| 267 |
|
| 268 | static struct platform_driver jz4740_pwm_driver = {
|
| 269 | .driver = {
|
| 270 | .name = "jz4740-pwm",
|
| 271 | .of_match_table = jz4740_pwm_dt_ids,
|
| 272 | },
|
| 273 | .probe = jz4740_pwm_probe,
|
| 274 | };
|
| 275 | module_platform_driver(jz4740_pwm_driver);
|
| 276 |
|
| 277 | MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
|
| 278 | MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver");
|
| 279 | MODULE_ALIAS("platform:jz4740-pwm");
|
| 280 | MODULE_LICENSE("GPL");
|