Skip to content

Commit 738bbc6

Browse files
Haibo Chenlag-linaro
authored andcommitted
gpio: adp5585: Add Analog Devices ADP5585 support
The ADP5585 is a 10/11 input/output port expander with a built in keypad matrix decoder, programmable logic, reset generator, and PWM generator. This driver supports the GPIO function using the platform device registered by the core MFD driver. The driver is derived from an initial implementation from NXP, available in commit 451f61b46b76 ("MLK-25917-2 gpio: adp5585-gpio: add adp5585-gpio support") in their BSP kernel tree. It has been extensively rewritten. Signed-off-by: Haibo Chen <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Acked-by: Bartosz Golaszewski <[email protected]> Co-developed-by: Laurent Pinchart <[email protected]> Signed-off-by: Laurent Pinchart <[email protected]> Reviewed-by: Frank Li <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lee Jones <[email protected]>
1 parent 480a8ad commit 738bbc6

File tree

4 files changed

+238
-0
lines changed

4 files changed

+238
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ L: [email protected]
543543
544544
S: Maintained
545545
F: Documentation/devicetree/bindings/*/adi,adp5585*.yaml
546+
F: drivers/gpio/gpio-adp5585.c
546547
F: drivers/mfd/adp5585.c
547548
F: include/linux/mfd/adp5585.h
548549

drivers/gpio/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,13 @@ config GPIO_ADP5520
12331233
This option enables support for on-chip GPIO found
12341234
on Analog Devices ADP5520 PMICs.
12351235

1236+
config GPIO_ADP5585
1237+
tristate "GPIO Support for ADP5585"
1238+
depends on MFD_ADP5585
1239+
help
1240+
This option enables support for the GPIO function found in the Analog
1241+
Devices ADP5585.
1242+
12361243
config GPIO_ALTERA_A10SR
12371244
tristate "Altera Arria10 System Resource GPIO"
12381245
depends on MFD_ALTERA_A10SR

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
2626
obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
2727
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
2828
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
29+
obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o
2930
obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
3031
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
3132
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o

drivers/gpio/gpio-adp5585.c

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Analog Devices ADP5585 GPIO driver
4+
*
5+
* Copyright 2022 NXP
6+
* Copyright 2024 Ideas on Board Oy
7+
*/
8+
9+
#include <linux/device.h>
10+
#include <linux/gpio/driver.h>
11+
#include <linux/mfd/adp5585.h>
12+
#include <linux/module.h>
13+
#include <linux/platform_device.h>
14+
#include <linux/regmap.h>
15+
#include <linux/types.h>
16+
17+
#define ADP5585_GPIO_MAX 11
18+
19+
struct adp5585_gpio_dev {
20+
struct gpio_chip gpio_chip;
21+
struct regmap *regmap;
22+
};
23+
24+
static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off)
25+
{
26+
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
27+
unsigned int bank = ADP5585_BANK(off);
28+
unsigned int bit = ADP5585_BIT(off);
29+
unsigned int val;
30+
31+
regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
32+
33+
return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
34+
}
35+
36+
static int adp5585_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
37+
{
38+
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
39+
unsigned int bank = ADP5585_BANK(off);
40+
unsigned int bit = ADP5585_BIT(off);
41+
42+
return regmap_clear_bits(adp5585_gpio->regmap,
43+
ADP5585_GPIO_DIRECTION_A + bank, bit);
44+
}
45+
46+
static int adp5585_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val)
47+
{
48+
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
49+
unsigned int bank = ADP5585_BANK(off);
50+
unsigned int bit = ADP5585_BIT(off);
51+
int ret;
52+
53+
ret = regmap_update_bits(adp5585_gpio->regmap,
54+
ADP5585_GPO_DATA_OUT_A + bank, bit,
55+
val ? bit : 0);
56+
if (ret)
57+
return ret;
58+
59+
return regmap_set_bits(adp5585_gpio->regmap,
60+
ADP5585_GPIO_DIRECTION_A + bank, bit);
61+
}
62+
63+
static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off)
64+
{
65+
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
66+
unsigned int bank = ADP5585_BANK(off);
67+
unsigned int bit = ADP5585_BIT(off);
68+
unsigned int reg;
69+
unsigned int val;
70+
71+
/*
72+
* The input status register doesn't reflect the pin state when the
73+
* GPIO is configured as an output. Check the direction, and read the
74+
* input status from GPI_STATUS or output value from GPO_DATA_OUT
75+
* accordingly.
76+
*
77+
* We don't need any locking, as concurrent access to the same GPIO
78+
* isn't allowed by the GPIO API, so there's no risk of the
79+
* .direction_input(), .direction_output() or .set() operations racing
80+
* with this.
81+
*/
82+
regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
83+
reg = val & bit ? ADP5585_GPO_DATA_OUT_A : ADP5585_GPI_STATUS_A;
84+
regmap_read(adp5585_gpio->regmap, reg + bank, &val);
85+
86+
return !!(val & bit);
87+
}
88+
89+
static void adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off, int val)
90+
{
91+
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
92+
unsigned int bank = ADP5585_BANK(off);
93+
unsigned int bit = ADP5585_BIT(off);
94+
95+
regmap_update_bits(adp5585_gpio->regmap, ADP5585_GPO_DATA_OUT_A + bank,
96+
bit, val ? bit : 0);
97+
}
98+
99+
static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
100+
unsigned int off, unsigned int bias)
101+
{
102+
unsigned int bit, reg, mask, val;
103+
104+
/*
105+
* The bias configuration fields are 2 bits wide and laid down in
106+
* consecutive registers ADP5585_RPULL_CONFIG_*, with a hole of 4 bits
107+
* after R5.
108+
*/
109+
bit = off * 2 + (off > 5 ? 4 : 0);
110+
reg = ADP5585_RPULL_CONFIG_A + bit / 8;
111+
mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);
112+
val = bias << (bit % 8);
113+
114+
return regmap_update_bits(adp5585_gpio->regmap, reg, mask, val);
115+
}
116+
117+
static int adp5585_gpio_set_drive(struct adp5585_gpio_dev *adp5585_gpio,
118+
unsigned int off, enum pin_config_param drive)
119+
{
120+
unsigned int bank = ADP5585_BANK(off);
121+
unsigned int bit = ADP5585_BIT(off);
122+
123+
return regmap_update_bits(adp5585_gpio->regmap,
124+
ADP5585_GPO_OUT_MODE_A + bank, bit,
125+
drive == PIN_CONFIG_DRIVE_OPEN_DRAIN ? bit : 0);
126+
}
127+
128+
static int adp5585_gpio_set_debounce(struct adp5585_gpio_dev *adp5585_gpio,
129+
unsigned int off, unsigned int debounce)
130+
{
131+
unsigned int bank = ADP5585_BANK(off);
132+
unsigned int bit = ADP5585_BIT(off);
133+
134+
return regmap_update_bits(adp5585_gpio->regmap,
135+
ADP5585_DEBOUNCE_DIS_A + bank, bit,
136+
debounce ? 0 : bit);
137+
}
138+
139+
static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off,
140+
unsigned long config)
141+
{
142+
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
143+
enum pin_config_param param = pinconf_to_config_param(config);
144+
u32 arg = pinconf_to_config_argument(config);
145+
146+
switch (param) {
147+
case PIN_CONFIG_BIAS_DISABLE:
148+
return adp5585_gpio_set_bias(adp5585_gpio, off,
149+
ADP5585_Rx_PULL_CFG_DISABLE);
150+
151+
case PIN_CONFIG_BIAS_PULL_DOWN:
152+
return adp5585_gpio_set_bias(adp5585_gpio, off, arg ?
153+
ADP5585_Rx_PULL_CFG_PD_300K :
154+
ADP5585_Rx_PULL_CFG_DISABLE);
155+
156+
case PIN_CONFIG_BIAS_PULL_UP:
157+
return adp5585_gpio_set_bias(adp5585_gpio, off, arg ?
158+
ADP5585_Rx_PULL_CFG_PU_300K :
159+
ADP5585_Rx_PULL_CFG_DISABLE);
160+
161+
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
162+
case PIN_CONFIG_DRIVE_PUSH_PULL:
163+
return adp5585_gpio_set_drive(adp5585_gpio, off, param);
164+
165+
case PIN_CONFIG_INPUT_DEBOUNCE:
166+
return adp5585_gpio_set_debounce(adp5585_gpio, off, arg);
167+
168+
default:
169+
return -ENOTSUPP;
170+
};
171+
}
172+
173+
static int adp5585_gpio_probe(struct platform_device *pdev)
174+
{
175+
struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
176+
struct adp5585_gpio_dev *adp5585_gpio;
177+
struct device *dev = &pdev->dev;
178+
struct gpio_chip *gc;
179+
int ret;
180+
181+
adp5585_gpio = devm_kzalloc(dev, sizeof(*adp5585_gpio), GFP_KERNEL);
182+
if (!adp5585_gpio)
183+
return -ENOMEM;
184+
185+
adp5585_gpio->regmap = adp5585->regmap;
186+
187+
device_set_of_node_from_dev(dev, dev->parent);
188+
189+
gc = &adp5585_gpio->gpio_chip;
190+
gc->parent = dev;
191+
gc->get_direction = adp5585_gpio_get_direction;
192+
gc->direction_input = adp5585_gpio_direction_input;
193+
gc->direction_output = adp5585_gpio_direction_output;
194+
gc->get = adp5585_gpio_get_value;
195+
gc->set = adp5585_gpio_set_value;
196+
gc->set_config = adp5585_gpio_set_config;
197+
gc->can_sleep = true;
198+
199+
gc->base = -1;
200+
gc->ngpio = ADP5585_GPIO_MAX;
201+
gc->label = pdev->name;
202+
gc->owner = THIS_MODULE;
203+
204+
ret = devm_gpiochip_add_data(dev, &adp5585_gpio->gpio_chip,
205+
adp5585_gpio);
206+
if (ret)
207+
return dev_err_probe(dev, ret, "failed to add GPIO chip\n");
208+
209+
return 0;
210+
}
211+
212+
static const struct platform_device_id adp5585_gpio_id_table[] = {
213+
{ "adp5585-gpio" },
214+
{ /* Sentinel */ }
215+
};
216+
MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table);
217+
218+
static struct platform_driver adp5585_gpio_driver = {
219+
.driver = {
220+
.name = "adp5585-gpio",
221+
},
222+
.probe = adp5585_gpio_probe,
223+
.id_table = adp5585_gpio_id_table,
224+
};
225+
module_platform_driver(adp5585_gpio_driver);
226+
227+
MODULE_AUTHOR("Haibo Chen <[email protected]>");
228+
MODULE_DESCRIPTION("GPIO ADP5585 Driver");
229+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)