blob: 8bd30c75b2ffc96a93fb6886d0876150c65a3e39 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +01002/*
3 * Copyright (C) 2009
4 * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
5 *
Stefano Babic7faee912011-08-21 10:45:44 +02006 * Copyright (C) 2011
7 * Stefano Babic, DENX Software Engineering, <sbabic@denx.de>
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +01008 */
9#include <common.h>
Simon Glass2772b4d2014-10-01 19:57:26 -060010#include <errno.h>
11#include <dm.h>
12#include <malloc.h>
Stefano Babicd77fe992010-07-06 17:05:06 +020013#include <asm/arch/imx-regs.h>
Stefano Babic7faee912011-08-21 10:45:44 +020014#include <asm/gpio.h>
Stefano Babicd77fe992010-07-06 17:05:06 +020015#include <asm/io.h>
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010016
Stefano Babic7faee912011-08-21 10:45:44 +020017enum mxc_gpio_direction {
18 MXC_GPIO_DIRECTION_IN,
19 MXC_GPIO_DIRECTION_OUT,
20};
21
Simon Glass2772b4d2014-10-01 19:57:26 -060022#define GPIO_PER_BANK 32
23
24struct mxc_gpio_plat {
Peng Fan86be4262015-02-10 14:46:33 +080025 int bank_index;
Simon Glass2772b4d2014-10-01 19:57:26 -060026 struct gpio_regs *regs;
27};
28
29struct mxc_bank_info {
Simon Glass2772b4d2014-10-01 19:57:26 -060030 struct gpio_regs *regs;
31};
32
33#ifndef CONFIG_DM_GPIO
Vikram Narayanan10999cf2012-04-10 04:26:08 +000034#define GPIO_TO_PORT(n) (n / 32)
Stefano Babic7faee912011-08-21 10:45:44 +020035
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010036/* GPIO port description */
37static unsigned long gpio_ports[] = {
Stefano Babicd77fe992010-07-06 17:05:06 +020038 [0] = GPIO1_BASE_ADDR,
39 [1] = GPIO2_BASE_ADDR,
40 [2] = GPIO3_BASE_ADDR,
tremcf233ed2012-08-25 05:30:33 +000041#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
Adrian Alonso840d2e32015-08-11 11:19:51 -050042 defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +000043 defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || \
Peng Fanb2242e12018-10-18 14:28:27 +020044 defined(CONFIG_ARCH_IMX8)
Stefano Babicd77fe992010-07-06 17:05:06 +020045 [3] = GPIO4_BASE_ADDR,
46#endif
Adrian Alonso840d2e32015-08-11 11:19:51 -050047#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +000048 defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || \
Peng Fanb2242e12018-10-18 14:28:27 +020049 defined(CONFIG_ARCH_IMX8)
Liu Hui-R64343a71164c2011-01-03 22:27:38 +000050 [4] = GPIO5_BASE_ADDR,
Peng Fan39945c12018-11-20 10:19:25 +000051#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) || defined(CONFIG_IMX8M))
Liu Hui-R64343a71164c2011-01-03 22:27:38 +000052 [5] = GPIO6_BASE_ADDR,
tremcf233ed2012-08-25 05:30:33 +000053#endif
Peng Fanf5dd87e2015-07-20 19:28:31 +080054#endif
Peng Fanb2242e12018-10-18 14:28:27 +020055#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_MX7) || \
56 defined(CONFIG_ARCH_IMX8)
Fabio Estevam1b691df2018-01-03 12:33:05 -020057#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL))
Liu Hui-R64343a71164c2011-01-03 22:27:38 +000058 [6] = GPIO7_BASE_ADDR,
59#endif
Peng Fanf5dd87e2015-07-20 19:28:31 +080060#endif
Peng Fanb2242e12018-10-18 14:28:27 +020061#if defined(CONFIG_ARCH_IMX8)
62 [7] = GPIO8_BASE_ADDR,
63#endif
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010064};
65
Stefano Babic7faee912011-08-21 10:45:44 +020066static int mxc_gpio_direction(unsigned int gpio,
67 enum mxc_gpio_direction direction)
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010068{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +000069 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +020070 struct gpio_regs *regs;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010071 u32 l;
72
73 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -060074 return -1;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010075
76 gpio &= 0x1f;
77
Stefano Babicd77fe992010-07-06 17:05:06 +020078 regs = (struct gpio_regs *)gpio_ports[port];
79
80 l = readl(&regs->gpio_dir);
81
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010082 switch (direction) {
Stefano Babicd77fe992010-07-06 17:05:06 +020083 case MXC_GPIO_DIRECTION_OUT:
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010084 l |= 1 << gpio;
85 break;
Stefano Babicd77fe992010-07-06 17:05:06 +020086 case MXC_GPIO_DIRECTION_IN:
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010087 l &= ~(1 << gpio);
88 }
Stefano Babicd77fe992010-07-06 17:05:06 +020089 writel(l, &regs->gpio_dir);
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010090
91 return 0;
92}
93
Joe Hershbergerf8928f12011-11-11 15:55:36 -060094int gpio_set_value(unsigned gpio, int value)
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010095{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +000096 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +020097 struct gpio_regs *regs;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010098 u32 l;
99
100 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600101 return -1;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +0100102
103 gpio &= 0x1f;
104
Stefano Babicd77fe992010-07-06 17:05:06 +0200105 regs = (struct gpio_regs *)gpio_ports[port];
106
107 l = readl(&regs->gpio_dr);
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +0100108 if (value)
109 l |= 1 << gpio;
110 else
111 l &= ~(1 << gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +0200112 writel(l, &regs->gpio_dr);
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600113
114 return 0;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +0100115}
Stefano Babica44d2a52010-04-13 12:07:00 +0200116
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600117int gpio_get_value(unsigned gpio)
Stefano Babica44d2a52010-04-13 12:07:00 +0200118{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +0000119 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +0200120 struct gpio_regs *regs;
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600121 u32 val;
Stefano Babica44d2a52010-04-13 12:07:00 +0200122
123 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600124 return -1;
Stefano Babica44d2a52010-04-13 12:07:00 +0200125
126 gpio &= 0x1f;
127
Stefano Babicd77fe992010-07-06 17:05:06 +0200128 regs = (struct gpio_regs *)gpio_ports[port];
129
Benoît Thébaudeaudaaf7a92012-08-20 10:55:41 +0000130 val = (readl(&regs->gpio_psr) >> gpio) & 0x01;
Stefano Babica44d2a52010-04-13 12:07:00 +0200131
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600132 return val;
Stefano Babica44d2a52010-04-13 12:07:00 +0200133}
Stefano Babic7faee912011-08-21 10:45:44 +0200134
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600135int gpio_request(unsigned gpio, const char *label)
Stefano Babic7faee912011-08-21 10:45:44 +0200136{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +0000137 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babic7faee912011-08-21 10:45:44 +0200138 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600139 return -1;
Stefano Babic7faee912011-08-21 10:45:44 +0200140 return 0;
141}
142
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600143int gpio_free(unsigned gpio)
Stefano Babic7faee912011-08-21 10:45:44 +0200144{
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600145 return 0;
Stefano Babic7faee912011-08-21 10:45:44 +0200146}
147
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600148int gpio_direction_input(unsigned gpio)
Stefano Babic7faee912011-08-21 10:45:44 +0200149{
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600150 return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN);
Stefano Babic7faee912011-08-21 10:45:44 +0200151}
152
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600153int gpio_direction_output(unsigned gpio, int value)
Stefano Babic7faee912011-08-21 10:45:44 +0200154{
Dirk Behme1e0803f2013-07-15 15:58:27 +0200155 int ret = gpio_set_value(gpio, value);
Stefano Babic7faee912011-08-21 10:45:44 +0200156
157 if (ret < 0)
158 return ret;
159
Dirk Behme1e0803f2013-07-15 15:58:27 +0200160 return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT);
Stefano Babic7faee912011-08-21 10:45:44 +0200161}
Simon Glass2772b4d2014-10-01 19:57:26 -0600162#endif
163
164#ifdef CONFIG_DM_GPIO
Peng Fan0ed2cb12015-02-10 14:46:34 +0800165#include <fdtdec.h>
Simon Glass2772b4d2014-10-01 19:57:26 -0600166static int mxc_gpio_is_output(struct gpio_regs *regs, int offset)
167{
168 u32 val;
169
170 val = readl(&regs->gpio_dir);
171
172 return val & (1 << offset) ? 1 : 0;
173}
174
175static void mxc_gpio_bank_direction(struct gpio_regs *regs, int offset,
176 enum mxc_gpio_direction direction)
177{
178 u32 l;
179
180 l = readl(&regs->gpio_dir);
181
182 switch (direction) {
183 case MXC_GPIO_DIRECTION_OUT:
184 l |= 1 << offset;
185 break;
186 case MXC_GPIO_DIRECTION_IN:
187 l &= ~(1 << offset);
188 }
189 writel(l, &regs->gpio_dir);
190}
191
192static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset,
193 int value)
194{
195 u32 l;
196
197 l = readl(&regs->gpio_dr);
198 if (value)
199 l |= 1 << offset;
200 else
201 l &= ~(1 << offset);
202 writel(l, &regs->gpio_dr);
203}
204
205static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset)
206{
207 return (readl(&regs->gpio_psr) >> offset) & 0x01;
208}
209
Simon Glass2772b4d2014-10-01 19:57:26 -0600210/* set GPIO pin 'gpio' as an input */
211static int mxc_gpio_direction_input(struct udevice *dev, unsigned offset)
212{
213 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600214
215 /* Configure GPIO direction as input. */
216 mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_IN);
217
218 return 0;
219}
220
221/* set GPIO pin 'gpio' as an output, with polarity 'value' */
222static int mxc_gpio_direction_output(struct udevice *dev, unsigned offset,
223 int value)
224{
225 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600226
227 /* Configure GPIO output value. */
228 mxc_gpio_bank_set_value(bank->regs, offset, value);
229
230 /* Configure GPIO direction as output. */
231 mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_OUT);
232
233 return 0;
234}
235
236/* read GPIO IN value of pin 'gpio' */
237static int mxc_gpio_get_value(struct udevice *dev, unsigned offset)
238{
239 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600240
241 return mxc_gpio_bank_get_value(bank->regs, offset);
242}
243
244/* write GPIO OUT value to pin 'gpio' */
245static int mxc_gpio_set_value(struct udevice *dev, unsigned offset,
246 int value)
247{
248 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600249
250 mxc_gpio_bank_set_value(bank->regs, offset, value);
251
252 return 0;
253}
254
Simon Glass2772b4d2014-10-01 19:57:26 -0600255static int mxc_gpio_get_function(struct udevice *dev, unsigned offset)
256{
257 struct mxc_bank_info *bank = dev_get_priv(dev);
258
Simon Glass2772b4d2014-10-01 19:57:26 -0600259 /* GPIOF_FUNC is not implemented yet */
260 if (mxc_gpio_is_output(bank->regs, offset))
261 return GPIOF_OUTPUT;
262 else
263 return GPIOF_INPUT;
264}
265
266static const struct dm_gpio_ops gpio_mxc_ops = {
Simon Glass2772b4d2014-10-01 19:57:26 -0600267 .direction_input = mxc_gpio_direction_input,
268 .direction_output = mxc_gpio_direction_output,
269 .get_value = mxc_gpio_get_value,
270 .set_value = mxc_gpio_set_value,
271 .get_function = mxc_gpio_get_function,
Simon Glass2772b4d2014-10-01 19:57:26 -0600272};
273
Simon Glass2772b4d2014-10-01 19:57:26 -0600274static int mxc_gpio_probe(struct udevice *dev)
275{
276 struct mxc_bank_info *bank = dev_get_priv(dev);
277 struct mxc_gpio_plat *plat = dev_get_platdata(dev);
Simon Glassde0977b2015-03-05 12:25:20 -0700278 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600279 int banknum;
280 char name[18], *str;
281
Peng Fan86be4262015-02-10 14:46:33 +0800282 banknum = plat->bank_index;
Simon Glass2772b4d2014-10-01 19:57:26 -0600283 sprintf(name, "GPIO%d_", banknum + 1);
284 str = strdup(name);
285 if (!str)
286 return -ENOMEM;
287 uc_priv->bank_name = str;
288 uc_priv->gpio_count = GPIO_PER_BANK;
289 bank->regs = plat->regs;
290
291 return 0;
292}
293
Peng Fan0ed2cb12015-02-10 14:46:34 +0800294static int mxc_gpio_bind(struct udevice *dev)
295{
296 struct mxc_gpio_plat *plat = dev->platdata;
297 fdt_addr_t addr;
298
299 /*
300 * If platdata already exsits, directly return.
301 * Actually only when DT is not supported, platdata
302 * is statically initialized in U_BOOT_DEVICES.Here
303 * will return.
304 */
305 if (plat)
306 return 0;
307
Simon Glassba1dea42017-05-17 17:18:05 -0600308 addr = devfdt_get_addr(dev);
Peng Fan0ed2cb12015-02-10 14:46:34 +0800309 if (addr == FDT_ADDR_T_NONE)
Simon Glassf44b4bf2017-09-17 16:54:53 -0600310 return -EINVAL;
Peng Fan0ed2cb12015-02-10 14:46:34 +0800311
312 /*
313 * TODO:
314 * When every board is converted to driver model and DT is supported,
315 * this can be done by auto-alloc feature, but not using calloc
316 * to alloc memory for platdata.
Simon Glass87f4cb72017-09-17 16:54:52 -0600317 *
318 * For example mxc_plat below uses platform data rather than device
319 * tree.
320 *
321 * NOTE: DO NOT COPY this code if you are using device tree.
Peng Fan0ed2cb12015-02-10 14:46:34 +0800322 */
323 plat = calloc(1, sizeof(*plat));
324 if (!plat)
325 return -ENOMEM;
326
327 plat->regs = (struct gpio_regs *)addr;
328 plat->bank_index = dev->req_seq;
329 dev->platdata = plat;
330
331 return 0;
332}
333
334static const struct udevice_id mxc_gpio_ids[] = {
335 { .compatible = "fsl,imx35-gpio" },
336 { }
337};
338
Simon Glass2772b4d2014-10-01 19:57:26 -0600339U_BOOT_DRIVER(gpio_mxc) = {
340 .name = "gpio_mxc",
341 .id = UCLASS_GPIO,
342 .ops = &gpio_mxc_ops,
343 .probe = mxc_gpio_probe,
344 .priv_auto_alloc_size = sizeof(struct mxc_bank_info),
Peng Fan0ed2cb12015-02-10 14:46:34 +0800345 .of_match = mxc_gpio_ids,
346 .bind = mxc_gpio_bind,
347};
348
Masahiro Yamada366b24f2015-08-12 07:31:55 +0900349#if !CONFIG_IS_ENABLED(OF_CONTROL)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800350static const struct mxc_gpio_plat mxc_plat[] = {
351 { 0, (struct gpio_regs *)GPIO1_BASE_ADDR },
352 { 1, (struct gpio_regs *)GPIO2_BASE_ADDR },
353 { 2, (struct gpio_regs *)GPIO3_BASE_ADDR },
354#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
Peng Fanfa94fb42018-01-10 13:20:42 +0800355 defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000356 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800357 { 3, (struct gpio_regs *)GPIO4_BASE_ADDR },
358#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800359#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000360 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800361 { 4, (struct gpio_regs *)GPIO5_BASE_ADDR },
Peng Fan39945c12018-11-20 10:19:25 +0000362#ifndef CONFIG_IMX8M
Peng Fan0ed2cb12015-02-10 14:46:34 +0800363 { 5, (struct gpio_regs *)GPIO6_BASE_ADDR },
364#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800365#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200366#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800367 { 6, (struct gpio_regs *)GPIO7_BASE_ADDR },
368#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200369#if defined(CONFIG_ARCH_IMX8)
370 { 7, (struct gpio_regs *)GPIO8_BASE_ADDR },
371#endif
Simon Glass2772b4d2014-10-01 19:57:26 -0600372};
373
374U_BOOT_DEVICES(mxc_gpios) = {
375 { "gpio_mxc", &mxc_plat[0] },
376 { "gpio_mxc", &mxc_plat[1] },
377 { "gpio_mxc", &mxc_plat[2] },
378#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
Peng Fanfa94fb42018-01-10 13:20:42 +0800379 defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000380 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Simon Glass2772b4d2014-10-01 19:57:26 -0600381 { "gpio_mxc", &mxc_plat[3] },
382#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800383#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000384 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Simon Glass2772b4d2014-10-01 19:57:26 -0600385 { "gpio_mxc", &mxc_plat[4] },
Peng Fan39945c12018-11-20 10:19:25 +0000386#ifndef CONFIG_IMX8M
Simon Glass2772b4d2014-10-01 19:57:26 -0600387 { "gpio_mxc", &mxc_plat[5] },
388#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800389#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200390#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
Simon Glass2772b4d2014-10-01 19:57:26 -0600391 { "gpio_mxc", &mxc_plat[6] },
392#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200393#if defined(CONFIG_ARCH_IMX8)
394 { "gpio_mxc", &mxc_plat[7] },
395#endif
Simon Glass2772b4d2014-10-01 19:57:26 -0600396};
397#endif
Peng Fan0ed2cb12015-02-10 14:46:34 +0800398#endif