Skip to content

Commit 4fcb32c

Browse files
committed
drivers/gpio: Add a driver that wraps the PWM API as a GPIO controller
For cases where spare PWM outputs are available, but are desired to be addressed a standard outputs instead. Wraps a PWM channel as a new GPIO chip with the one output. Signed-off-by: Dave Stevenson <[email protected]>
1 parent 30de723 commit 4fcb32c

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

drivers/gpio/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,14 @@ config GPIO_PMIC_EIC_SPRD
473473
help
474474
Say yes here to support Spreadtrum PMIC EIC device.
475475

476+
config GPIO_PWM
477+
tristate "PWM chip GPIO"
478+
depends on OF_GPIO
479+
depends on PWM
480+
help
481+
Turn on support for exposing a PWM chip as a GPIO
482+
driver.
483+
476484
config GPIO_PXA
477485
bool "PXA GPIO support"
478486
depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o
120120
obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
121121
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
122122
obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
123+
obj-$(CONFIG_GPIO_PWM) += gpio-pwm.o
123124
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
124125
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
125126
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o

drivers/gpio/gpio-pwm.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* GPIO driver wrapping PWM API
4+
*
5+
* PWM 0% and PWM 100% are equivalent to digital GPIO
6+
* outputs, and there are times where it is useful to use
7+
* PWM outputs as straight GPIOs (eg outputs of NXP PCA9685
8+
* I2C PWM chip). This driver wraps the PWM API as a GPIO
9+
* controller.
10+
*
11+
* Copyright (C) 2021 Raspberry Pi (Trading) Ltd.
12+
*/
13+
14+
#include <linux/err.h>
15+
#include <linux/gpio/driver.h>
16+
#include <linux/module.h>
17+
#include <linux/platform_device.h>
18+
#include <linux/pwm.h>
19+
20+
#define NUM_GPIO 1
21+
struct pwm_gpio {
22+
struct gpio_chip gc;
23+
struct pwm_device **pwm;
24+
};
25+
26+
static int pwm_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
27+
{
28+
return GPIO_LINE_DIRECTION_OUT;
29+
}
30+
31+
static void pwm_gpio_set(struct gpio_chip *gc, unsigned int off, int val)
32+
{
33+
struct pwm_gpio *pwm_gpio = gpiochip_get_data(gc);
34+
struct pwm_state state;
35+
36+
pwm_get_state(pwm_gpio->pwm[off], &state);
37+
state.duty_cycle = val ? state.period : 0;
38+
pwm_apply_state(pwm_gpio->pwm[off], &state);
39+
}
40+
41+
static int pwm_gpio_parse_dt(struct pwm_gpio *pwm_gpio,
42+
struct device *dev)
43+
{
44+
struct device_node *node = dev->of_node;
45+
struct pwm_state state;
46+
unsigned int i;
47+
int ret = 0;
48+
49+
if (!node)
50+
return -ENODEV;
51+
52+
pwm_gpio->pwm = devm_kzalloc(dev,
53+
sizeof(*pwm_gpio->pwm) * NUM_GPIO,
54+
GFP_KERNEL);
55+
if (!pwm_gpio->pwm)
56+
return -ENOMEM;
57+
58+
for (i = 0; i < NUM_GPIO; i++) {
59+
pwm_gpio->pwm[i] = devm_pwm_get(dev, NULL);
60+
if (IS_ERR(pwm_gpio->pwm[i])) {
61+
ret = PTR_ERR(pwm_gpio->pwm[i]);
62+
if (ret != -EPROBE_DEFER)
63+
dev_err(dev, "unable to request PWM\n");
64+
goto err_alloc;
65+
}
66+
67+
/* Sync up PWM state. */
68+
pwm_init_state(pwm_gpio->pwm[i], &state);
69+
70+
state.duty_cycle = 0;
71+
pwm_apply_state(pwm_gpio->pwm[i], &state);
72+
}
73+
74+
pwm_gpio->gc.ngpio = NUM_GPIO;
75+
76+
err_alloc:
77+
return ret;
78+
}
79+
80+
static int pwm_gpio_probe(struct platform_device *pdev)
81+
{
82+
struct device *dev = &pdev->dev;
83+
struct pwm_gpio *pwm_gpio;
84+
int ret;
85+
86+
pwm_gpio = devm_kzalloc(dev, sizeof(*pwm_gpio), GFP_KERNEL);
87+
if (!pwm_gpio)
88+
return -ENOMEM;
89+
90+
pwm_gpio->gc.parent = dev;
91+
pwm_gpio->gc.label = "pwm-gpio";
92+
pwm_gpio->gc.owner = THIS_MODULE;
93+
pwm_gpio->gc.of_node = dev->of_node;
94+
pwm_gpio->gc.base = -1;
95+
96+
pwm_gpio->gc.get_direction = pwm_gpio_get_direction;
97+
pwm_gpio->gc.set = pwm_gpio_set;
98+
pwm_gpio->gc.can_sleep = true;
99+
100+
ret = pwm_gpio_parse_dt(pwm_gpio, dev);
101+
if (ret)
102+
return ret;
103+
if (!pwm_gpio->gc.ngpio)
104+
return 0;
105+
106+
return devm_gpiochip_add_data(dev, &pwm_gpio->gc, pwm_gpio);
107+
}
108+
109+
static int pwm_gpio_remove(struct platform_device *pdev)
110+
{
111+
return 0;
112+
}
113+
114+
static const struct of_device_id pwm_gpio_of_match[] = {
115+
{ .compatible = "pwm-gpio" },
116+
{ }
117+
};
118+
MODULE_DEVICE_TABLE(of, pwm_gpio_of_match);
119+
120+
static struct platform_driver pwm_gpio_driver = {
121+
.driver = {
122+
.name = "pwm-gpio",
123+
.of_match_table = of_match_ptr(pwm_gpio_of_match),
124+
},
125+
.probe = pwm_gpio_probe,
126+
.remove = pwm_gpio_remove,
127+
};
128+
module_platform_driver(pwm_gpio_driver);
129+
130+
MODULE_LICENSE("GPL");
131+
MODULE_AUTHOR("Dave Stevenson <[email protected]>");
132+
MODULE_DESCRIPTION("PWM GPIO driver");

0 commit comments

Comments
 (0)