gpio-tps65218.c (5826B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright 2015 Verifone Int. 4 * 5 * Author: Nicolas Saenz Julienne <nicolassaenzj@gmail.com> 6 * 7 * This driver is based on the gpio-tps65912 implementation. 8 */ 9 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <linux/errno.h> 13#include <linux/gpio/driver.h> 14#include <linux/platform_device.h> 15#include <linux/regmap.h> 16#include <linux/mfd/tps65218.h> 17 18struct tps65218_gpio { 19 struct tps65218 *tps65218; 20 struct gpio_chip gpio_chip; 21}; 22 23static int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset) 24{ 25 struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); 26 struct tps65218 *tps65218 = tps65218_gpio->tps65218; 27 unsigned int val; 28 int ret; 29 30 ret = regmap_read(tps65218->regmap, TPS65218_REG_ENABLE2, &val); 31 if (ret) 32 return ret; 33 34 return !!(val & (TPS65218_ENABLE2_GPIO1 << offset)); 35} 36 37static void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset, 38 int value) 39{ 40 struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); 41 struct tps65218 *tps65218 = tps65218_gpio->tps65218; 42 43 if (value) 44 tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, 45 TPS65218_ENABLE2_GPIO1 << offset, 46 TPS65218_ENABLE2_GPIO1 << offset, 47 TPS65218_PROTECT_L1); 48 else 49 tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, 50 TPS65218_ENABLE2_GPIO1 << offset, 51 TPS65218_PROTECT_L1); 52} 53 54static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset, 55 int value) 56{ 57 /* Only drives GPOs */ 58 tps65218_gpio_set(gc, offset, value); 59 return 0; 60} 61 62static int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset) 63{ 64 return -EPERM; 65} 66 67static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) 68{ 69 struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); 70 struct tps65218 *tps65218 = tps65218_gpio->tps65218; 71 int ret; 72 73 if (gpiochip_line_is_open_source(gc, offset)) { 74 dev_err(gc->parent, "can't work as open source\n"); 75 return -EINVAL; 76 } 77 78 switch (offset) { 79 case 0: 80 if (!gpiochip_line_is_open_drain(gc, offset)) { 81 dev_err(gc->parent, "GPO1 works only as open drain\n"); 82 return -EINVAL; 83 } 84 85 /* Disable sequencer for GPO1 */ 86 ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7, 87 TPS65218_SEQ7_GPO1_SEQ_MASK, 88 TPS65218_PROTECT_L1); 89 if (ret) 90 return ret; 91 92 /* Setup GPO1 */ 93 ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1, 94 TPS65218_CONFIG1_IO1_SEL, 95 TPS65218_PROTECT_L1); 96 if (ret) 97 return ret; 98 99 break; 100 case 1: 101 /* Setup GPO2 */ 102 ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1, 103 TPS65218_CONFIG1_IO1_SEL, 104 TPS65218_PROTECT_L1); 105 if (ret) 106 return ret; 107 108 break; 109 110 case 2: 111 if (!gpiochip_line_is_open_drain(gc, offset)) { 112 dev_err(gc->parent, "GPO3 works only as open drain\n"); 113 return -EINVAL; 114 } 115 116 /* Disable sequencer for GPO3 */ 117 ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7, 118 TPS65218_SEQ7_GPO3_SEQ_MASK, 119 TPS65218_PROTECT_L1); 120 if (ret) 121 return ret; 122 123 /* Setup GPO3 */ 124 ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG2, 125 TPS65218_CONFIG2_DC12_RST, 126 TPS65218_PROTECT_L1); 127 if (ret) 128 return ret; 129 130 break; 131 default: 132 return -EINVAL; 133 } 134 135 return 0; 136} 137 138static int tps65218_gpio_set_config(struct gpio_chip *gc, unsigned offset, 139 unsigned long config) 140{ 141 struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); 142 struct tps65218 *tps65218 = tps65218_gpio->tps65218; 143 enum pin_config_param param = pinconf_to_config_param(config); 144 145 switch (offset) { 146 case 0: 147 case 2: 148 /* GPO1 is hardwired to be open drain */ 149 if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) 150 return 0; 151 return -ENOTSUPP; 152 case 1: 153 /* GPO2 is push-pull by default, can be set as open drain. */ 154 if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) 155 return tps65218_clear_bits(tps65218, 156 TPS65218_REG_CONFIG1, 157 TPS65218_CONFIG1_GPO2_BUF, 158 TPS65218_PROTECT_L1); 159 if (param == PIN_CONFIG_DRIVE_PUSH_PULL) 160 return tps65218_set_bits(tps65218, 161 TPS65218_REG_CONFIG1, 162 TPS65218_CONFIG1_GPO2_BUF, 163 TPS65218_CONFIG1_GPO2_BUF, 164 TPS65218_PROTECT_L1); 165 return -ENOTSUPP; 166 default: 167 break; 168 } 169 return -ENOTSUPP; 170} 171 172static const struct gpio_chip template_chip = { 173 .label = "gpio-tps65218", 174 .owner = THIS_MODULE, 175 .request = tps65218_gpio_request, 176 .direction_output = tps65218_gpio_output, 177 .direction_input = tps65218_gpio_input, 178 .get = tps65218_gpio_get, 179 .set = tps65218_gpio_set, 180 .set_config = tps65218_gpio_set_config, 181 .can_sleep = true, 182 .ngpio = 3, 183 .base = -1, 184}; 185 186static int tps65218_gpio_probe(struct platform_device *pdev) 187{ 188 struct tps65218 *tps65218 = dev_get_drvdata(pdev->dev.parent); 189 struct tps65218_gpio *tps65218_gpio; 190 191 tps65218_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65218_gpio), 192 GFP_KERNEL); 193 if (!tps65218_gpio) 194 return -ENOMEM; 195 196 tps65218_gpio->tps65218 = tps65218; 197 tps65218_gpio->gpio_chip = template_chip; 198 tps65218_gpio->gpio_chip.parent = &pdev->dev; 199 200 return devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip, 201 tps65218_gpio); 202} 203 204static const struct of_device_id tps65218_dt_match[] = { 205 { .compatible = "ti,tps65218-gpio" }, 206 { } 207}; 208MODULE_DEVICE_TABLE(of, tps65218_dt_match); 209 210static const struct platform_device_id tps65218_gpio_id_table[] = { 211 { "tps65218-gpio", }, 212 { /* sentinel */ } 213}; 214MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table); 215 216static struct platform_driver tps65218_gpio_driver = { 217 .driver = { 218 .name = "tps65218-gpio", 219 .of_match_table = of_match_ptr(tps65218_dt_match) 220 }, 221 .probe = tps65218_gpio_probe, 222 .id_table = tps65218_gpio_id_table, 223}; 224 225module_platform_driver(tps65218_gpio_driver); 226 227MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>"); 228MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs"); 229MODULE_LICENSE("GPL v2");