blob: 54afd44fbd3c53ccbe2c6c46ac1c9cf7bb0f7107 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +02002/*
3 * Qualcomm GPIO driver
4 *
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +02006 */
7
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +02008#include <dm.h>
9#include <errno.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060010#include <asm/global_data.h>
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020011#include <asm/gpio.h>
12#include <asm/io.h>
Caleb Connollyfabb8972023-11-14 12:55:42 +000013#include <mach/gpio.h>
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020014
15DECLARE_GLOBAL_DATA_PTR;
16
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020017/* OE */
18#define GPIO_OE_DISABLE (0x0 << 9)
19#define GPIO_OE_ENABLE (0x1 << 9)
20#define GPIO_OE_MASK (0x1 << 9)
21
22/* GPIO_IN_OUT register shifts. */
23#define GPIO_IN 0
24#define GPIO_OUT 1
25
26struct msm_gpio_bank {
27 phys_addr_t base;
Caleb Connollyfabb8972023-11-14 12:55:42 +000028 const struct msm_pin_data *pin_data;
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020029};
30
Caleb Connollyfabb8972023-11-14 12:55:42 +000031#define GPIO_CONFIG_REG(dev, x) \
32 (qcom_pin_offset(((struct msm_gpio_bank *)dev_get_priv(dev))->pin_data->pin_offsets, x))
33
34#define GPIO_IN_OUT_REG(dev, x) \
35 (GPIO_CONFIG_REG(dev, x) + 0x4)
36
Neil Armstrong6102ac42024-09-11 20:07:14 +020037static void msm_gpio_direction_input_special(struct msm_gpio_bank *priv,
38 unsigned int gpio)
39{
40 unsigned int offset = gpio - priv->pin_data->special_pins_start;
41 const struct msm_special_pin_data *data;
42
43 if (!priv->pin_data->special_pins_data)
44 return;
45
46 data = &priv->pin_data->special_pins_data[offset];
47
48 if (!data->ctl_reg || data->oe_bit >= 31)
49 return;
50
51 /* switch direction */
52 clrsetbits_le32(priv->base + data->ctl_reg,
53 BIT(data->oe_bit), 0);
54}
55
Caleb Connolly241dfda2024-04-03 14:07:43 +020056static void msm_gpio_direction_input(struct udevice *dev, unsigned int gpio)
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020057{
58 struct msm_gpio_bank *priv = dev_get_priv(dev);
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020059
Caleb Connolly190005c2024-02-26 17:26:17 +000060 if (qcom_is_special_pin(priv->pin_data, gpio))
Neil Armstrong6102ac42024-09-11 20:07:14 +020061 msm_gpio_direction_input_special(priv, gpio);
Caleb Connolly190005c2024-02-26 17:26:17 +000062
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020063 /* Disable OE bit */
Caleb Connollyfabb8972023-11-14 12:55:42 +000064 clrsetbits_le32(priv->base + GPIO_CONFIG_REG(dev, gpio),
65 GPIO_OE_MASK, GPIO_OE_DISABLE);
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020066
Caleb Connolly241dfda2024-04-03 14:07:43 +020067 return;
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020068}
69
Neil Armstrong6102ac42024-09-11 20:07:14 +020070static int msm_gpio_set_value_special(struct msm_gpio_bank *priv,
71 unsigned int gpio, int value)
72{
73 unsigned int offset = gpio - priv->pin_data->special_pins_start;
74 const struct msm_special_pin_data *data;
75
76 if (!priv->pin_data->special_pins_data)
77 return 0;
78
79 data = &priv->pin_data->special_pins_data[offset];
80
81 if (!data->io_reg || data->out_bit >= 31)
82 return 0;
83
84 value = !!value;
85 /* set value */
86 writel(value << data->out_bit, priv->base + data->io_reg);
87
88 return 0;
89}
90
Caleb Connolly8fff7eb2023-11-14 12:55:43 +000091static int msm_gpio_set_value(struct udevice *dev, unsigned int gpio, int value)
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020092{
93 struct msm_gpio_bank *priv = dev_get_priv(dev);
94
Caleb Connolly190005c2024-02-26 17:26:17 +000095 if (qcom_is_special_pin(priv->pin_data, gpio))
Neil Armstrong6102ac42024-09-11 20:07:14 +020096 return msm_gpio_set_value_special(priv, gpio, value);
Caleb Connolly190005c2024-02-26 17:26:17 +000097
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +020098 value = !!value;
99 /* set value */
Caleb Connollyfabb8972023-11-14 12:55:42 +0000100 writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_REG(dev, gpio));
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200101
102 return 0;
103}
104
Neil Armstrong6102ac42024-09-11 20:07:14 +0200105static int msm_gpio_direction_output_special(struct msm_gpio_bank *priv,
106 unsigned int gpio,
107 int value)
108{
109 unsigned int offset = gpio - priv->pin_data->special_pins_start;
110 const struct msm_special_pin_data *data;
111
112 if (!priv->pin_data->special_pins_data)
113 return 0;
114
115 data = &priv->pin_data->special_pins_data[offset];
116
117 if (!data->io_reg || data->out_bit >= 31)
118 return 0;
119
120 value = !!value;
121 /* set value */
122 writel(value << data->out_bit, priv->base + data->io_reg);
123
124 if (!data->ctl_reg || data->oe_bit >= 31)
125 return 0;
126
127 /* switch direction */
128 clrsetbits_le32(priv->base + data->ctl_reg,
129 BIT(data->oe_bit), BIT(data->oe_bit));
130
131 return 0;
132}
133
Caleb Connolly8fff7eb2023-11-14 12:55:43 +0000134static int msm_gpio_direction_output(struct udevice *dev, unsigned int gpio,
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200135 int value)
136{
137 struct msm_gpio_bank *priv = dev_get_priv(dev);
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200138
Caleb Connolly190005c2024-02-26 17:26:17 +0000139 if (qcom_is_special_pin(priv->pin_data, gpio))
Neil Armstrong6102ac42024-09-11 20:07:14 +0200140 return msm_gpio_direction_output_special(priv, gpio, value);
Caleb Connolly190005c2024-02-26 17:26:17 +0000141
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200142 value = !!value;
143 /* set value */
Caleb Connollyfabb8972023-11-14 12:55:42 +0000144 writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_REG(dev, gpio));
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200145 /* switch direction */
Caleb Connollyfabb8972023-11-14 12:55:42 +0000146 clrsetbits_le32(priv->base + GPIO_CONFIG_REG(dev, gpio),
147 GPIO_OE_MASK, GPIO_OE_ENABLE);
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200148
149 return 0;
150}
151
Caleb Connolly241dfda2024-04-03 14:07:43 +0200152static int msm_gpio_set_flags(struct udevice *dev, unsigned int gpio, ulong flags)
153{
154 if (flags & GPIOD_IS_OUT_ACTIVE) {
155 return msm_gpio_direction_output(dev, gpio, 1);
156 } else if (flags & GPIOD_IS_OUT) {
157 return msm_gpio_direction_output(dev, gpio, 0);
158 } else if (flags & GPIOD_IS_IN) {
159 msm_gpio_direction_input(dev, gpio);
160 if (flags & GPIOD_PULL_UP)
161 return msm_gpio_set_value(dev, gpio, 1);
162 else if (flags & GPIOD_PULL_DOWN)
163 return msm_gpio_set_value(dev, gpio, 0);
164 }
165
166 return 0;
167}
168
Neil Armstrong6102ac42024-09-11 20:07:14 +0200169static int msm_gpio_get_value_special(struct msm_gpio_bank *priv, unsigned int gpio)
170{
171 unsigned int offset = gpio - priv->pin_data->special_pins_start;
172 const struct msm_special_pin_data *data;
173
174 if (!priv->pin_data->special_pins_data)
Neil Armstrong140e7b12025-04-01 09:45:20 +0200175 return -EINVAL;
Neil Armstrong6102ac42024-09-11 20:07:14 +0200176
177 data = &priv->pin_data->special_pins_data[offset];
178
Neil Armstrong140e7b12025-04-01 09:45:20 +0200179 if (!data->io_reg)
180 return -EINVAL;
181
182 if (data->in_bit >= 31) {
183 if (data->out_bit >= 31)
184 return -EINVAL;
185
186 return !!(readl(priv->base + data->io_reg) >> data->out_bit);
187 }
Neil Armstrong6102ac42024-09-11 20:07:14 +0200188
189 return !!(readl(priv->base + data->io_reg) >> data->in_bit);
190}
191
Caleb Connolly8fff7eb2023-11-14 12:55:43 +0000192static int msm_gpio_get_value(struct udevice *dev, unsigned int gpio)
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200193{
194 struct msm_gpio_bank *priv = dev_get_priv(dev);
195
Caleb Connolly190005c2024-02-26 17:26:17 +0000196 if (qcom_is_special_pin(priv->pin_data, gpio))
Neil Armstrong6102ac42024-09-11 20:07:14 +0200197 return msm_gpio_get_value_special(priv, gpio);
Caleb Connolly190005c2024-02-26 17:26:17 +0000198
Caleb Connollyfabb8972023-11-14 12:55:42 +0000199 return !!(readl(priv->base + GPIO_IN_OUT_REG(dev, gpio)) >> GPIO_IN);
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200200}
201
Neil Armstrongcc9fb232025-04-01 09:45:19 +0200202static int msm_gpio_get_function_special(struct msm_gpio_bank *priv,
203 unsigned int gpio)
204{
205 unsigned int offset = gpio - priv->pin_data->special_pins_start;
206 const struct msm_special_pin_data *data;
207
208 if (!priv->pin_data->special_pins_data)
209 return GPIOF_UNKNOWN;
210
211 data = &priv->pin_data->special_pins_data[offset];
212
213 /* No I/O fields, cannot control/read the I/O value */
214 if (!data->io_reg || (data->out_bit >= 31 && data->in_bit >= 31))
215 return GPIOF_FUNC;
216
217 /* No Output-Enable register, cannot control I/O direction */
218 if (!data->ctl_reg || data->oe_bit >= 31) {
219 if (data->out_bit >= 31)
220 return GPIOF_INPUT;
221 else
222 return GPIOF_OUTPUT;
223 }
224
225 if (readl(priv->base + data->ctl_reg) & BIT(data->oe_bit))
226 return GPIOF_OUTPUT;
227
228 return GPIOF_INPUT;
229}
230
Caleb Connolly8fff7eb2023-11-14 12:55:43 +0000231static int msm_gpio_get_function(struct udevice *dev, unsigned int gpio)
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200232{
233 struct msm_gpio_bank *priv = dev_get_priv(dev);
234
Caleb Connolly190005c2024-02-26 17:26:17 +0000235 if (qcom_is_special_pin(priv->pin_data, gpio))
Neil Armstrongcc9fb232025-04-01 09:45:19 +0200236 return msm_gpio_get_function_special(priv, gpio);
Caleb Connolly190005c2024-02-26 17:26:17 +0000237
Caleb Connollyfabb8972023-11-14 12:55:42 +0000238 if (readl(priv->base + GPIO_CONFIG_REG(dev, gpio)) & GPIO_OE_ENABLE)
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200239 return GPIOF_OUTPUT;
240
241 return GPIOF_INPUT;
242}
243
244static const struct dm_gpio_ops gpio_msm_ops = {
Caleb Connolly241dfda2024-04-03 14:07:43 +0200245 .set_flags = msm_gpio_set_flags,
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200246 .get_value = msm_gpio_get_value,
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200247 .get_function = msm_gpio_get_function,
248};
249
250static int msm_gpio_probe(struct udevice *dev)
251{
252 struct msm_gpio_bank *priv = dev_get_priv(dev);
253
Masahiro Yamadaa89b4de2020-07-17 14:36:48 +0900254 priv->base = dev_read_addr(dev);
Caleb Connollyfabb8972023-11-14 12:55:42 +0000255 priv->pin_data = (struct msm_pin_data *)dev_get_driver_data(dev);
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200256
257 return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0;
258}
259
Simon Glassaad29ae2020-12-03 16:55:21 -0700260static int msm_gpio_of_to_plat(struct udevice *dev)
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200261{
262 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
Caleb Connollyfabb8972023-11-14 12:55:42 +0000263 const struct msm_pin_data *pin_data = (struct msm_pin_data *)dev_get_driver_data(dev);
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200264
Caleb Connollyfabb8972023-11-14 12:55:42 +0000265 /* Get the pin count from the pinctrl driver */
266 uc_priv->gpio_count = pin_data->pin_count;
Simon Glassdd79d6e2017-01-17 16:52:55 -0700267 uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200268 "gpio-bank-name", NULL);
269 if (uc_priv->bank_name == NULL)
270 uc_priv->bank_name = "soc";
271
272 return 0;
273}
274
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200275U_BOOT_DRIVER(gpio_msm) = {
276 .name = "gpio_msm",
277 .id = UCLASS_GPIO,
Simon Glassaad29ae2020-12-03 16:55:21 -0700278 .of_to_plat = msm_gpio_of_to_plat,
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200279 .probe = msm_gpio_probe,
280 .ops = &gpio_msm_ops,
Sumit Gargb7572e52022-07-27 13:52:04 +0530281 .flags = DM_UC_FLAG_SEQ_ALIAS,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700282 .priv_auto = sizeof(struct msm_gpio_bank),
Mateusz Kulikowskia1b3ae92016-03-31 23:12:15 +0200283};