gpio-raspberrypi-exp.c (6395B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Raspberry Pi 3 expander GPIO driver 4 * 5 * Uses the firmware mailbox service to communicate with the 6 * 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 8 19 20#define RPI_EXP_GPIO_BASE 128 21 22#define RPI_EXP_GPIO_DIR_IN 0 23#define RPI_EXP_GPIO_DIR_OUT 1 24 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 = of_match_ptr(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");