1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2024 Google LLC 4 * 5 * This driver provides the ability to control GPIOs on the Chrome OS EC. 6 * There isn't any direction control, and setting values on GPIOs is only 7 * possible when the system is unlocked. 8 */ 9 10 #include <linux/bitops.h> 11 #include <linux/device.h> 12 #include <linux/errno.h> 13 #include <linux/gpio/driver.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/platform_data/cros_ec_commands.h> 17 #include <linux/platform_data/cros_ec_proto.h> 18 #include <linux/platform_device.h> 19 #include <linux/property.h> 20 #include <linux/slab.h> 21 22 /* Prefix all names to avoid collisions with EC <-> AP nets */ 23 static const char cros_ec_gpio_prefix[] = "EC:"; 24 25 /* Setting gpios is only supported when the system is unlocked */ 26 static int cros_ec_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) 27 { 28 const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); 29 struct cros_ec_device *cros_ec = gpiochip_get_data(gc); 30 struct ec_params_gpio_set params = { 31 .val = val, 32 }; 33 ssize_t copied; 34 35 copied = strscpy(params.name, name, sizeof(params.name)); 36 if (copied < 0) 37 return copied; 38 39 return cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_SET, ¶ms, 40 sizeof(params), NULL, 0); 41 } 42 43 static int cros_ec_gpio_get(struct gpio_chip *gc, unsigned int gpio) 44 { 45 const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); 46 struct cros_ec_device *cros_ec = gpiochip_get_data(gc); 47 struct ec_params_gpio_get params; 48 struct ec_response_gpio_get response; 49 int ret; 50 ssize_t copied; 51 52 copied = strscpy(params.name, name, sizeof(params.name)); 53 if (copied < 0) 54 return -EINVAL; 55 56 ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_GET, ¶ms, 57 sizeof(params), &response, sizeof(response)); 58 if (ret < 0) { 59 dev_err(gc->parent, "error getting gpio%d (%s) on EC: %d\n", gpio, name, ret); 60 return ret; 61 } 62 63 return response.val; 64 } 65 66 #define CROS_EC_GPIO_INPUT BIT(8) 67 #define CROS_EC_GPIO_OUTPUT BIT(9) 68 69 static int cros_ec_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) 70 { 71 const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); 72 struct cros_ec_device *cros_ec = gpiochip_get_data(gc); 73 struct ec_params_gpio_get_v1 params = { 74 .subcmd = EC_GPIO_GET_INFO, 75 .get_info.index = gpio, 76 }; 77 struct ec_response_gpio_get_v1 response; 78 int ret; 79 80 ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, 81 sizeof(params), &response, sizeof(response)); 82 if (ret < 0) { 83 dev_err(gc->parent, "error getting direction of gpio%d (%s) on EC: %d\n", gpio, name, ret); 84 return ret; 85 } 86 87 if (response.get_info.flags & CROS_EC_GPIO_INPUT) 88 return GPIO_LINE_DIRECTION_IN; 89 90 if (response.get_info.flags & CROS_EC_GPIO_OUTPUT) 91 return GPIO_LINE_DIRECTION_OUT; 92 93 return -EINVAL; 94 } 95 96 /* Query EC for all gpio line names */ 97 static int cros_ec_gpio_init_names(struct cros_ec_device *cros_ec, struct gpio_chip *gc) 98 { 99 struct ec_params_gpio_get_v1 params = { 100 .subcmd = EC_GPIO_GET_INFO, 101 }; 102 struct ec_response_gpio_get_v1 response; 103 int ret, i; 104 /* EC may not NUL terminate */ 105 size_t name_len = strlen(cros_ec_gpio_prefix) + sizeof(response.get_info.name) + 1; 106 ssize_t copied; 107 const char **names; 108 char *str; 109 110 names = devm_kcalloc(gc->parent, gc->ngpio, sizeof(*names), GFP_KERNEL); 111 if (!names) 112 return -ENOMEM; 113 gc->names = names; 114 115 str = devm_kcalloc(gc->parent, gc->ngpio, name_len, GFP_KERNEL); 116 if (!str) 117 return -ENOMEM; 118 119 /* Get gpio line names one at a time */ 120 for (i = 0; i < gc->ngpio; i++) { 121 params.get_info.index = i; 122 ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, 123 sizeof(params), &response, sizeof(response)); 124 if (ret < 0) { 125 dev_err_probe(gc->parent, ret, "error getting gpio%d info\n", i); 126 return ret; 127 } 128 129 names[i] = str; 130 copied = scnprintf(str, name_len, "%s%s", cros_ec_gpio_prefix, 131 response.get_info.name); 132 if (copied < 0) 133 return copied; 134 135 str += copied + 1; 136 } 137 138 return 0; 139 } 140 141 /* Query EC for number of gpios */ 142 static int cros_ec_gpio_ngpios(struct cros_ec_device *cros_ec) 143 { 144 struct ec_params_gpio_get_v1 params = { 145 .subcmd = EC_GPIO_GET_COUNT, 146 }; 147 struct ec_response_gpio_get_v1 response; 148 int ret; 149 150 ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, 151 sizeof(params), &response, sizeof(response)); 152 if (ret < 0) 153 return ret; 154 155 return response.get_count.val; 156 } 157 158 static int cros_ec_gpio_probe(struct platform_device *pdev) 159 { 160 struct device *dev = &pdev->dev; 161 struct device *parent = dev->parent; 162 struct cros_ec_dev *ec_dev = dev_get_drvdata(parent); 163 struct cros_ec_device *cros_ec = ec_dev->ec_dev; 164 struct gpio_chip *gc; 165 int ngpios; 166 int ret; 167 168 /* Use the fwnode from the protocol device, e.g. cros-ec-spi */ 169 device_set_node(dev, dev_fwnode(cros_ec->dev)); 170 171 ngpios = cros_ec_gpio_ngpios(cros_ec); 172 if (ngpios < 0) { 173 dev_err_probe(dev, ngpios, "error getting gpio count\n"); 174 return ngpios; 175 } 176 177 gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); 178 if (!gc) 179 return -ENOMEM; 180 181 gc->ngpio = ngpios; 182 gc->parent = dev; 183 ret = cros_ec_gpio_init_names(cros_ec, gc); 184 if (ret) 185 return ret; 186 187 gc->can_sleep = true; 188 gc->label = dev_name(dev); 189 gc->base = -1; 190 gc->set = cros_ec_gpio_set; 191 gc->get = cros_ec_gpio_get; 192 gc->get_direction = cros_ec_gpio_get_direction; 193 194 return devm_gpiochip_add_data(dev, gc, cros_ec); 195 } 196 197 static const struct platform_device_id cros_ec_gpio_id[] = { 198 { .name = "cros-ec-gpio" }, 199 { } 200 }; 201 MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id); 202 203 static struct platform_driver cros_ec_gpio_driver = { 204 .probe = cros_ec_gpio_probe, 205 .driver = { 206 .name = "cros-ec-gpio", 207 }, 208 .id_table = cros_ec_gpio_id, 209 }; 210 module_platform_driver(cros_ec_gpio_driver); 211 212 MODULE_DESCRIPTION("ChromeOS EC GPIO Driver"); 213 MODULE_LICENSE("GPL"); 214