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