pwm-twl.c (9562B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Driver for TWL4030/6030 Generic Pulse Width Modulator 4 * 5 * Copyright (C) 2012 Texas Instruments 6 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 7 */ 8 9#include <linux/module.h> 10#include <linux/of.h> 11#include <linux/platform_device.h> 12#include <linux/pwm.h> 13#include <linux/mfd/twl.h> 14#include <linux/slab.h> 15 16/* 17 * This driver handles the PWMs of TWL4030 and TWL6030. 18 * The TRM names for the PWMs on TWL4030 are: PWM0, PWM1 19 * TWL6030 also have two PWMs named in the TRM as PWM1, PWM2 20 */ 21 22#define TWL_PWM_MAX 0x7f 23 24/* Registers, bits and macro for TWL4030 */ 25#define TWL4030_GPBR1_REG 0x0c 26#define TWL4030_PMBR1_REG 0x0d 27 28/* GPBR1 register bits */ 29#define TWL4030_PWMXCLK_ENABLE (1 << 0) 30#define TWL4030_PWMX_ENABLE (1 << 2) 31#define TWL4030_PWMX_BITS (TWL4030_PWMX_ENABLE | TWL4030_PWMXCLK_ENABLE) 32#define TWL4030_PWM_TOGGLE(pwm, x) ((x) << (pwm)) 33 34/* PMBR1 register bits */ 35#define TWL4030_GPIO6_PWM0_MUTE_MASK (0x03 << 2) 36#define TWL4030_GPIO6_PWM0_MUTE_PWM0 (0x01 << 2) 37#define TWL4030_GPIO7_VIBRASYNC_PWM1_MASK (0x03 << 4) 38#define TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1 (0x03 << 4) 39 40/* Register, bits and macro for TWL6030 */ 41#define TWL6030_TOGGLE3_REG 0x92 42 43#define TWL6030_PWMXR (1 << 0) 44#define TWL6030_PWMXS (1 << 1) 45#define TWL6030_PWMXEN (1 << 2) 46#define TWL6030_PWM_TOGGLE(pwm, x) ((x) << (pwm * 3)) 47 48struct twl_pwm_chip { 49 struct pwm_chip chip; 50 struct mutex mutex; 51 u8 twl6030_toggle3; 52 u8 twl4030_pwm_mux; 53}; 54 55static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip) 56{ 57 return container_of(chip, struct twl_pwm_chip, chip); 58} 59 60static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 61 u64 duty_ns, u64 period_ns) 62{ 63 int duty_cycle = DIV64_U64_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1; 64 u8 pwm_config[2] = { 1, 0 }; 65 int base, ret; 66 67 /* 68 * To configure the duty period: 69 * On-cycle is set to 1 (the minimum allowed value) 70 * The off time of 0 is not configurable, so the mapping is: 71 * 0 -> off cycle = 2, 72 * 1 -> off cycle = 2, 73 * 2 -> off cycle = 3, 74 * 126 - > off cycle 127, 75 * 127 - > off cycle 1 76 * When on cycle == off cycle the PWM will be always on 77 */ 78 if (duty_cycle == 1) 79 duty_cycle = 2; 80 else if (duty_cycle > TWL_PWM_MAX) 81 duty_cycle = 1; 82 83 base = pwm->hwpwm * 3; 84 85 pwm_config[1] = duty_cycle; 86 87 ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2); 88 if (ret < 0) 89 dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); 90 91 return ret; 92} 93 94static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 95{ 96 struct twl_pwm_chip *twl = to_twl(chip); 97 int ret; 98 u8 val; 99 100 mutex_lock(&twl->mutex); 101 ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); 102 if (ret < 0) { 103 dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); 104 goto out; 105 } 106 107 val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); 108 109 ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); 110 if (ret < 0) 111 dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); 112 113 val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); 114 115 ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); 116 if (ret < 0) 117 dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); 118 119out: 120 mutex_unlock(&twl->mutex); 121 return ret; 122} 123 124static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 125{ 126 struct twl_pwm_chip *twl = to_twl(chip); 127 int ret; 128 u8 val; 129 130 mutex_lock(&twl->mutex); 131 ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); 132 if (ret < 0) { 133 dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); 134 goto out; 135 } 136 137 val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); 138 139 ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); 140 if (ret < 0) 141 dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); 142 143 val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); 144 145 ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); 146 if (ret < 0) 147 dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); 148 149out: 150 mutex_unlock(&twl->mutex); 151} 152 153static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 154{ 155 struct twl_pwm_chip *twl = to_twl(chip); 156 int ret; 157 u8 val, mask, bits; 158 159 if (pwm->hwpwm == 1) { 160 mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; 161 bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1; 162 } else { 163 mask = TWL4030_GPIO6_PWM0_MUTE_MASK; 164 bits = TWL4030_GPIO6_PWM0_MUTE_PWM0; 165 } 166 167 mutex_lock(&twl->mutex); 168 ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); 169 if (ret < 0) { 170 dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); 171 goto out; 172 } 173 174 /* Save the current MUX configuration for the PWM */ 175 twl->twl4030_pwm_mux &= ~mask; 176 twl->twl4030_pwm_mux |= (val & mask); 177 178 /* Select PWM functionality */ 179 val &= ~mask; 180 val |= bits; 181 182 ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); 183 if (ret < 0) 184 dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); 185 186out: 187 mutex_unlock(&twl->mutex); 188 return ret; 189} 190 191static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 192{ 193 struct twl_pwm_chip *twl = to_twl(chip); 194 int ret; 195 u8 val, mask; 196 197 if (pwm->hwpwm == 1) 198 mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; 199 else 200 mask = TWL4030_GPIO6_PWM0_MUTE_MASK; 201 202 mutex_lock(&twl->mutex); 203 ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); 204 if (ret < 0) { 205 dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); 206 goto out; 207 } 208 209 /* Restore the MUX configuration for the PWM */ 210 val &= ~mask; 211 val |= (twl->twl4030_pwm_mux & mask); 212 213 ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); 214 if (ret < 0) 215 dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); 216 217out: 218 mutex_unlock(&twl->mutex); 219} 220 221static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 222{ 223 struct twl_pwm_chip *twl = to_twl(chip); 224 int ret; 225 u8 val; 226 227 mutex_lock(&twl->mutex); 228 val = twl->twl6030_toggle3; 229 val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); 230 val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); 231 232 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); 233 if (ret < 0) { 234 dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); 235 goto out; 236 } 237 238 twl->twl6030_toggle3 = val; 239out: 240 mutex_unlock(&twl->mutex); 241 return ret; 242} 243 244static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 245{ 246 struct twl_pwm_chip *twl = to_twl(chip); 247 int ret; 248 u8 val; 249 250 mutex_lock(&twl->mutex); 251 val = twl->twl6030_toggle3; 252 val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); 253 val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); 254 255 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); 256 if (ret < 0) { 257 dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); 258 goto out; 259 } 260 261 val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN); 262 263 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); 264 if (ret < 0) { 265 dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); 266 goto out; 267 } 268 269 val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN); 270 271 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); 272 if (ret < 0) { 273 dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); 274 goto out; 275 } 276 277 twl->twl6030_toggle3 = val; 278out: 279 mutex_unlock(&twl->mutex); 280} 281 282static int twl4030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 283 const struct pwm_state *state) 284{ 285 int err; 286 287 if (state->polarity != PWM_POLARITY_NORMAL) 288 return -EINVAL; 289 290 if (!state->enabled) { 291 if (pwm->state.enabled) 292 twl4030_pwm_disable(chip, pwm); 293 294 return 0; 295 } 296 297 err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); 298 if (err) 299 return err; 300 301 if (!pwm->state.enabled) 302 err = twl4030_pwm_enable(chip, pwm); 303 304 return err; 305} 306 307static int twl6030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 308 const struct pwm_state *state) 309{ 310 int err; 311 312 if (state->polarity != PWM_POLARITY_NORMAL) 313 return -EINVAL; 314 315 if (!state->enabled) { 316 if (pwm->state.enabled) 317 twl6030_pwm_disable(chip, pwm); 318 319 return 0; 320 } 321 322 err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); 323 if (err) 324 return err; 325 326 if (!pwm->state.enabled) 327 err = twl6030_pwm_enable(chip, pwm); 328 329 return err; 330} 331 332static const struct pwm_ops twl4030_pwm_ops = { 333 .apply = twl4030_pwm_apply, 334 .request = twl4030_pwm_request, 335 .free = twl4030_pwm_free, 336 .owner = THIS_MODULE, 337}; 338 339static const struct pwm_ops twl6030_pwm_ops = { 340 .apply = twl6030_pwm_apply, 341 .owner = THIS_MODULE, 342}; 343 344static int twl_pwm_probe(struct platform_device *pdev) 345{ 346 struct twl_pwm_chip *twl; 347 348 twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); 349 if (!twl) 350 return -ENOMEM; 351 352 if (twl_class_is_4030()) 353 twl->chip.ops = &twl4030_pwm_ops; 354 else 355 twl->chip.ops = &twl6030_pwm_ops; 356 357 twl->chip.dev = &pdev->dev; 358 twl->chip.npwm = 2; 359 360 mutex_init(&twl->mutex); 361 362 return devm_pwmchip_add(&pdev->dev, &twl->chip); 363} 364 365#ifdef CONFIG_OF 366static const struct of_device_id twl_pwm_of_match[] = { 367 { .compatible = "ti,twl4030-pwm" }, 368 { .compatible = "ti,twl6030-pwm" }, 369 { }, 370}; 371MODULE_DEVICE_TABLE(of, twl_pwm_of_match); 372#endif 373 374static struct platform_driver twl_pwm_driver = { 375 .driver = { 376 .name = "twl-pwm", 377 .of_match_table = of_match_ptr(twl_pwm_of_match), 378 }, 379 .probe = twl_pwm_probe, 380}; 381module_platform_driver(twl_pwm_driver); 382 383MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 384MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030"); 385MODULE_ALIAS("platform:twl-pwm"); 386MODULE_LICENSE("GPL");