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