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