Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pwm: Add Renesas TPU PWM driver

The Timer Pulse Unit (TPU) is a 4-channels 16-bit timer used to generate
waveforms. This driver exposes PWM functions through the PWM API for
other drivers to use.

The code is loosely based on the leds-renesas-tpu driver by Magnus Damm
and the TPU PWM driver shipped in the Armadillo EVA 800 kernel sources.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Axel Lin <axel.lin@ingics.com>
Tested-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>

authored by

Laurent Pinchart and committed by
Thierry Reding
99b82abb 76abbdde

+502
+10
drivers/pwm/Kconfig
··· 128 128 To compile this driver as a module, choose M here: the module 129 129 will be called pwm-pxa. 130 130 131 + config PWM_RENESAS_TPU 132 + tristate "Renesas TPU PWM support" 133 + depends on ARCH_SHMOBILE 134 + help 135 + This driver exposes the Timer Pulse Unit (TPU) PWM controller found 136 + in Renesas chips through the PWM API. 137 + 138 + To compile this driver as a module, choose M here: the module 139 + will be called pwm-renesas-tpu. 140 + 131 141 config PWM_SAMSUNG 132 142 tristate "Samsung PWM support" 133 143 depends on PLAT_SAMSUNG
+1
drivers/pwm/Makefile
··· 10 10 obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o 11 11 obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o 12 12 obj-$(CONFIG_PWM_PXA) += pwm-pxa.o 13 + obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o 13 14 obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o 14 15 obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o 15 16 obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
+475
drivers/pwm/pwm-renesas-tpu.c
··· 1 + /* 2 + * R-Mobile TPU PWM driver 3 + * 4 + * Copyright (C) 2012 Renesas Solutions Corp. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + */ 15 + 16 + #include <linux/clk.h> 17 + #include <linux/err.h> 18 + #include <linux/io.h> 19 + #include <linux/init.h> 20 + #include <linux/ioport.h> 21 + #include <linux/module.h> 22 + #include <linux/mutex.h> 23 + #include <linux/platform_data/pwm-renesas-tpu.h> 24 + #include <linux/platform_device.h> 25 + #include <linux/pm_runtime.h> 26 + #include <linux/pwm.h> 27 + #include <linux/slab.h> 28 + #include <linux/spinlock.h> 29 + 30 + #define TPU_TSTR 0x00 /* Timer start register (shared) */ 31 + 32 + #define TPU_TCRn 0x00 /* Timer control register */ 33 + #define TPU_TCR_CCLR_NONE (0 << 5) 34 + #define TPU_TCR_CCLR_TGRA (1 << 5) 35 + #define TPU_TCR_CCLR_TGRB (2 << 5) 36 + #define TPU_TCR_CCLR_TGRC (5 << 5) 37 + #define TPU_TCR_CCLR_TGRD (6 << 5) 38 + #define TPU_TCR_CKEG_RISING (0 << 3) 39 + #define TPU_TCR_CKEG_FALLING (1 << 3) 40 + #define TPU_TCR_CKEG_BOTH (2 << 3) 41 + #define TPU_TMDRn 0x04 /* Timer mode register */ 42 + #define TPU_TMDR_BFWT (1 << 6) 43 + #define TPU_TMDR_BFB (1 << 5) 44 + #define TPU_TMDR_BFA (1 << 4) 45 + #define TPU_TMDR_MD_NORMAL (0 << 0) 46 + #define TPU_TMDR_MD_PWM (2 << 0) 47 + #define TPU_TIORn 0x08 /* Timer I/O control register */ 48 + #define TPU_TIOR_IOA_0 (0 << 0) 49 + #define TPU_TIOR_IOA_0_CLR (1 << 0) 50 + #define TPU_TIOR_IOA_0_SET (2 << 0) 51 + #define TPU_TIOR_IOA_0_TOGGLE (3 << 0) 52 + #define TPU_TIOR_IOA_1 (4 << 0) 53 + #define TPU_TIOR_IOA_1_CLR (5 << 0) 54 + #define TPU_TIOR_IOA_1_SET (6 << 0) 55 + #define TPU_TIOR_IOA_1_TOGGLE (7 << 0) 56 + #define TPU_TIERn 0x0c /* Timer interrupt enable register */ 57 + #define TPU_TSRn 0x10 /* Timer status register */ 58 + #define TPU_TCNTn 0x14 /* Timer counter */ 59 + #define TPU_TGRAn 0x18 /* Timer general register A */ 60 + #define TPU_TGRBn 0x1c /* Timer general register B */ 61 + #define TPU_TGRCn 0x20 /* Timer general register C */ 62 + #define TPU_TGRDn 0x24 /* Timer general register D */ 63 + 64 + #define TPU_CHANNEL_OFFSET 0x10 65 + #define TPU_CHANNEL_SIZE 0x40 66 + 67 + enum tpu_pin_state { 68 + TPU_PIN_INACTIVE, /* Pin is driven inactive */ 69 + TPU_PIN_PWM, /* Pin is driven by PWM */ 70 + TPU_PIN_ACTIVE, /* Pin is driven active */ 71 + }; 72 + 73 + struct tpu_device; 74 + 75 + struct tpu_pwm_device { 76 + bool timer_on; /* Whether the timer is running */ 77 + 78 + struct tpu_device *tpu; 79 + unsigned int channel; /* Channel number in the TPU */ 80 + 81 + enum pwm_polarity polarity; 82 + unsigned int prescaler; 83 + u16 period; 84 + u16 duty; 85 + }; 86 + 87 + struct tpu_device { 88 + struct platform_device *pdev; 89 + struct tpu_pwm_platform_data *pdata; 90 + struct pwm_chip chip; 91 + spinlock_t lock; 92 + 93 + void __iomem *base; 94 + struct clk *clk; 95 + }; 96 + 97 + #define to_tpu_device(c) container_of(c, struct tpu_device, chip) 98 + 99 + static void tpu_pwm_write(struct tpu_pwm_device *pwm, int reg_nr, u16 value) 100 + { 101 + void __iomem *base = pwm->tpu->base + TPU_CHANNEL_OFFSET 102 + + pwm->channel * TPU_CHANNEL_SIZE; 103 + 104 + iowrite16(value, base + reg_nr); 105 + } 106 + 107 + static void tpu_pwm_set_pin(struct tpu_pwm_device *pwm, 108 + enum tpu_pin_state state) 109 + { 110 + static const char * const states[] = { "inactive", "PWM", "active" }; 111 + 112 + dev_dbg(&pwm->tpu->pdev->dev, "%u: configuring pin as %s\n", 113 + pwm->channel, states[state]); 114 + 115 + switch (state) { 116 + case TPU_PIN_INACTIVE: 117 + tpu_pwm_write(pwm, TPU_TIORn, 118 + pwm->polarity == PWM_POLARITY_INVERSED ? 119 + TPU_TIOR_IOA_1 : TPU_TIOR_IOA_0); 120 + break; 121 + case TPU_PIN_PWM: 122 + tpu_pwm_write(pwm, TPU_TIORn, 123 + pwm->polarity == PWM_POLARITY_INVERSED ? 124 + TPU_TIOR_IOA_0_SET : TPU_TIOR_IOA_1_CLR); 125 + break; 126 + case TPU_PIN_ACTIVE: 127 + tpu_pwm_write(pwm, TPU_TIORn, 128 + pwm->polarity == PWM_POLARITY_INVERSED ? 129 + TPU_TIOR_IOA_0 : TPU_TIOR_IOA_1); 130 + break; 131 + } 132 + } 133 + 134 + static void tpu_pwm_start_stop(struct tpu_pwm_device *pwm, int start) 135 + { 136 + unsigned long flags; 137 + u16 value; 138 + 139 + spin_lock_irqsave(&pwm->tpu->lock, flags); 140 + value = ioread16(pwm->tpu->base + TPU_TSTR); 141 + 142 + if (start) 143 + value |= 1 << pwm->channel; 144 + else 145 + value &= ~(1 << pwm->channel); 146 + 147 + iowrite16(value, pwm->tpu->base + TPU_TSTR); 148 + spin_unlock_irqrestore(&pwm->tpu->lock, flags); 149 + } 150 + 151 + static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm) 152 + { 153 + int ret; 154 + 155 + if (!pwm->timer_on) { 156 + /* Wake up device and enable clock. */ 157 + pm_runtime_get_sync(&pwm->tpu->pdev->dev); 158 + ret = clk_prepare_enable(pwm->tpu->clk); 159 + if (ret) { 160 + dev_err(&pwm->tpu->pdev->dev, "cannot enable clock\n"); 161 + return ret; 162 + } 163 + pwm->timer_on = true; 164 + } 165 + 166 + /* 167 + * Make sure the channel is stopped, as we need to reconfigure it 168 + * completely. First drive the pin to the inactive state to avoid 169 + * glitches. 170 + */ 171 + tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE); 172 + tpu_pwm_start_stop(pwm, false); 173 + 174 + /* 175 + * - Clear TCNT on TGRB match 176 + * - Count on rising edge 177 + * - Set prescaler 178 + * - Output 0 until TGRA, output 1 until TGRB (active low polarity) 179 + * - Output 1 until TGRA, output 0 until TGRB (active high polarity 180 + * - PWM mode 181 + */ 182 + tpu_pwm_write(pwm, TPU_TCRn, TPU_TCR_CCLR_TGRB | TPU_TCR_CKEG_RISING | 183 + pwm->prescaler); 184 + tpu_pwm_write(pwm, TPU_TMDRn, TPU_TMDR_MD_PWM); 185 + tpu_pwm_set_pin(pwm, TPU_PIN_PWM); 186 + tpu_pwm_write(pwm, TPU_TGRAn, pwm->duty); 187 + tpu_pwm_write(pwm, TPU_TGRBn, pwm->period); 188 + 189 + dev_dbg(&pwm->tpu->pdev->dev, "%u: TGRA 0x%04x TGRB 0x%04x\n", 190 + pwm->channel, pwm->duty, pwm->period); 191 + 192 + /* Start the channel. */ 193 + tpu_pwm_start_stop(pwm, true); 194 + 195 + return 0; 196 + } 197 + 198 + static void tpu_pwm_timer_stop(struct tpu_pwm_device *pwm) 199 + { 200 + if (!pwm->timer_on) 201 + return; 202 + 203 + /* Disable channel. */ 204 + tpu_pwm_start_stop(pwm, false); 205 + 206 + /* Stop clock and mark device as idle. */ 207 + clk_disable_unprepare(pwm->tpu->clk); 208 + pm_runtime_put(&pwm->tpu->pdev->dev); 209 + 210 + pwm->timer_on = false; 211 + } 212 + 213 + /* ----------------------------------------------------------------------------- 214 + * PWM API 215 + */ 216 + 217 + static int tpu_pwm_request(struct pwm_chip *chip, struct pwm_device *_pwm) 218 + { 219 + struct tpu_device *tpu = to_tpu_device(chip); 220 + struct tpu_pwm_device *pwm; 221 + 222 + if (_pwm->hwpwm >= TPU_CHANNEL_MAX) 223 + return -EINVAL; 224 + 225 + pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); 226 + if (pwm == NULL) 227 + return -ENOMEM; 228 + 229 + pwm->tpu = tpu; 230 + pwm->channel = _pwm->hwpwm; 231 + pwm->polarity = tpu->pdata ? tpu->pdata->channels[pwm->channel].polarity 232 + : PWM_POLARITY_NORMAL; 233 + pwm->prescaler = 0; 234 + pwm->period = 0; 235 + pwm->duty = 0; 236 + 237 + pwm->timer_on = false; 238 + 239 + pwm_set_chip_data(_pwm, pwm); 240 + 241 + return 0; 242 + } 243 + 244 + static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *_pwm) 245 + { 246 + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); 247 + 248 + tpu_pwm_timer_stop(pwm); 249 + kfree(pwm); 250 + } 251 + 252 + static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm, 253 + int duty_ns, int period_ns) 254 + { 255 + static const unsigned int prescalers[] = { 1, 4, 16, 64 }; 256 + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); 257 + struct tpu_device *tpu = to_tpu_device(chip); 258 + unsigned int prescaler; 259 + bool duty_only = false; 260 + u32 clk_rate; 261 + u32 period; 262 + u32 duty; 263 + int ret; 264 + 265 + /* 266 + * Pick a prescaler to avoid overflowing the counter. 267 + * TODO: Pick the highest acceptable prescaler. 268 + */ 269 + clk_rate = clk_get_rate(tpu->clk); 270 + 271 + for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) { 272 + period = clk_rate / prescalers[prescaler] 273 + / (NSEC_PER_SEC / period_ns); 274 + if (period <= 0xffff) 275 + break; 276 + } 277 + 278 + if (prescaler == ARRAY_SIZE(prescalers) || period == 0) { 279 + dev_err(&tpu->pdev->dev, "clock rate mismatch\n"); 280 + return -ENOTSUPP; 281 + } 282 + 283 + if (duty_ns) { 284 + duty = clk_rate / prescalers[prescaler] 285 + / (NSEC_PER_SEC / duty_ns); 286 + if (duty > period) 287 + return -EINVAL; 288 + } else { 289 + duty = 0; 290 + } 291 + 292 + dev_dbg(&tpu->pdev->dev, 293 + "rate %u, prescaler %u, period %u, duty %u\n", 294 + clk_rate, prescalers[prescaler], period, duty); 295 + 296 + if (pwm->prescaler == prescaler && pwm->period == period) 297 + duty_only = true; 298 + 299 + pwm->prescaler = prescaler; 300 + pwm->period = period; 301 + pwm->duty = duty; 302 + 303 + /* If the channel is disabled we're done. */ 304 + if (!test_bit(PWMF_ENABLED, &_pwm->flags)) 305 + return 0; 306 + 307 + if (duty_only && pwm->timer_on) { 308 + /* 309 + * If only the duty cycle changed and the timer is already 310 + * running, there's no need to reconfigure it completely, Just 311 + * modify the duty cycle. 312 + */ 313 + tpu_pwm_write(pwm, TPU_TGRAn, pwm->duty); 314 + dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", pwm->channel, 315 + pwm->duty); 316 + } else { 317 + /* Otherwise perform a full reconfiguration. */ 318 + ret = tpu_pwm_timer_start(pwm); 319 + if (ret < 0) 320 + return ret; 321 + } 322 + 323 + if (duty == 0 || duty == period) { 324 + /* 325 + * To avoid running the timer when not strictly required, handle 326 + * 0% and 100% duty cycles as fixed levels and stop the timer. 327 + */ 328 + tpu_pwm_set_pin(pwm, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE); 329 + tpu_pwm_timer_stop(pwm); 330 + } 331 + 332 + return 0; 333 + } 334 + 335 + static int tpu_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *_pwm, 336 + enum pwm_polarity polarity) 337 + { 338 + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); 339 + 340 + pwm->polarity = polarity; 341 + 342 + return 0; 343 + } 344 + 345 + static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *_pwm) 346 + { 347 + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); 348 + int ret; 349 + 350 + ret = tpu_pwm_timer_start(pwm); 351 + if (ret < 0) 352 + return ret; 353 + 354 + /* 355 + * To avoid running the timer when not strictly required, handle 0% and 356 + * 100% duty cycles as fixed levels and stop the timer. 357 + */ 358 + if (pwm->duty == 0 || pwm->duty == pwm->period) { 359 + tpu_pwm_set_pin(pwm, pwm->duty ? 360 + TPU_PIN_ACTIVE : TPU_PIN_INACTIVE); 361 + tpu_pwm_timer_stop(pwm); 362 + } 363 + 364 + return 0; 365 + } 366 + 367 + static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *_pwm) 368 + { 369 + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); 370 + 371 + /* The timer must be running to modify the pin output configuration. */ 372 + tpu_pwm_timer_start(pwm); 373 + tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE); 374 + tpu_pwm_timer_stop(pwm); 375 + } 376 + 377 + static const struct pwm_ops tpu_pwm_ops = { 378 + .request = tpu_pwm_request, 379 + .free = tpu_pwm_free, 380 + .config = tpu_pwm_config, 381 + .set_polarity = tpu_pwm_set_polarity, 382 + .enable = tpu_pwm_enable, 383 + .disable = tpu_pwm_disable, 384 + .owner = THIS_MODULE, 385 + }; 386 + 387 + /* ----------------------------------------------------------------------------- 388 + * Probe and remove 389 + */ 390 + 391 + static int tpu_probe(struct platform_device *pdev) 392 + { 393 + struct tpu_device *tpu; 394 + struct resource *res; 395 + int ret; 396 + 397 + tpu = devm_kzalloc(&pdev->dev, sizeof(*tpu), GFP_KERNEL); 398 + if (tpu == NULL) { 399 + dev_err(&pdev->dev, "failed to allocate driver data\n"); 400 + return -ENOMEM; 401 + } 402 + 403 + tpu->pdata = pdev->dev.platform_data; 404 + 405 + /* Map memory, get clock and pin control. */ 406 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 407 + if (!res) { 408 + dev_err(&pdev->dev, "failed to get I/O memory\n"); 409 + return -ENXIO; 410 + } 411 + 412 + tpu->base = devm_ioremap_resource(&pdev->dev, res); 413 + if (tpu->base == NULL) { 414 + dev_err(&pdev->dev, "failed to remap I/O memory\n"); 415 + return -ENXIO; 416 + } 417 + 418 + tpu->clk = devm_clk_get(&pdev->dev, NULL); 419 + if (IS_ERR(tpu->clk)) { 420 + dev_err(&pdev->dev, "cannot get clock\n"); 421 + return PTR_ERR(tpu->clk); 422 + } 423 + 424 + /* Initialize and register the device. */ 425 + platform_set_drvdata(pdev, tpu); 426 + 427 + spin_lock_init(&tpu->lock); 428 + tpu->pdev = pdev; 429 + 430 + tpu->chip.dev = &pdev->dev; 431 + tpu->chip.ops = &tpu_pwm_ops; 432 + tpu->chip.base = -1; 433 + tpu->chip.npwm = TPU_CHANNEL_MAX; 434 + 435 + ret = pwmchip_add(&tpu->chip); 436 + if (ret < 0) { 437 + dev_err(&pdev->dev, "failed to register PWM chip\n"); 438 + return ret; 439 + } 440 + 441 + dev_info(&pdev->dev, "TPU PWM %d registered\n", tpu->pdev->id); 442 + 443 + pm_runtime_enable(&pdev->dev); 444 + 445 + return 0; 446 + } 447 + 448 + static int tpu_remove(struct platform_device *pdev) 449 + { 450 + struct tpu_device *tpu = platform_get_drvdata(pdev); 451 + int ret; 452 + 453 + ret = pwmchip_remove(&tpu->chip); 454 + if (ret) 455 + return ret; 456 + 457 + pm_runtime_disable(&pdev->dev); 458 + 459 + return 0; 460 + } 461 + 462 + static struct platform_driver tpu_driver = { 463 + .probe = tpu_probe, 464 + .remove = tpu_remove, 465 + .driver = { 466 + .name = "renesas-tpu-pwm", 467 + .owner = THIS_MODULE, 468 + } 469 + }; 470 + 471 + module_platform_driver(tpu_driver); 472 + 473 + MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 474 + MODULE_DESCRIPTION("Renesas TPU PWM Driver"); 475 + MODULE_LICENSE("GPL v2");
+16
include/linux/platform_data/pwm-renesas-tpu.h
··· 1 + #ifndef __PWM_RENESAS_TPU_H__ 2 + #define __PWM_RENESAS_TPU_H__ 3 + 4 + #include <linux/pwm.h> 5 + 6 + #define TPU_CHANNEL_MAX 4 7 + 8 + struct tpu_pwm_channel_data { 9 + enum pwm_polarity polarity; 10 + }; 11 + 12 + struct tpu_pwm_platform_data { 13 + struct tpu_pwm_channel_data channels[TPU_CHANNEL_MAX]; 14 + }; 15 + 16 + #endif /* __PWM_RENESAS_TPU_H__ */