brintos

brintos / linux-shallow public Read only

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