1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * gpiolib support for Wolfson Arizona class devices
5 * Copyright 2012 Wolfson Microelectronics PLC.
7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <linux/gpio/driver.h>
14 #include <linux/platform_device.h>
15 #include <linux/pm_runtime.h>
16 #include <linux/seq_file.h>
18 #include <linux/mfd/arizona/core.h>
19 #include <linux/mfd/arizona/pdata.h>
20 #include <linux/mfd/arizona/registers.h>
23 struct arizona
*arizona
;
24 struct gpio_chip gpio_chip
;
27 static int arizona_gpio_direction_in(struct gpio_chip
*chip
, unsigned offset
)
29 struct arizona_gpio
*arizona_gpio
= gpiochip_get_data(chip
);
30 struct arizona
*arizona
= arizona_gpio
->arizona
;
31 bool persistent
= gpiochip_line_is_persistent(chip
, offset
);
35 ret
= regmap_update_bits_check(arizona
->regmap
,
36 ARIZONA_GPIO1_CTRL
+ offset
,
37 ARIZONA_GPN_DIR
, ARIZONA_GPN_DIR
,
42 if (change
&& persistent
) {
43 pm_runtime_mark_last_busy(chip
->parent
);
44 pm_runtime_put_autosuspend(chip
->parent
);
50 static int arizona_gpio_get(struct gpio_chip
*chip
, unsigned offset
)
52 struct arizona_gpio
*arizona_gpio
= gpiochip_get_data(chip
);
53 struct arizona
*arizona
= arizona_gpio
->arizona
;
54 unsigned int reg
, val
;
57 reg
= ARIZONA_GPIO1_CTRL
+ offset
;
58 ret
= regmap_read(arizona
->regmap
, reg
, &val
);
62 /* Resume to read actual registers for input pins */
63 if (val
& ARIZONA_GPN_DIR
) {
64 ret
= pm_runtime_get_sync(chip
->parent
);
66 dev_err(chip
->parent
, "Failed to resume: %d\n", ret
);
67 pm_runtime_put_autosuspend(chip
->parent
);
71 /* Register is cached, drop it to ensure a physical read */
72 ret
= regcache_drop_region(arizona
->regmap
, reg
, reg
);
74 dev_err(chip
->parent
, "Failed to drop cache: %d\n",
76 pm_runtime_put_autosuspend(chip
->parent
);
80 ret
= regmap_read(arizona
->regmap
, reg
, &val
);
82 pm_runtime_put_autosuspend(chip
->parent
);
86 pm_runtime_mark_last_busy(chip
->parent
);
87 pm_runtime_put_autosuspend(chip
->parent
);
90 if (val
& ARIZONA_GPN_LVL
)
96 static int arizona_gpio_direction_out(struct gpio_chip
*chip
,
97 unsigned offset
, int value
)
99 struct arizona_gpio
*arizona_gpio
= gpiochip_get_data(chip
);
100 struct arizona
*arizona
= arizona_gpio
->arizona
;
101 bool persistent
= gpiochip_line_is_persistent(chip
, offset
);
105 ret
= regmap_read(arizona
->regmap
, ARIZONA_GPIO1_CTRL
+ offset
, &val
);
109 if ((val
& ARIZONA_GPN_DIR
) && persistent
) {
110 ret
= pm_runtime_get_sync(chip
->parent
);
112 dev_err(chip
->parent
, "Failed to resume: %d\n", ret
);
113 pm_runtime_put(chip
->parent
);
119 value
= ARIZONA_GPN_LVL
;
121 return regmap_update_bits(arizona
->regmap
, ARIZONA_GPIO1_CTRL
+ offset
,
122 ARIZONA_GPN_DIR
| ARIZONA_GPN_LVL
, value
);
125 static void arizona_gpio_set(struct gpio_chip
*chip
, unsigned offset
, int value
)
127 struct arizona_gpio
*arizona_gpio
= gpiochip_get_data(chip
);
128 struct arizona
*arizona
= arizona_gpio
->arizona
;
131 value
= ARIZONA_GPN_LVL
;
133 regmap_update_bits(arizona
->regmap
, ARIZONA_GPIO1_CTRL
+ offset
,
134 ARIZONA_GPN_LVL
, value
);
137 static const struct gpio_chip template_chip
= {
139 .owner
= THIS_MODULE
,
140 .direction_input
= arizona_gpio_direction_in
,
141 .get
= arizona_gpio_get
,
142 .direction_output
= arizona_gpio_direction_out
,
143 .set
= arizona_gpio_set
,
147 static int arizona_gpio_probe(struct platform_device
*pdev
)
149 struct arizona
*arizona
= dev_get_drvdata(pdev
->dev
.parent
);
150 struct arizona_pdata
*pdata
= &arizona
->pdata
;
151 struct arizona_gpio
*arizona_gpio
;
154 arizona_gpio
= devm_kzalloc(&pdev
->dev
, sizeof(*arizona_gpio
),
159 arizona_gpio
->arizona
= arizona
;
160 arizona_gpio
->gpio_chip
= template_chip
;
161 arizona_gpio
->gpio_chip
.parent
= &pdev
->dev
;
162 #ifdef CONFIG_OF_GPIO
163 arizona_gpio
->gpio_chip
.of_node
= arizona
->dev
->of_node
;
166 switch (arizona
->type
) {
173 arizona_gpio
->gpio_chip
.ngpio
= 5;
177 arizona_gpio
->gpio_chip
.ngpio
= 2;
180 dev_err(&pdev
->dev
, "Unknown chip variant %d\n",
185 if (pdata
->gpio_base
)
186 arizona_gpio
->gpio_chip
.base
= pdata
->gpio_base
;
188 arizona_gpio
->gpio_chip
.base
= -1;
190 pm_runtime_enable(&pdev
->dev
);
192 ret
= devm_gpiochip_add_data(&pdev
->dev
, &arizona_gpio
->gpio_chip
,
195 pm_runtime_disable(&pdev
->dev
);
196 dev_err(&pdev
->dev
, "Could not register gpiochip, %d\n",
204 static struct platform_driver arizona_gpio_driver
= {
205 .driver
.name
= "arizona-gpio",
206 .probe
= arizona_gpio_probe
,
209 module_platform_driver(arizona_gpio_driver
);
211 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
212 MODULE_DESCRIPTION("GPIO interface for Arizona devices");
213 MODULE_LICENSE("GPL");
214 MODULE_ALIAS("platform:arizona-gpio");