]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
074a1fdd SW |
2 | /* |
3 | * Copyright (c) 2010-2016, NVIDIA CORPORATION. | |
4 | * (based on tegra_gpio.c) | |
074a1fdd SW |
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 | ||
074a1fdd SW |
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 | ||
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 | ||
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 | ||
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 | ||
85 | static int tegra186_gpio_direction_input(struct udevice *dev, unsigned offset) | |
86 | { | |
87 | return tegra186_gpio_set_out(dev, offset, false); | |
88 | } | |
89 | ||
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 | ||
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 | ||
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 | ||
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 | ||
138 | static int tegra186_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, | |
3a57123e | 139 | struct ofnode_phandle_args *args) |
074a1fdd SW |
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 | */ | |
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 | ||
a821c4af | 179 | regs = (uint32_t *)devfdt_get_addr_name(parent, "gpio"); |
074a1fdd | 180 | if (regs == (uint32_t *)FDT_ADDR_T_NONE) |
7c84319a | 181 | return -EINVAL; |
074a1fdd SW |
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; | |
e160f7d4 | 197 | dev_set_of_offset(dev, dev_of_offset(parent)); |
074a1fdd SW |
198 | } |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
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, | |
074a1fdd | 284 | }; |