]>
Commit | Line | Data |
---|---|---|
8d30fcd9 SG |
1 | /* |
2 | * Copyright (c) 2011 The Chromium OS Authors. | |
1a459660 | 3 | * SPDX-License-Identifier: GPL-2.0+ |
8d30fcd9 SG |
4 | */ |
5 | ||
6 | #include <common.h> | |
e2d8a714 SG |
7 | #include <dm.h> |
8 | #include <fdtdec.h> | |
9 | #include <malloc.h> | |
8d30fcd9 | 10 | #include <asm/gpio.h> |
3669e0e7 | 11 | #include <dt-bindings/gpio/gpio.h> |
8d30fcd9 | 12 | |
e2d8a714 SG |
13 | DECLARE_GLOBAL_DATA_PTR; |
14 | ||
8d30fcd9 SG |
15 | /* Flags for each GPIO */ |
16 | #define GPIOF_OUTPUT (1 << 0) /* Currently set as an output */ | |
17 | #define GPIOF_HIGH (1 << 1) /* Currently set high */ | |
743268f5 | 18 | #define GPIOF_ODR (1 << 2) /* Currently set to open drain mode */ |
8d30fcd9 SG |
19 | |
20 | struct gpio_state { | |
21 | const char *label; /* label given by requester */ | |
22 | u8 flags; /* flags (GPIOF_...) */ | |
23 | }; | |
24 | ||
8d30fcd9 | 25 | /* Access routines for GPIO state */ |
54c5d08a | 26 | static u8 *get_gpio_flags(struct udevice *dev, unsigned offset) |
8d30fcd9 | 27 | { |
e564f054 | 28 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
e2d8a714 SG |
29 | struct gpio_state *state = dev_get_priv(dev); |
30 | ||
31 | if (offset >= uc_priv->gpio_count) { | |
8d30fcd9 | 32 | static u8 invalid_flags; |
e2d8a714 | 33 | printf("sandbox_gpio: error: invalid gpio %u\n", offset); |
8d30fcd9 SG |
34 | return &invalid_flags; |
35 | } | |
36 | ||
e2d8a714 | 37 | return &state[offset].flags; |
8d30fcd9 SG |
38 | } |
39 | ||
54c5d08a | 40 | static int get_gpio_flag(struct udevice *dev, unsigned offset, int flag) |
8d30fcd9 | 41 | { |
e2d8a714 | 42 | return (*get_gpio_flags(dev, offset) & flag) != 0; |
8d30fcd9 SG |
43 | } |
44 | ||
54c5d08a | 45 | static int set_gpio_flag(struct udevice *dev, unsigned offset, int flag, |
e2d8a714 | 46 | int value) |
8d30fcd9 | 47 | { |
e2d8a714 | 48 | u8 *gpio = get_gpio_flags(dev, offset); |
8d30fcd9 SG |
49 | |
50 | if (value) | |
51 | *gpio |= flag; | |
52 | else | |
53 | *gpio &= ~flag; | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
8d30fcd9 SG |
58 | /* |
59 | * Back-channel sandbox-internal-only access to GPIO state | |
60 | */ | |
61 | ||
54c5d08a | 62 | int sandbox_gpio_get_value(struct udevice *dev, unsigned offset) |
8d30fcd9 | 63 | { |
e2d8a714 SG |
64 | if (get_gpio_flag(dev, offset, GPIOF_OUTPUT)) |
65 | debug("sandbox_gpio: get_value on output gpio %u\n", offset); | |
66 | return get_gpio_flag(dev, offset, GPIOF_HIGH); | |
8d30fcd9 SG |
67 | } |
68 | ||
54c5d08a | 69 | int sandbox_gpio_set_value(struct udevice *dev, unsigned offset, int value) |
8d30fcd9 | 70 | { |
e2d8a714 | 71 | return set_gpio_flag(dev, offset, GPIOF_HIGH, value); |
8d30fcd9 SG |
72 | } |
73 | ||
743268f5 | 74 | int sandbox_gpio_get_open_drain(struct udevice *dev, unsigned offset) |
75 | { | |
76 | return get_gpio_flag(dev, offset, GPIOF_ODR); | |
77 | } | |
78 | ||
79 | int sandbox_gpio_set_open_drain(struct udevice *dev, unsigned offset, int value) | |
80 | { | |
81 | return set_gpio_flag(dev, offset, GPIOF_ODR, value); | |
82 | } | |
83 | ||
54c5d08a | 84 | int sandbox_gpio_get_direction(struct udevice *dev, unsigned offset) |
8d30fcd9 | 85 | { |
e2d8a714 | 86 | return get_gpio_flag(dev, offset, GPIOF_OUTPUT); |
8d30fcd9 SG |
87 | } |
88 | ||
54c5d08a | 89 | int sandbox_gpio_set_direction(struct udevice *dev, unsigned offset, int output) |
8d30fcd9 | 90 | { |
e2d8a714 | 91 | return set_gpio_flag(dev, offset, GPIOF_OUTPUT, output); |
8d30fcd9 SG |
92 | } |
93 | ||
94 | /* | |
95 | * These functions implement the public interface within U-Boot | |
96 | */ | |
97 | ||
e2d8a714 | 98 | /* set GPIO port 'offset' as an input */ |
54c5d08a | 99 | static int sb_gpio_direction_input(struct udevice *dev, unsigned offset) |
8d30fcd9 | 100 | { |
e2d8a714 | 101 | debug("%s: offset:%u\n", __func__, offset); |
8d30fcd9 | 102 | |
e2d8a714 | 103 | return sandbox_gpio_set_direction(dev, offset, 0); |
8d30fcd9 SG |
104 | } |
105 | ||
e2d8a714 | 106 | /* set GPIO port 'offset' as an output, with polarity 'value' */ |
54c5d08a | 107 | static int sb_gpio_direction_output(struct udevice *dev, unsigned offset, |
e2d8a714 | 108 | int value) |
8d30fcd9 | 109 | { |
e2d8a714 | 110 | debug("%s: offset:%u, value = %d\n", __func__, offset, value); |
8d30fcd9 | 111 | |
e2d8a714 SG |
112 | return sandbox_gpio_set_direction(dev, offset, 1) | |
113 | sandbox_gpio_set_value(dev, offset, value); | |
8d30fcd9 SG |
114 | } |
115 | ||
e2d8a714 | 116 | /* read GPIO IN value of port 'offset' */ |
54c5d08a | 117 | static int sb_gpio_get_value(struct udevice *dev, unsigned offset) |
8d30fcd9 | 118 | { |
e2d8a714 | 119 | debug("%s: offset:%u\n", __func__, offset); |
8d30fcd9 | 120 | |
e2d8a714 | 121 | return sandbox_gpio_get_value(dev, offset); |
8d30fcd9 SG |
122 | } |
123 | ||
e2d8a714 | 124 | /* write GPIO OUT value to port 'offset' */ |
54c5d08a | 125 | static int sb_gpio_set_value(struct udevice *dev, unsigned offset, int value) |
8d30fcd9 | 126 | { |
e2d8a714 | 127 | debug("%s: offset:%u, value = %d\n", __func__, offset, value); |
8d30fcd9 | 128 | |
e2d8a714 SG |
129 | if (!sandbox_gpio_get_direction(dev, offset)) { |
130 | printf("sandbox_gpio: error: set_value on input gpio %u\n", | |
131 | offset); | |
8d30fcd9 SG |
132 | return -1; |
133 | } | |
134 | ||
e2d8a714 | 135 | return sandbox_gpio_set_value(dev, offset, value); |
8d30fcd9 SG |
136 | } |
137 | ||
743268f5 | 138 | /* read GPIO ODR value of port 'offset' */ |
139 | static int sb_gpio_get_open_drain(struct udevice *dev, unsigned offset) | |
140 | { | |
141 | debug("%s: offset:%u\n", __func__, offset); | |
142 | ||
143 | return sandbox_gpio_get_open_drain(dev, offset); | |
144 | } | |
145 | ||
146 | /* write GPIO ODR value to port 'offset' */ | |
147 | static int sb_gpio_set_open_drain(struct udevice *dev, unsigned offset, int value) | |
148 | { | |
149 | debug("%s: offset:%u, value = %d\n", __func__, offset, value); | |
150 | ||
151 | if (!sandbox_gpio_get_direction(dev, offset)) { | |
152 | printf("sandbox_gpio: error: set_open_drain on input gpio %u\n", | |
153 | offset); | |
154 | return -1; | |
155 | } | |
156 | ||
157 | return sandbox_gpio_set_open_drain(dev, offset, value); | |
158 | } | |
159 | ||
699ea960 SG |
160 | static int sb_gpio_get_function(struct udevice *dev, unsigned offset) |
161 | { | |
162 | if (get_gpio_flag(dev, offset, GPIOF_OUTPUT)) | |
163 | return GPIOF_OUTPUT; | |
164 | return GPIOF_INPUT; | |
165 | } | |
166 | ||
3669e0e7 SG |
167 | static int sb_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, |
168 | struct fdtdec_phandle_args *args) | |
169 | { | |
170 | desc->offset = args->args[0]; | |
171 | if (args->args_count < 2) | |
172 | return 0; | |
173 | if (args->args[1] & GPIO_ACTIVE_LOW) | |
174 | desc->flags |= GPIOD_ACTIVE_LOW; | |
175 | if (args->args[1] & 2) | |
176 | desc->flags |= GPIOD_IS_IN; | |
177 | if (args->args[1] & 4) | |
178 | desc->flags |= GPIOD_IS_OUT; | |
179 | if (args->args[1] & 8) | |
180 | desc->flags |= GPIOD_IS_OUT_ACTIVE; | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
e2d8a714 | 185 | static const struct dm_gpio_ops gpio_sandbox_ops = { |
e2d8a714 SG |
186 | .direction_input = sb_gpio_direction_input, |
187 | .direction_output = sb_gpio_direction_output, | |
188 | .get_value = sb_gpio_get_value, | |
189 | .set_value = sb_gpio_set_value, | |
743268f5 | 190 | .get_open_drain = sb_gpio_get_open_drain, |
191 | .set_open_drain = sb_gpio_set_open_drain, | |
699ea960 | 192 | .get_function = sb_gpio_get_function, |
3669e0e7 | 193 | .xlate = sb_gpio_xlate, |
e2d8a714 SG |
194 | }; |
195 | ||
54c5d08a | 196 | static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev) |
e2d8a714 | 197 | { |
e564f054 | 198 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
8d30fcd9 | 199 | |
e160f7d4 | 200 | uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), |
e2d8a714 | 201 | "num-gpios", 0); |
e160f7d4 | 202 | uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), |
e2d8a714 | 203 | "gpio-bank-name", NULL); |
8d30fcd9 | 204 | |
e2d8a714 SG |
205 | return 0; |
206 | } | |
207 | ||
54c5d08a | 208 | static int gpio_sandbox_probe(struct udevice *dev) |
e2d8a714 | 209 | { |
e564f054 | 210 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
e2d8a714 | 211 | |
e160f7d4 | 212 | if (dev_of_offset(dev) == -1) { |
e2d8a714 SG |
213 | /* Tell the uclass how many GPIOs we have */ |
214 | uc_priv->gpio_count = CONFIG_SANDBOX_GPIO_COUNT; | |
8d30fcd9 | 215 | } |
e2d8a714 SG |
216 | |
217 | dev->priv = calloc(sizeof(struct gpio_state), uc_priv->gpio_count); | |
218 | ||
219 | return 0; | |
8d30fcd9 | 220 | } |
e2d8a714 | 221 | |
62cb89d5 SG |
222 | static int gpio_sandbox_remove(struct udevice *dev) |
223 | { | |
224 | free(dev->priv); | |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
ae7f4513 | 229 | static const struct udevice_id sandbox_gpio_ids[] = { |
e2d8a714 SG |
230 | { .compatible = "sandbox,gpio" }, |
231 | { } | |
232 | }; | |
233 | ||
234 | U_BOOT_DRIVER(gpio_sandbox) = { | |
235 | .name = "gpio_sandbox", | |
236 | .id = UCLASS_GPIO, | |
237 | .of_match = sandbox_gpio_ids, | |
238 | .ofdata_to_platdata = sandbox_gpio_ofdata_to_platdata, | |
239 | .probe = gpio_sandbox_probe, | |
62cb89d5 | 240 | .remove = gpio_sandbox_remove, |
e2d8a714 SG |
241 | .ops = &gpio_sandbox_ops, |
242 | }; |