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