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

pwm: visconti: Add Toshiba Visconti SoC PWM support

Add driver for the PWM controller on Toshiba Visconti ARM SoC.

Signed-off-by: Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
[thierry.reding@gmail.com: fix up a couple of checkpatch warnings]
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>

authored by

Nobuhiro Iwamatsu and committed by
Thierry Reding
721b5957 b0221e70

+200
+9
drivers/pwm/Kconfig
··· 601 601 To compile this driver as a module, choose M here: the module 602 602 will be called pwm-twl-led. 603 603 604 + config PWM_VISCONTI 605 + tristate "Toshiba Visconti PWM support" 606 + depends on ARCH_VISCONTI || COMPILE_TEST 607 + help 608 + PWM Subsystem driver support for Toshiba Visconti SoCs. 609 + 610 + To compile this driver as a module, choose M here: the module 611 + will be called pwm-visconti. 612 + 604 613 config PWM_VT8500 605 614 tristate "vt8500 PWM support" 606 615 depends on ARCH_VT8500 || COMPILE_TEST
+1
drivers/pwm/Makefile
··· 56 56 obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o 57 57 obj-$(CONFIG_PWM_TWL) += pwm-twl.o 58 58 obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o 59 + obj-$(CONFIG_PWM_VISCONTI) += pwm-visconti.o 59 60 obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
+190
drivers/pwm/pwm-visconti.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Toshiba Visconti pulse-width-modulation controller driver 4 + * 5 + * Copyright (c) 2020 - 2021 TOSHIBA CORPORATION 6 + * Copyright (c) 2020 - 2021 Toshiba Electronic Devices & Storage Corporation 7 + * 8 + * Authors: Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> 9 + * 10 + * Limitations: 11 + * - The fixed input clock is running at 1 MHz and is divided by either 1, 12 + * 2, 4 or 8. 13 + * - When the settings of the PWM are modified, the new values are shadowed 14 + * in hardware until the PIPGM_PCSR register is written and the currently 15 + * running period is completed. This way the hardware switches atomically 16 + * from the old setting to the new. 17 + * - Disabling the hardware completes the currently running period and keeps 18 + * the output at low level at all times. 19 + */ 20 + 21 + #include <linux/err.h> 22 + #include <linux/io.h> 23 + #include <linux/module.h> 24 + #include <linux/of_device.h> 25 + #include <linux/platform_device.h> 26 + #include <linux/pwm.h> 27 + 28 + #define PIPGM_PCSR(ch) (0x400 + 4 * (ch)) 29 + #define PIPGM_PDUT(ch) (0x420 + 4 * (ch)) 30 + #define PIPGM_PWMC(ch) (0x440 + 4 * (ch)) 31 + 32 + #define PIPGM_PWMC_PWMACT BIT(5) 33 + #define PIPGM_PWMC_CLK_MASK GENMASK(1, 0) 34 + #define PIPGM_PWMC_POLARITY_MASK GENMASK(5, 5) 35 + 36 + struct visconti_pwm_chip { 37 + struct pwm_chip chip; 38 + void __iomem *base; 39 + }; 40 + 41 + static inline struct visconti_pwm_chip *visconti_pwm_from_chip(struct pwm_chip *chip) 42 + { 43 + return container_of(chip, struct visconti_pwm_chip, chip); 44 + } 45 + 46 + static int visconti_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 47 + const struct pwm_state *state) 48 + { 49 + struct visconti_pwm_chip *priv = visconti_pwm_from_chip(chip); 50 + u32 period, duty_cycle, pwmc0; 51 + 52 + if (!state->enabled) { 53 + writel(0, priv->base + PIPGM_PCSR(pwm->hwpwm)); 54 + return 0; 55 + } 56 + 57 + /* 58 + * The biggest period the hardware can provide is 59 + * (0xffff << 3) * 1000 ns 60 + * This value fits easily in an u32, so simplify the maths by 61 + * capping the values to 32 bit integers. 62 + */ 63 + if (state->period > (0xffff << 3) * 1000) 64 + period = (0xffff << 3) * 1000; 65 + else 66 + period = state->period; 67 + 68 + if (state->duty_cycle > period) 69 + duty_cycle = period; 70 + else 71 + duty_cycle = state->duty_cycle; 72 + 73 + /* 74 + * The input clock runs fixed at 1 MHz, so we have only 75 + * microsecond resolution and so can divide by 76 + * NSEC_PER_SEC / CLKFREQ = 1000 without losing precision. 77 + */ 78 + period /= 1000; 79 + duty_cycle /= 1000; 80 + 81 + if (!period) 82 + return -ERANGE; 83 + 84 + /* 85 + * PWMC controls a divider that divides the input clk by a 86 + * power of two between 1 and 8. As a smaller divider yields 87 + * higher precision, pick the smallest possible one. 88 + */ 89 + if (period > 0xffff) { 90 + pwmc0 = ilog2(period >> 16); 91 + if (WARN_ON(pwmc0 > 3)) 92 + return -EINVAL; 93 + } else { 94 + pwmc0 = 0; 95 + } 96 + 97 + period >>= pwmc0; 98 + duty_cycle >>= pwmc0; 99 + 100 + if (state->polarity == PWM_POLARITY_INVERSED) 101 + pwmc0 |= PIPGM_PWMC_PWMACT; 102 + writel(pwmc0, priv->base + PIPGM_PWMC(pwm->hwpwm)); 103 + writel(duty_cycle, priv->base + PIPGM_PDUT(pwm->hwpwm)); 104 + writel(period, priv->base + PIPGM_PCSR(pwm->hwpwm)); 105 + 106 + return 0; 107 + } 108 + 109 + static void visconti_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 110 + struct pwm_state *state) 111 + { 112 + struct visconti_pwm_chip *priv = visconti_pwm_from_chip(chip); 113 + u32 period, duty, pwmc0, pwmc0_clk; 114 + 115 + period = readl(priv->base + PIPGM_PCSR(pwm->hwpwm)); 116 + duty = readl(priv->base + PIPGM_PDUT(pwm->hwpwm)); 117 + pwmc0 = readl(priv->base + PIPGM_PWMC(pwm->hwpwm)); 118 + pwmc0_clk = pwmc0 & PIPGM_PWMC_CLK_MASK; 119 + 120 + state->period = (period << pwmc0_clk) * NSEC_PER_USEC; 121 + state->duty_cycle = (duty << pwmc0_clk) * NSEC_PER_USEC; 122 + if (pwmc0 & PIPGM_PWMC_POLARITY_MASK) 123 + state->polarity = PWM_POLARITY_INVERSED; 124 + else 125 + state->polarity = PWM_POLARITY_NORMAL; 126 + 127 + state->enabled = true; 128 + } 129 + 130 + static const struct pwm_ops visconti_pwm_ops = { 131 + .apply = visconti_pwm_apply, 132 + .get_state = visconti_pwm_get_state, 133 + .owner = THIS_MODULE, 134 + }; 135 + 136 + static int visconti_pwm_probe(struct platform_device *pdev) 137 + { 138 + struct device *dev = &pdev->dev; 139 + struct visconti_pwm_chip *priv; 140 + int ret; 141 + 142 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 143 + if (!priv) 144 + return -ENOMEM; 145 + 146 + priv->base = devm_platform_ioremap_resource(pdev, 0); 147 + if (IS_ERR(priv->base)) 148 + return PTR_ERR(priv->base); 149 + 150 + platform_set_drvdata(pdev, priv); 151 + 152 + priv->chip.dev = dev; 153 + priv->chip.ops = &visconti_pwm_ops; 154 + priv->chip.npwm = 4; 155 + 156 + ret = pwmchip_add(&priv->chip); 157 + if (ret < 0) 158 + return dev_err_probe(&pdev->dev, ret, "Cannot register visconti PWM\n"); 159 + 160 + return 0; 161 + } 162 + 163 + static int visconti_pwm_remove(struct platform_device *pdev) 164 + { 165 + struct visconti_pwm_chip *priv = platform_get_drvdata(pdev); 166 + 167 + pwmchip_remove(&priv->chip); 168 + 169 + return 0; 170 + } 171 + 172 + static const struct of_device_id visconti_pwm_of_match[] = { 173 + { .compatible = "toshiba,visconti-pwm", }, 174 + { } 175 + }; 176 + MODULE_DEVICE_TABLE(of, visconti_pwm_of_match); 177 + 178 + static struct platform_driver visconti_pwm_driver = { 179 + .driver = { 180 + .name = "pwm-visconti", 181 + .of_match_table = visconti_pwm_of_match, 182 + }, 183 + .probe = visconti_pwm_probe, 184 + .remove = visconti_pwm_remove, 185 + }; 186 + module_platform_driver(visconti_pwm_driver); 187 + 188 + MODULE_LICENSE("GPL v2"); 189 + MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>"); 190 + MODULE_ALIAS("platform:pwm-visconti");