1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * GPIO driver for TI TPS65219 PMICs 4 * 5 * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/ 6 */ 7 8 #include <linux/bits.h> 9 #include <linux/gpio/driver.h> 10 #include <linux/mfd/tps65219.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 #include <linux/regmap.h> 14 15 #define TPS65219_GPIO0_DIR_MASK BIT(3) 16 #define TPS65219_GPIO0_OFFSET 2 17 #define TPS65219_GPIO0_IDX 0 18 #define TPS65219_GPIO_DIR_IN 1 19 #define TPS65219_GPIO_DIR_OUT 0 20 21 struct tps65219_gpio { 22 struct gpio_chip gpio_chip; 23 struct tps65219 *tps; 24 }; 25 26 static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) 27 { 28 struct tps65219_gpio *gpio = gpiochip_get_data(gc); 29 int ret, val; 30 31 if (offset != TPS65219_GPIO0_IDX) 32 return GPIO_LINE_DIRECTION_OUT; 33 34 ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); 35 if (ret) 36 return ret; 37 38 return !!(val & TPS65219_GPIO0_DIR_MASK); 39 } 40 41 static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) 42 { 43 struct tps65219_gpio *gpio = gpiochip_get_data(gc); 44 struct device *dev = gpio->tps->dev; 45 int ret, val; 46 47 if (offset != TPS65219_GPIO0_IDX) { 48 dev_err(dev, "GPIO%d is output only, cannot get\n", offset); 49 return -ENOTSUPP; 50 } 51 52 ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_CTRL, &val); 53 if (ret) 54 return ret; 55 56 ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK)); 57 dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret); 58 59 /* 60 * Depending on NVM config, return an error if direction is output, otherwise the GPIO0 61 * status bit. 62 */ 63 64 if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) 65 return -ENOTSUPP; 66 67 return ret; 68 } 69 70 static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) 71 { 72 struct tps65219_gpio *gpio = gpiochip_get_data(gc); 73 struct device *dev = gpio->tps->dev; 74 int v, mask, bit; 75 76 bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1; 77 78 mask = BIT(bit); 79 v = value ? mask : 0; 80 81 if (regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, mask, v)) 82 dev_err(dev, "GPIO%d, set to value %d failed.\n", offset, value); 83 } 84 85 static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, 86 unsigned int direction) 87 { 88 struct tps65219_gpio *gpio = gpiochip_get_data(gc); 89 struct device *dev = gpio->tps->dev; 90 91 /* 92 * Documentation is stating that GPIO0 direction must not be changed in Linux: 93 * Table 8-34. MFP_1_CONFIG(3): MULTI_DEVICE_ENABLE, should only be changed in INITIALIZE 94 * state (prior to ON Request). 95 * Set statically by NVM, changing direction in application can cause a hang. 96 * Below can be used for test purpose only. 97 */ 98 99 #if 0 100 int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, 101 TPS65219_GPIO0_DIR_MASK, direction); 102 if (ret) { 103 dev_err(dev, 104 "GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n", 105 direction, offset); 106 return ret; 107 } 108 #endif 109 110 dev_err(dev, 111 "GPIO%d direction set by NVM, change to %u failed, not allowed by specification\n", 112 offset, direction); 113 114 return -ENOTSUPP; 115 } 116 117 static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) 118 { 119 struct tps65219_gpio *gpio = gpiochip_get_data(gc); 120 struct device *dev = gpio->tps->dev; 121 122 if (offset != TPS65219_GPIO0_IDX) { 123 dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset); 124 return -ENOTSUPP; 125 } 126 127 if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_IN) 128 return 0; 129 130 return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_IN); 131 } 132 133 static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) 134 { 135 tps65219_gpio_set(gc, offset, value); 136 if (offset != TPS65219_GPIO0_IDX) 137 return 0; 138 139 if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) 140 return 0; 141 142 return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_OUT); 143 } 144 145 static const struct gpio_chip tps65219_template_chip = { 146 .label = "tps65219-gpio", 147 .owner = THIS_MODULE, 148 .get_direction = tps65219_gpio_get_direction, 149 .direction_input = tps65219_gpio_direction_input, 150 .direction_output = tps65219_gpio_direction_output, 151 .get = tps65219_gpio_get, 152 .set = tps65219_gpio_set, 153 .base = -1, 154 .ngpio = 3, 155 .can_sleep = true, 156 }; 157 158 static int tps65219_gpio_probe(struct platform_device *pdev) 159 { 160 struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent); 161 struct tps65219_gpio *gpio; 162 163 gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); 164 if (!gpio) 165 return -ENOMEM; 166 167 gpio->tps = tps; 168 gpio->gpio_chip = tps65219_template_chip; 169 gpio->gpio_chip.parent = tps->dev; 170 171 return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio); 172 } 173 174 static struct platform_driver tps65219_gpio_driver = { 175 .driver = { 176 .name = "tps65219-gpio", 177 }, 178 .probe = tps65219_gpio_probe, 179 }; 180 module_platform_driver(tps65219_gpio_driver); 181 182 MODULE_ALIAS("platform:tps65219-gpio"); 183 MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>"); 184 MODULE_DESCRIPTION("TPS65219 GPIO driver"); 185 MODULE_LICENSE("GPL"); 186