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