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