xref: /openbmc/u-boot/drivers/gpio/tegra186_gpio.c (revision 1d6edcbf)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2010-2016, NVIDIA CORPORATION.
4  * (based on tegra_gpio.c)
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <malloc.h>
10 #include <errno.h>
11 #include <fdtdec.h>
12 #include <asm/io.h>
13 #include <asm/bitops.h>
14 #include <asm/gpio.h>
15 #include <dm/device-internal.h>
16 #include <dt-bindings/gpio/gpio.h>
17 #include "tegra186_gpio_priv.h"
18 
19 struct tegra186_gpio_port_data {
20 	const char *name;
21 	uint32_t offset;
22 };
23 
24 struct tegra186_gpio_ctlr_data {
25 	const struct tegra186_gpio_port_data *ports;
26 	uint32_t port_count;
27 };
28 
29 struct tegra186_gpio_platdata {
30 	const char *name;
31 	uint32_t *regs;
32 };
33 
tegra186_gpio_reg(struct udevice * dev,uint32_t reg,uint32_t gpio)34 static uint32_t *tegra186_gpio_reg(struct udevice *dev, uint32_t reg,
35 				   uint32_t gpio)
36 {
37 	struct tegra186_gpio_platdata *plat = dev->platdata;
38 	uint32_t index = (reg + (gpio * TEGRA186_GPIO_PER_GPIO_STRIDE)) / 4;
39 
40 	return &(plat->regs[index]);
41 }
42 
tegra186_gpio_set_out(struct udevice * dev,unsigned offset,bool output)43 static int tegra186_gpio_set_out(struct udevice *dev, unsigned offset,
44 				 bool output)
45 {
46 	uint32_t *reg;
47 	uint32_t rval;
48 
49 	reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_CONTROL, offset);
50 	rval = readl(reg);
51 	if (output)
52 		rval &= ~TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED;
53 	else
54 		rval |= TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED;
55 	writel(rval, reg);
56 
57 	reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
58 	rval = readl(reg);
59 	if (output)
60 		rval |= TEGRA186_GPIO_ENABLE_CONFIG_OUT;
61 	else
62 		rval &= ~TEGRA186_GPIO_ENABLE_CONFIG_OUT;
63 	rval |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE;
64 	writel(rval, reg);
65 
66 	return 0;
67 }
68 
tegra186_gpio_set_val(struct udevice * dev,unsigned offset,bool val)69 static int tegra186_gpio_set_val(struct udevice *dev, unsigned offset, bool val)
70 {
71 	uint32_t *reg;
72 	uint32_t rval;
73 
74 	reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE, offset);
75 	rval = readl(reg);
76 	if (val)
77 		rval |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH;
78 	else
79 		rval &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH;
80 	writel(rval, reg);
81 
82 	return 0;
83 }
84 
tegra186_gpio_direction_input(struct udevice * dev,unsigned offset)85 static int tegra186_gpio_direction_input(struct udevice *dev, unsigned offset)
86 {
87 	return tegra186_gpio_set_out(dev, offset, false);
88 }
89 
tegra186_gpio_direction_output(struct udevice * dev,unsigned offset,int value)90 static int tegra186_gpio_direction_output(struct udevice *dev, unsigned offset,
91 				       int value)
92 {
93 	int ret;
94 
95 	ret = tegra186_gpio_set_val(dev, offset, value != 0);
96 	if (ret)
97 		return ret;
98 	return tegra186_gpio_set_out(dev, offset, true);
99 }
100 
tegra186_gpio_get_value(struct udevice * dev,unsigned offset)101 static int tegra186_gpio_get_value(struct udevice *dev, unsigned offset)
102 {
103 	uint32_t *reg;
104 	uint32_t rval;
105 
106 	reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
107 	rval = readl(reg);
108 
109 	if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT)
110 		reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE,
111 					offset);
112 	else
113 		reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_INPUT, offset);
114 
115 	rval = readl(reg);
116 	return !!rval;
117 }
118 
tegra186_gpio_set_value(struct udevice * dev,unsigned offset,int value)119 static int tegra186_gpio_set_value(struct udevice *dev, unsigned offset,
120 				   int value)
121 {
122 	return tegra186_gpio_set_val(dev, offset, value != 0);
123 }
124 
tegra186_gpio_get_function(struct udevice * dev,unsigned offset)125 static int tegra186_gpio_get_function(struct udevice *dev, unsigned offset)
126 {
127 	uint32_t *reg;
128 	uint32_t rval;
129 
130 	reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
131 	rval = readl(reg);
132 	if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT)
133 		return GPIOF_OUTPUT;
134 	else
135 		return GPIOF_INPUT;
136 }
137 
tegra186_gpio_xlate(struct udevice * dev,struct gpio_desc * desc,struct ofnode_phandle_args * args)138 static int tegra186_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
139 			       struct ofnode_phandle_args *args)
140 {
141 	int gpio, port, ret;
142 
143 	gpio = args->args[0];
144 	port = gpio / TEGRA186_GPIO_PER_GPIO_COUNT;
145 	ret = device_get_child(dev, port, &desc->dev);
146 	if (ret)
147 		return ret;
148 	desc->offset = gpio % TEGRA186_GPIO_PER_GPIO_COUNT;
149 	desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
150 
151 	return 0;
152 }
153 
154 static const struct dm_gpio_ops tegra186_gpio_ops = {
155 	.direction_input	= tegra186_gpio_direction_input,
156 	.direction_output	= tegra186_gpio_direction_output,
157 	.get_value		= tegra186_gpio_get_value,
158 	.set_value		= tegra186_gpio_set_value,
159 	.get_function		= tegra186_gpio_get_function,
160 	.xlate			= tegra186_gpio_xlate,
161 };
162 
163 /**
164  * We have a top-level GPIO device with no actual GPIOs. It has a child device
165  * for each port within the controller.
166  */
tegra186_gpio_bind(struct udevice * parent)167 static int tegra186_gpio_bind(struct udevice *parent)
168 {
169 	struct tegra186_gpio_platdata *parent_plat = parent->platdata;
170 	struct tegra186_gpio_ctlr_data *ctlr_data =
171 		(struct tegra186_gpio_ctlr_data *)dev_get_driver_data(parent);
172 	uint32_t *regs;
173 	int port, ret;
174 
175 	/* If this is a child device, there is nothing to do here */
176 	if (parent_plat)
177 		return 0;
178 
179 	regs = (uint32_t *)devfdt_get_addr_name(parent, "gpio");
180 	if (regs == (uint32_t *)FDT_ADDR_T_NONE)
181 		return -EINVAL;
182 
183 	for (port = 0; port < ctlr_data->port_count; port++) {
184 		struct tegra186_gpio_platdata *plat;
185 		struct udevice *dev;
186 
187 		plat = calloc(1, sizeof(*plat));
188 		if (!plat)
189 			return -ENOMEM;
190 		plat->name = ctlr_data->ports[port].name;
191 		plat->regs = &(regs[ctlr_data->ports[port].offset / 4]);
192 
193 		ret = device_bind(parent, parent->driver, plat->name, plat,
194 				  -1, &dev);
195 		if (ret)
196 			return ret;
197 		dev_set_of_offset(dev, dev_of_offset(parent));
198 	}
199 
200 	return 0;
201 }
202 
tegra186_gpio_probe(struct udevice * dev)203 static int tegra186_gpio_probe(struct udevice *dev)
204 {
205 	struct tegra186_gpio_platdata *plat = dev->platdata;
206 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
207 
208 	/* Only child devices have ports */
209 	if (!plat)
210 		return 0;
211 
212 	uc_priv->gpio_count = TEGRA186_GPIO_PER_GPIO_COUNT;
213 	uc_priv->bank_name = plat->name;
214 
215 	return 0;
216 }
217 
218 static const struct tegra186_gpio_port_data tegra186_gpio_main_ports[] = {
219 	{"A",  0x2000},
220 	{"B",  0x3000},
221 	{"C",  0x3200},
222 	{"D",  0x3400},
223 	{"E",  0x2200},
224 	{"F",  0x2400},
225 	{"G",  0x4200},
226 	{"H",  0x1000},
227 	{"I",  0x0800},
228 	{"J",  0x5000},
229 	{"K",  0x5200},
230 	{"L",  0x1200},
231 	{"M",  0x5600},
232 	{"N",  0x0000},
233 	{"O",  0x0200},
234 	{"P",  0x4000},
235 	{"Q",  0x0400},
236 	{"R",  0x0a00},
237 	{"T",  0x0600},
238 	{"X",  0x1400},
239 	{"Y",  0x1600},
240 	{"BB", 0x2600},
241 	{"CC", 0x5400},
242 };
243 
244 static const struct tegra186_gpio_ctlr_data tegra186_gpio_main_data = {
245 	.ports = tegra186_gpio_main_ports,
246 	.port_count = ARRAY_SIZE(tegra186_gpio_main_ports),
247 };
248 
249 static const struct tegra186_gpio_port_data tegra186_gpio_aon_ports[] = {
250 	{"S",  0x0200},
251 	{"U",  0x0400},
252 	{"V",  0x0800},
253 	{"W",  0x0a00},
254 	{"Z",  0x0e00},
255 	{"AA", 0x0c00},
256 	{"EE", 0x0600},
257 	{"FF", 0x0000},
258 };
259 
260 static const struct tegra186_gpio_ctlr_data tegra186_gpio_aon_data = {
261 	.ports = tegra186_gpio_aon_ports,
262 	.port_count = ARRAY_SIZE(tegra186_gpio_aon_ports),
263 };
264 
265 static const struct udevice_id tegra186_gpio_ids[] = {
266 	{
267 		.compatible = "nvidia,tegra186-gpio",
268 		.data = (ulong)&tegra186_gpio_main_data,
269 	},
270 	{
271 		.compatible = "nvidia,tegra186-gpio-aon",
272 		.data = (ulong)&tegra186_gpio_aon_data,
273 	},
274 	{ }
275 };
276 
277 U_BOOT_DRIVER(tegra186_gpio) = {
278 	.name = "tegra186_gpio",
279 	.id = UCLASS_GPIO,
280 	.of_match = tegra186_gpio_ids,
281 	.bind = tegra186_gpio_bind,
282 	.probe = tegra186_gpio_probe,
283 	.ops = &tegra186_gpio_ops,
284 };
285