xref: /linux/drivers/gpio/gpio-raspberrypi-exp.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
1a98d90e7SDave Stevenson // SPDX-License-Identifier: GPL-2.0+
2a98d90e7SDave Stevenson /*
3a98d90e7SDave Stevenson  *  Raspberry Pi 3 expander GPIO driver
4a98d90e7SDave Stevenson  *
5a98d90e7SDave Stevenson  *  Uses the firmware mailbox service to communicate with the
6a98d90e7SDave Stevenson  *  GPIO expander on the VPU.
7a98d90e7SDave Stevenson  *
8a98d90e7SDave Stevenson  *  Copyright (C) 2017 Raspberry Pi Trading Ltd.
9a98d90e7SDave Stevenson  */
10a98d90e7SDave Stevenson 
11a98d90e7SDave Stevenson #include <linux/err.h>
12a98d90e7SDave Stevenson #include <linux/gpio/driver.h>
13a98d90e7SDave Stevenson #include <linux/module.h>
14a98d90e7SDave Stevenson #include <linux/platform_device.h>
15a98d90e7SDave Stevenson #include <soc/bcm2835/raspberrypi-firmware.h>
16a98d90e7SDave Stevenson 
17a98d90e7SDave Stevenson #define MODULE_NAME "raspberrypi-exp-gpio"
18a98d90e7SDave Stevenson #define NUM_GPIO 8
19a98d90e7SDave Stevenson 
20a98d90e7SDave Stevenson #define RPI_EXP_GPIO_BASE	128
21a98d90e7SDave Stevenson 
22a98d90e7SDave Stevenson #define RPI_EXP_GPIO_DIR_IN	0
23a98d90e7SDave Stevenson #define RPI_EXP_GPIO_DIR_OUT	1
24a98d90e7SDave Stevenson 
25a98d90e7SDave Stevenson struct rpi_exp_gpio {
26a98d90e7SDave Stevenson 	struct gpio_chip gc;
27a98d90e7SDave Stevenson 	struct rpi_firmware *fw;
28a98d90e7SDave Stevenson };
29a98d90e7SDave Stevenson 
30a98d90e7SDave Stevenson /* VC4 firmware mailbox interface data structures */
31a98d90e7SDave Stevenson 
32a98d90e7SDave Stevenson struct gpio_set_config {
33a98d90e7SDave Stevenson 	u32 gpio;
34a98d90e7SDave Stevenson 	u32 direction;
35a98d90e7SDave Stevenson 	u32 polarity;
36a98d90e7SDave Stevenson 	u32 term_en;
37a98d90e7SDave Stevenson 	u32 term_pull_up;
38a98d90e7SDave Stevenson 	u32 state;
39a98d90e7SDave Stevenson };
40a98d90e7SDave Stevenson 
41a98d90e7SDave Stevenson struct gpio_get_config {
42a98d90e7SDave Stevenson 	u32 gpio;
43a98d90e7SDave Stevenson 	u32 direction;
44a98d90e7SDave Stevenson 	u32 polarity;
45a98d90e7SDave Stevenson 	u32 term_en;
46a98d90e7SDave Stevenson 	u32 term_pull_up;
47a98d90e7SDave Stevenson };
48a98d90e7SDave Stevenson 
49a98d90e7SDave Stevenson struct gpio_get_set_state {
50a98d90e7SDave Stevenson 	u32 gpio;
51a98d90e7SDave Stevenson 	u32 state;
52a98d90e7SDave Stevenson };
53a98d90e7SDave Stevenson 
rpi_exp_gpio_get_polarity(struct gpio_chip * gc,unsigned int off)54a98d90e7SDave Stevenson static int rpi_exp_gpio_get_polarity(struct gpio_chip *gc, unsigned int off)
55a98d90e7SDave Stevenson {
56a98d90e7SDave Stevenson 	struct rpi_exp_gpio *gpio;
57a98d90e7SDave Stevenson 	struct gpio_get_config get;
58a98d90e7SDave Stevenson 	int ret;
59a98d90e7SDave Stevenson 
60a98d90e7SDave Stevenson 	gpio = gpiochip_get_data(gc);
61a98d90e7SDave Stevenson 
62a98d90e7SDave Stevenson 	get.gpio = off + RPI_EXP_GPIO_BASE;	/* GPIO to update */
63a98d90e7SDave Stevenson 
64a98d90e7SDave Stevenson 	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_CONFIG,
65a98d90e7SDave Stevenson 				    &get, sizeof(get));
66a98d90e7SDave Stevenson 	if (ret || get.gpio != 0) {
67a98d90e7SDave Stevenson 		dev_err(gc->parent, "Failed to get GPIO %u config (%d %x)\n",
68a98d90e7SDave Stevenson 			off, ret, get.gpio);
69a98d90e7SDave Stevenson 		return ret ? ret : -EIO;
70a98d90e7SDave Stevenson 	}
71a98d90e7SDave Stevenson 	return get.polarity;
72a98d90e7SDave Stevenson }
73a98d90e7SDave Stevenson 
rpi_exp_gpio_dir_in(struct gpio_chip * gc,unsigned int off)74a98d90e7SDave Stevenson static int rpi_exp_gpio_dir_in(struct gpio_chip *gc, unsigned int off)
75a98d90e7SDave Stevenson {
76a98d90e7SDave Stevenson 	struct rpi_exp_gpio *gpio;
77a98d90e7SDave Stevenson 	struct gpio_set_config set_in;
78a98d90e7SDave Stevenson 	int ret;
79a98d90e7SDave Stevenson 
80a98d90e7SDave Stevenson 	gpio = gpiochip_get_data(gc);
81a98d90e7SDave Stevenson 
82a98d90e7SDave Stevenson 	set_in.gpio = off + RPI_EXP_GPIO_BASE;	/* GPIO to update */
83a98d90e7SDave Stevenson 	set_in.direction = RPI_EXP_GPIO_DIR_IN;
84a98d90e7SDave Stevenson 	set_in.term_en = 0;		/* termination disabled */
85a98d90e7SDave Stevenson 	set_in.term_pull_up = 0;	/* n/a as termination disabled */
86a98d90e7SDave Stevenson 	set_in.state = 0;		/* n/a as configured as an input */
87a98d90e7SDave Stevenson 
88a98d90e7SDave Stevenson 	ret = rpi_exp_gpio_get_polarity(gc, off);
89a98d90e7SDave Stevenson 	if (ret < 0)
90a98d90e7SDave Stevenson 		return ret;
91a98d90e7SDave Stevenson 	set_in.polarity = ret;		/* Retain existing setting */
92a98d90e7SDave Stevenson 
93a98d90e7SDave Stevenson 	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_CONFIG,
94a98d90e7SDave Stevenson 				    &set_in, sizeof(set_in));
95a98d90e7SDave Stevenson 	if (ret || set_in.gpio != 0) {
96a98d90e7SDave Stevenson 		dev_err(gc->parent, "Failed to set GPIO %u to input (%d %x)\n",
97a98d90e7SDave Stevenson 			off, ret, set_in.gpio);
98a98d90e7SDave Stevenson 		return ret ? ret : -EIO;
99a98d90e7SDave Stevenson 	}
100a98d90e7SDave Stevenson 	return 0;
101a98d90e7SDave Stevenson }
102a98d90e7SDave Stevenson 
rpi_exp_gpio_dir_out(struct gpio_chip * gc,unsigned int off,int val)103a98d90e7SDave Stevenson static int rpi_exp_gpio_dir_out(struct gpio_chip *gc, unsigned int off, int val)
104a98d90e7SDave Stevenson {
105a98d90e7SDave Stevenson 	struct rpi_exp_gpio *gpio;
106a98d90e7SDave Stevenson 	struct gpio_set_config set_out;
107a98d90e7SDave Stevenson 	int ret;
108a98d90e7SDave Stevenson 
109a98d90e7SDave Stevenson 	gpio = gpiochip_get_data(gc);
110a98d90e7SDave Stevenson 
111a98d90e7SDave Stevenson 	set_out.gpio = off + RPI_EXP_GPIO_BASE;	/* GPIO to update */
112a98d90e7SDave Stevenson 	set_out.direction = RPI_EXP_GPIO_DIR_OUT;
113a98d90e7SDave Stevenson 	set_out.term_en = 0;		/* n/a as an output */
114a98d90e7SDave Stevenson 	set_out.term_pull_up = 0;	/* n/a as termination disabled */
115a98d90e7SDave Stevenson 	set_out.state = val;		/* Output state */
116a98d90e7SDave Stevenson 
117a98d90e7SDave Stevenson 	ret = rpi_exp_gpio_get_polarity(gc, off);
118a98d90e7SDave Stevenson 	if (ret < 0)
119a98d90e7SDave Stevenson 		return ret;
120a98d90e7SDave Stevenson 	set_out.polarity = ret;		/* Retain existing setting */
121a98d90e7SDave Stevenson 
122a98d90e7SDave Stevenson 	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_CONFIG,
123a98d90e7SDave Stevenson 				    &set_out, sizeof(set_out));
124a98d90e7SDave Stevenson 	if (ret || set_out.gpio != 0) {
125a98d90e7SDave Stevenson 		dev_err(gc->parent, "Failed to set GPIO %u to output (%d %x)\n",
126a98d90e7SDave Stevenson 			off, ret, set_out.gpio);
127a98d90e7SDave Stevenson 		return ret ? ret : -EIO;
128a98d90e7SDave Stevenson 	}
129a98d90e7SDave Stevenson 	return 0;
130a98d90e7SDave Stevenson }
131a98d90e7SDave Stevenson 
rpi_exp_gpio_get_direction(struct gpio_chip * gc,unsigned int off)132a98d90e7SDave Stevenson static int rpi_exp_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
133a98d90e7SDave Stevenson {
134a98d90e7SDave Stevenson 	struct rpi_exp_gpio *gpio;
135a98d90e7SDave Stevenson 	struct gpio_get_config get;
136a98d90e7SDave Stevenson 	int ret;
137a98d90e7SDave Stevenson 
138a98d90e7SDave Stevenson 	gpio = gpiochip_get_data(gc);
139a98d90e7SDave Stevenson 
140a98d90e7SDave Stevenson 	get.gpio = off + RPI_EXP_GPIO_BASE;	/* GPIO to update */
141a98d90e7SDave Stevenson 
142a98d90e7SDave Stevenson 	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_CONFIG,
143a98d90e7SDave Stevenson 				    &get, sizeof(get));
144a98d90e7SDave Stevenson 	if (ret || get.gpio != 0) {
145a98d90e7SDave Stevenson 		dev_err(gc->parent,
146a98d90e7SDave Stevenson 			"Failed to get GPIO %u config (%d %x)\n", off, ret,
147a98d90e7SDave Stevenson 			get.gpio);
148a98d90e7SDave Stevenson 		return ret ? ret : -EIO;
149a98d90e7SDave Stevenson 	}
150e42615ecSMatti Vaittinen 	if (get.direction)
151e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
152e42615ecSMatti Vaittinen 
153e42615ecSMatti Vaittinen 	return GPIO_LINE_DIRECTION_IN;
154a98d90e7SDave Stevenson }
155a98d90e7SDave Stevenson 
rpi_exp_gpio_get(struct gpio_chip * gc,unsigned int off)156a98d90e7SDave Stevenson static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off)
157a98d90e7SDave Stevenson {
158a98d90e7SDave Stevenson 	struct rpi_exp_gpio *gpio;
159a98d90e7SDave Stevenson 	struct gpio_get_set_state get;
160a98d90e7SDave Stevenson 	int ret;
161a98d90e7SDave Stevenson 
162a98d90e7SDave Stevenson 	gpio = gpiochip_get_data(gc);
163a98d90e7SDave Stevenson 
164a98d90e7SDave Stevenson 	get.gpio = off + RPI_EXP_GPIO_BASE;	/* GPIO to update */
165a98d90e7SDave Stevenson 	get.state = 0;		/* storage for returned value */
166a98d90e7SDave Stevenson 
167a98d90e7SDave Stevenson 	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_STATE,
168a98d90e7SDave Stevenson 					 &get, sizeof(get));
169a98d90e7SDave Stevenson 	if (ret || get.gpio != 0) {
170a98d90e7SDave Stevenson 		dev_err(gc->parent,
171a98d90e7SDave Stevenson 			"Failed to get GPIO %u state (%d %x)\n", off, ret,
172a98d90e7SDave Stevenson 			get.gpio);
173a98d90e7SDave Stevenson 		return ret ? ret : -EIO;
174a98d90e7SDave Stevenson 	}
175a98d90e7SDave Stevenson 	return !!get.state;
176a98d90e7SDave Stevenson }
177a98d90e7SDave Stevenson 
rpi_exp_gpio_set(struct gpio_chip * gc,unsigned int off,int val)178a98d90e7SDave Stevenson static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val)
179a98d90e7SDave Stevenson {
180a98d90e7SDave Stevenson 	struct rpi_exp_gpio *gpio;
181a98d90e7SDave Stevenson 	struct gpio_get_set_state set;
182a98d90e7SDave Stevenson 	int ret;
183a98d90e7SDave Stevenson 
184a98d90e7SDave Stevenson 	gpio = gpiochip_get_data(gc);
185a98d90e7SDave Stevenson 
186a98d90e7SDave Stevenson 	set.gpio = off + RPI_EXP_GPIO_BASE;	/* GPIO to update */
187a98d90e7SDave Stevenson 	set.state = val;	/* Output state */
188a98d90e7SDave Stevenson 
189a98d90e7SDave Stevenson 	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_STATE,
190a98d90e7SDave Stevenson 					 &set, sizeof(set));
191a98d90e7SDave Stevenson 	if (ret || set.gpio != 0)
192a98d90e7SDave Stevenson 		dev_err(gc->parent,
193a98d90e7SDave Stevenson 			"Failed to set GPIO %u state (%d %x)\n", off, ret,
194a98d90e7SDave Stevenson 			set.gpio);
195a98d90e7SDave Stevenson }
196a98d90e7SDave Stevenson 
rpi_exp_gpio_probe(struct platform_device * pdev)197a98d90e7SDave Stevenson static int rpi_exp_gpio_probe(struct platform_device *pdev)
198a98d90e7SDave Stevenson {
199a98d90e7SDave Stevenson 	struct device *dev = &pdev->dev;
200a98d90e7SDave Stevenson 	struct device_node *np = dev->of_node;
201a98d90e7SDave Stevenson 	struct device_node *fw_node;
202a98d90e7SDave Stevenson 	struct rpi_firmware *fw;
203a98d90e7SDave Stevenson 	struct rpi_exp_gpio *rpi_gpio;
204a98d90e7SDave Stevenson 
205a98d90e7SDave Stevenson 	fw_node = of_get_parent(np);
206a98d90e7SDave Stevenson 	if (!fw_node) {
207a98d90e7SDave Stevenson 		dev_err(dev, "Missing firmware node\n");
208a98d90e7SDave Stevenson 		return -ENOENT;
209a98d90e7SDave Stevenson 	}
210a98d90e7SDave Stevenson 
2110e3333b2SNicolas Saenz Julienne 	fw = devm_rpi_firmware_get(&pdev->dev, fw_node);
21285af74c4SNicolas Saenz Julienne 	of_node_put(fw_node);
213a98d90e7SDave Stevenson 	if (!fw)
214a98d90e7SDave Stevenson 		return -EPROBE_DEFER;
215a98d90e7SDave Stevenson 
216a98d90e7SDave Stevenson 	rpi_gpio = devm_kzalloc(dev, sizeof(*rpi_gpio), GFP_KERNEL);
217a98d90e7SDave Stevenson 	if (!rpi_gpio)
218a98d90e7SDave Stevenson 		return -ENOMEM;
219a98d90e7SDave Stevenson 
220a98d90e7SDave Stevenson 	rpi_gpio->fw = fw;
221a98d90e7SDave Stevenson 	rpi_gpio->gc.parent = dev;
222a98d90e7SDave Stevenson 	rpi_gpio->gc.label = MODULE_NAME;
223a98d90e7SDave Stevenson 	rpi_gpio->gc.owner = THIS_MODULE;
224a98d90e7SDave Stevenson 	rpi_gpio->gc.base = -1;
225a98d90e7SDave Stevenson 	rpi_gpio->gc.ngpio = NUM_GPIO;
226a98d90e7SDave Stevenson 
227a98d90e7SDave Stevenson 	rpi_gpio->gc.direction_input = rpi_exp_gpio_dir_in;
228a98d90e7SDave Stevenson 	rpi_gpio->gc.direction_output = rpi_exp_gpio_dir_out;
229a98d90e7SDave Stevenson 	rpi_gpio->gc.get_direction = rpi_exp_gpio_get_direction;
230a98d90e7SDave Stevenson 	rpi_gpio->gc.get = rpi_exp_gpio_get;
231a98d90e7SDave Stevenson 	rpi_gpio->gc.set = rpi_exp_gpio_set;
232a98d90e7SDave Stevenson 	rpi_gpio->gc.can_sleep = true;
233a98d90e7SDave Stevenson 
234a98d90e7SDave Stevenson 	return devm_gpiochip_add_data(dev, &rpi_gpio->gc, rpi_gpio);
235a98d90e7SDave Stevenson }
236a98d90e7SDave Stevenson 
237*30531e14SZhu Wang static const struct of_device_id rpi_exp_gpio_ids[] = {
238a98d90e7SDave Stevenson 	{ .compatible = "raspberrypi,firmware-gpio" },
239a98d90e7SDave Stevenson 	{ }
240a98d90e7SDave Stevenson };
241a98d90e7SDave Stevenson MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids);
242a98d90e7SDave Stevenson 
243a98d90e7SDave Stevenson static struct platform_driver rpi_exp_gpio_driver = {
244a98d90e7SDave Stevenson 	.driver	= {
245a98d90e7SDave Stevenson 		.name		= MODULE_NAME,
246*30531e14SZhu Wang 		.of_match_table	= rpi_exp_gpio_ids,
247a98d90e7SDave Stevenson 	},
248a98d90e7SDave Stevenson 	.probe	= rpi_exp_gpio_probe,
249a98d90e7SDave Stevenson };
250a98d90e7SDave Stevenson module_platform_driver(rpi_exp_gpio_driver);
251a98d90e7SDave Stevenson 
252a98d90e7SDave Stevenson MODULE_LICENSE("GPL");
253a98d90e7SDave Stevenson MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.org>");
254a98d90e7SDave Stevenson MODULE_DESCRIPTION("Raspberry Pi 3 expander GPIO driver");
255a98d90e7SDave Stevenson MODULE_ALIAS("platform:rpi-exp-gpio");
256