gpio: vf610: set value before the direction to avoid a glitch
[platform/kernel/linux-rpi.git] / drivers / gpio / gpio-tps65219.c
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 (IS_ENABLED(CONFIG_DEBUG_GPIO)) {
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         }
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");