f48aaff978b415f1627a1850a2b56c7c92a3aaa4
[openwrt/staging/ldir.git] /
1 From dbd522842d668d590dd415cc99045a9b1f05aeae Mon Sep 17 00:00:00 2001
2 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
3 Date: Thu, 14 Oct 2021 11:09:18 +0100
4 Subject: [PATCH] drivers/gpio: Add a driver that wraps the PWM API as
5 a GPIO controller
6
7 For cases where spare PWM outputs are available, but are desired
8 to be addressed a standard outputs instead.
9 Wraps a PWM channel as a new GPIO chip with the one output.
10
11 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
12 ---
13 drivers/gpio/Kconfig | 8 +++
14 drivers/gpio/Makefile | 1 +
15 drivers/gpio/gpio-pwm.c | 144 ++++++++++++++++++++++++++++++++++++++++
16 3 files changed, 153 insertions(+)
17 create mode 100644 drivers/gpio/gpio-pwm.c
18
19 --- a/drivers/gpio/Kconfig
20 +++ b/drivers/gpio/Kconfig
21 @@ -500,6 +500,14 @@ config GPIO_PMIC_EIC_SPRD
22 help
23 Say yes here to support Spreadtrum PMIC EIC device.
24
25 +config GPIO_PWM
26 + tristate "PWM chip GPIO"
27 + depends on OF_GPIO
28 + depends on PWM
29 + help
30 + Turn on support for exposing a PWM chip as a GPIO
31 + driver.
32 +
33 config GPIO_PXA
34 bool "PXA GPIO support"
35 depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
36 --- a/drivers/gpio/Makefile
37 +++ b/drivers/gpio/Makefile
38 @@ -123,6 +123,7 @@ obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-
39 obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
40 obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
41 obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
42 +obj-$(CONFIG_GPIO_PWM) += gpio-pwm.o
43 obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
44 obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
45 obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
46 --- /dev/null
47 +++ b/drivers/gpio/gpio-pwm.c
48 @@ -0,0 +1,144 @@
49 +// SPDX-License-Identifier: GPL-2.0+
50 +/*
51 + * GPIO driver wrapping PWM API
52 + *
53 + * PWM 0% and PWM 100% are equivalent to digital GPIO
54 + * outputs, and there are times where it is useful to use
55 + * PWM outputs as straight GPIOs (eg outputs of NXP PCA9685
56 + * I2C PWM chip). This driver wraps the PWM API as a GPIO
57 + * controller.
58 + *
59 + * Copyright (C) 2021 Raspberry Pi (Trading) Ltd.
60 + */
61 +
62 +#include <linux/err.h>
63 +#include <linux/gpio/driver.h>
64 +#include <linux/module.h>
65 +#include <linux/platform_device.h>
66 +#include <linux/pwm.h>
67 +
68 +struct pwm_gpio {
69 + struct gpio_chip gc;
70 + struct pwm_device **pwm;
71 +};
72 +
73 +static int pwm_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
74 +{
75 + return GPIO_LINE_DIRECTION_OUT;
76 +}
77 +
78 +static void pwm_gpio_set(struct gpio_chip *gc, unsigned int off, int val)
79 +{
80 + struct pwm_gpio *pwm_gpio = gpiochip_get_data(gc);
81 + struct pwm_state state;
82 +
83 + pwm_get_state(pwm_gpio->pwm[off], &state);
84 + state.duty_cycle = val ? state.period : 0;
85 + pwm_apply_state(pwm_gpio->pwm[off], &state);
86 +}
87 +
88 +static int pwm_gpio_parse_dt(struct pwm_gpio *pwm_gpio,
89 + struct device *dev)
90 +{
91 + struct device_node *node = dev->of_node;
92 + struct pwm_state state;
93 + int ret = 0, i, num_gpios;
94 + const char *pwm_name;
95 +
96 + if (!node)
97 + return -ENODEV;
98 +
99 + num_gpios = of_property_count_strings(node, "pwm-names");
100 + if (num_gpios <= 0)
101 + return 0;
102 +
103 + pwm_gpio->pwm = devm_kzalloc(dev,
104 + sizeof(*pwm_gpio->pwm) * num_gpios,
105 + GFP_KERNEL);
106 + if (!pwm_gpio->pwm)
107 + return -ENOMEM;
108 +
109 + for (i = 0; i < num_gpios; i++) {
110 + ret = of_property_read_string_index(node, "pwm-names", i,
111 + &pwm_name);
112 + if (ret) {
113 + dev_err(dev, "unable to get pwm device index %d, name %s",
114 + i, pwm_name);
115 + goto error;
116 + }
117 +
118 + pwm_gpio->pwm[i] = devm_pwm_get(dev, pwm_name);
119 + if (IS_ERR(pwm_gpio->pwm[i])) {
120 + ret = PTR_ERR(pwm_gpio->pwm[i]);
121 + if (ret != -EPROBE_DEFER)
122 + dev_err(dev, "unable to request PWM\n");
123 + goto error;
124 + }
125 +
126 + /* Sync up PWM state. */
127 + pwm_init_state(pwm_gpio->pwm[i], &state);
128 +
129 + state.duty_cycle = 0;
130 + pwm_apply_state(pwm_gpio->pwm[i], &state);
131 + }
132 +
133 + pwm_gpio->gc.ngpio = num_gpios;
134 +
135 +error:
136 + return ret;
137 +}
138 +
139 +static int pwm_gpio_probe(struct platform_device *pdev)
140 +{
141 + struct device *dev = &pdev->dev;
142 + struct pwm_gpio *pwm_gpio;
143 + int ret;
144 +
145 + pwm_gpio = devm_kzalloc(dev, sizeof(*pwm_gpio), GFP_KERNEL);
146 + if (!pwm_gpio)
147 + return -ENOMEM;
148 +
149 + pwm_gpio->gc.parent = dev;
150 + pwm_gpio->gc.label = "pwm-gpio";
151 + pwm_gpio->gc.owner = THIS_MODULE;
152 + pwm_gpio->gc.of_node = dev->of_node;
153 + pwm_gpio->gc.base = -1;
154 +
155 + pwm_gpio->gc.get_direction = pwm_gpio_get_direction;
156 + pwm_gpio->gc.set = pwm_gpio_set;
157 + pwm_gpio->gc.can_sleep = true;
158 +
159 + ret = pwm_gpio_parse_dt(pwm_gpio, dev);
160 + if (ret)
161 + return ret;
162 +
163 + if (!pwm_gpio->gc.ngpio)
164 + return 0;
165 +
166 + return devm_gpiochip_add_data(dev, &pwm_gpio->gc, pwm_gpio);
167 +}
168 +
169 +static int pwm_gpio_remove(struct platform_device *pdev)
170 +{
171 + return 0;
172 +}
173 +
174 +static const struct of_device_id pwm_gpio_of_match[] = {
175 + { .compatible = "pwm-gpio" },
176 + { }
177 +};
178 +MODULE_DEVICE_TABLE(of, pwm_gpio_of_match);
179 +
180 +static struct platform_driver pwm_gpio_driver = {
181 + .driver = {
182 + .name = "pwm-gpio",
183 + .of_match_table = of_match_ptr(pwm_gpio_of_match),
184 + },
185 + .probe = pwm_gpio_probe,
186 + .remove = pwm_gpio_remove,
187 +};
188 +module_platform_driver(pwm_gpio_driver);
189 +
190 +MODULE_LICENSE("GPL");
191 +MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>");
192 +MODULE_DESCRIPTION("PWM GPIO driver");