]>
Commit | Line | Data |
---|---|---|
a972b8d7 MF |
1 | /* |
2 | * Control GPIO pins on the fly | |
3 | * | |
4 | * Copyright (c) 2008-2011 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2 or later. | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <command.h> | |
9165e842 | 11 | #include <errno.h> |
95a260a9 | 12 | #include <dm.h> |
a972b8d7 MF |
13 | #include <asm/gpio.h> |
14 | ||
fd11bea2 IC |
15 | int __weak name_to_gpio(const char *name) |
16 | { | |
17 | return simple_strtoul(name, NULL, 10); | |
18 | } | |
a972b8d7 MF |
19 | |
20 | enum gpio_cmd { | |
21 | GPIO_INPUT, | |
22 | GPIO_SET, | |
23 | GPIO_CLEAR, | |
24 | GPIO_TOGGLE, | |
25 | }; | |
26 | ||
95a260a9 | 27 | #if defined(CONFIG_DM_GPIO) && !defined(gpio_status) |
89e64054 | 28 | static const char * const gpio_function[GPIOF_COUNT] = { |
95a260a9 SG |
29 | "input", |
30 | "output", | |
89e64054 | 31 | "unused", |
95a260a9 | 32 | "unknown", |
89e64054 | 33 | "func", |
95a260a9 SG |
34 | }; |
35 | ||
89e64054 SG |
36 | /* A few flags used by show_gpio() */ |
37 | enum { | |
38 | FLAG_SHOW_ALL = 1 << 0, | |
39 | FLAG_SHOW_BANK = 1 << 1, | |
40 | FLAG_SHOW_NEWLINE = 1 << 2, | |
41 | }; | |
42 | ||
43 | static void show_gpio(struct udevice *dev, const char *bank_name, int offset, | |
44 | int *flagsp) | |
95a260a9 SG |
45 | { |
46 | struct dm_gpio_ops *ops = gpio_get_ops(dev); | |
89e64054 | 47 | int func = GPIOF_UNKNOWN; |
95a260a9 SG |
48 | char buf[80]; |
49 | int ret; | |
50 | ||
89e64054 SG |
51 | BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function)); |
52 | ||
53 | if (ops->get_function) { | |
54 | ret = ops->get_function(dev, offset); | |
55 | if (ret >= 0 && ret < ARRAY_SIZE(gpio_function)) | |
56 | func = ret; | |
57 | } | |
58 | if (!(*flagsp & FLAG_SHOW_ALL) && func == GPIOF_UNUSED) | |
59 | return; | |
60 | if ((*flagsp & FLAG_SHOW_BANK) && bank_name) { | |
61 | if (*flagsp & FLAG_SHOW_NEWLINE) { | |
62 | putc('\n'); | |
63 | *flagsp &= ~FLAG_SHOW_NEWLINE; | |
64 | } | |
65 | printf("Bank %s:\n", bank_name); | |
66 | *flagsp &= ~FLAG_SHOW_BANK; | |
67 | } | |
95a260a9 SG |
68 | *buf = '\0'; |
69 | if (ops->get_state) { | |
70 | ret = ops->get_state(dev, offset, buf, sizeof(buf)); | |
71 | if (ret) { | |
72 | puts("<unknown>"); | |
73 | return; | |
74 | } | |
75 | } else { | |
95a260a9 SG |
76 | sprintf(buf, "%s%u: %8s %d", bank_name, offset, |
77 | gpio_function[func], ops->get_value(dev, offset)); | |
78 | } | |
79 | ||
80 | puts(buf); | |
81 | puts("\n"); | |
82 | } | |
83 | ||
89e64054 | 84 | static int do_gpio_status(bool all, const char *gpio_name) |
95a260a9 | 85 | { |
54c5d08a | 86 | struct udevice *dev; |
89e64054 SG |
87 | int banklen; |
88 | int flags; | |
95a260a9 SG |
89 | int ret; |
90 | ||
89e64054 | 91 | flags = 0; |
95a260a9 SG |
92 | if (gpio_name && !*gpio_name) |
93 | gpio_name = NULL; | |
94 | for (ret = uclass_first_device(UCLASS_GPIO, &dev); | |
95 | dev; | |
96 | ret = uclass_next_device(&dev)) { | |
97 | const char *bank_name; | |
98 | int num_bits; | |
99 | ||
89e64054 SG |
100 | flags |= FLAG_SHOW_BANK; |
101 | if (all) | |
102 | flags |= FLAG_SHOW_ALL; | |
95a260a9 | 103 | bank_name = gpio_get_bank_info(dev, &num_bits); |
89e64054 SG |
104 | if (!num_bits) |
105 | continue; | |
106 | banklen = bank_name ? strlen(bank_name) : 0; | |
95a260a9 SG |
107 | |
108 | if (!gpio_name || !bank_name || | |
89e64054 | 109 | !strncmp(gpio_name, bank_name, banklen)) { |
95a260a9 SG |
110 | const char *p = NULL; |
111 | int offset; | |
112 | ||
89e64054 SG |
113 | p = gpio_name + banklen; |
114 | if (gpio_name && *p) { | |
95a260a9 | 115 | offset = simple_strtoul(p, NULL, 10); |
89e64054 | 116 | show_gpio(dev, bank_name, offset, &flags); |
95a260a9 | 117 | } else { |
89e64054 SG |
118 | for (offset = 0; offset < num_bits; offset++) { |
119 | show_gpio(dev, bank_name, offset, | |
120 | &flags); | |
121 | } | |
95a260a9 SG |
122 | } |
123 | } | |
89e64054 SG |
124 | /* Add a newline between bank names */ |
125 | if (!(flags & FLAG_SHOW_BANK)) | |
126 | flags |= FLAG_SHOW_NEWLINE; | |
95a260a9 SG |
127 | } |
128 | ||
129 | return ret; | |
130 | } | |
131 | #endif | |
132 | ||
a972b8d7 MF |
133 | static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
134 | { | |
95a260a9 | 135 | unsigned int gpio; |
a972b8d7 MF |
136 | enum gpio_cmd sub_cmd; |
137 | ulong value; | |
95a260a9 | 138 | const char *str_cmd, *str_gpio = NULL; |
9165e842 | 139 | int ret; |
95a260a9 | 140 | #ifdef CONFIG_DM_GPIO |
89e64054 | 141 | bool all = false; |
95a260a9 | 142 | #endif |
a972b8d7 | 143 | |
95a260a9 SG |
144 | if (argc < 2) |
145 | show_usage: | |
146 | return CMD_RET_USAGE; | |
147 | str_cmd = argv[1]; | |
89e64054 SG |
148 | argc -= 2; |
149 | argv += 2; | |
150 | #ifdef CONFIG_DM_GPIO | |
151 | if (argc > 0 && !strcmp(*argv, "-a")) { | |
152 | all = true; | |
153 | argc--; | |
154 | argv++; | |
155 | } | |
156 | #endif | |
157 | if (argc > 0) | |
158 | str_gpio = *argv; | |
95a260a9 SG |
159 | if (!strcmp(str_cmd, "status")) { |
160 | /* Support deprecated gpio_status() */ | |
a972b8d7 | 161 | #ifdef gpio_status |
a972b8d7 MF |
162 | gpio_status(); |
163 | return 0; | |
95a260a9 | 164 | #elif defined(CONFIG_DM_GPIO) |
89e64054 | 165 | return cmd_process_error(cmdtp, do_gpio_status(all, str_gpio)); |
95a260a9 SG |
166 | #else |
167 | goto show_usage; | |
a972b8d7 | 168 | #endif |
95a260a9 | 169 | } |
a972b8d7 | 170 | |
95a260a9 SG |
171 | if (!str_gpio) |
172 | goto show_usage; | |
a972b8d7 MF |
173 | |
174 | /* parse the behavior */ | |
175 | switch (*str_cmd) { | |
176 | case 'i': sub_cmd = GPIO_INPUT; break; | |
177 | case 's': sub_cmd = GPIO_SET; break; | |
178 | case 'c': sub_cmd = GPIO_CLEAR; break; | |
179 | case 't': sub_cmd = GPIO_TOGGLE; break; | |
180 | default: goto show_usage; | |
181 | } | |
182 | ||
95a260a9 SG |
183 | #if defined(CONFIG_DM_GPIO) |
184 | /* | |
185 | * TODO([email protected]): For now we must fit into the existing GPIO | |
186 | * framework, so we look up the name here and convert it to a GPIO number. | |
187 | * Once all GPIO drivers are converted to driver model, we can change the | |
188 | * code here to use the GPIO uclass interface instead of the numbered | |
189 | * GPIO compatibility layer. | |
190 | */ | |
191 | ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio); | |
192 | if (ret) | |
193 | return cmd_process_error(cmdtp, ret); | |
194 | #else | |
a972b8d7 MF |
195 | /* turn the gpio name into a gpio number */ |
196 | gpio = name_to_gpio(str_gpio); | |
197 | if (gpio < 0) | |
198 | goto show_usage; | |
95a260a9 | 199 | #endif |
a972b8d7 | 200 | /* grab the pin before we tweak it */ |
9165e842 SG |
201 | ret = gpio_request(gpio, "cmd_gpio"); |
202 | if (ret && ret != -EBUSY) { | |
6801201e MF |
203 | printf("gpio: requesting pin %u failed\n", gpio); |
204 | return -1; | |
205 | } | |
a972b8d7 MF |
206 | |
207 | /* finally, let's do it: set direction and exec command */ | |
208 | if (sub_cmd == GPIO_INPUT) { | |
209 | gpio_direction_input(gpio); | |
210 | value = gpio_get_value(gpio); | |
211 | } else { | |
212 | switch (sub_cmd) { | |
213 | case GPIO_SET: value = 1; break; | |
214 | case GPIO_CLEAR: value = 0; break; | |
215 | case GPIO_TOGGLE: value = !gpio_get_value(gpio); break; | |
216 | default: goto show_usage; | |
217 | } | |
218 | gpio_direction_output(gpio, value); | |
219 | } | |
220 | printf("gpio: pin %s (gpio %i) value is %lu\n", | |
221 | str_gpio, gpio, value); | |
222 | ||
9165e842 SG |
223 | if (ret != -EBUSY) |
224 | gpio_free(gpio); | |
a972b8d7 MF |
225 | |
226 | return value; | |
227 | } | |
228 | ||
89e64054 SG |
229 | U_BOOT_CMD(gpio, 4, 0, do_gpio, |
230 | "query and control gpio pins", | |
231 | "<input|set|clear|toggle> <pin>\n" | |
232 | " - input/set/clear/toggle the specified pin\n" | |
233 | "gpio status [-a] [<bank> | <pin>] - show [all/claimed] GPIOs"); |