blob: c924e52f0713109f069875a4033d2c9a5daff242 [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
Simon Glassfa4689a2019-12-06 21:41:35 -070033#if !CONFIG_IS_ENABLED(DM_GPIO)
Lukasz Majewskic00b0932019-06-09 22:54:40 +020034#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) || \
Giulio Benettia82cd872020-01-10 15:47:03 +010044 defined(CONFIG_ARCH_IMX8) || defined(CONFIG_IMXRT1050)
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) || \
Giulio Benettia82cd872020-01-10 15:47:03 +010049 defined(CONFIG_ARCH_IMX8) || defined(CONFIG_IMXRT1050)
Liu Hui-R64343a71164c2011-01-03 22:27:38 +000050 [4] = GPIO5_BASE_ADDR,
Giulio Benettia82cd872020-01-10 15:47:03 +010051#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) || \
52 defined(CONFIG_IMX8M) || defined(CONFIG_IMXRT1050))
Liu Hui-R64343a71164c2011-01-03 22:27:38 +000053 [5] = GPIO6_BASE_ADDR,
tremcf233ed2012-08-25 05:30:33 +000054#endif
Peng Fanf5dd87e2015-07-20 19:28:31 +080055#endif
Peng Fanb2242e12018-10-18 14:28:27 +020056#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_MX7) || \
57 defined(CONFIG_ARCH_IMX8)
Fabio Estevam1b691df2018-01-03 12:33:05 -020058#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL))
Liu Hui-R64343a71164c2011-01-03 22:27:38 +000059 [6] = GPIO7_BASE_ADDR,
60#endif
Peng Fanf5dd87e2015-07-20 19:28:31 +080061#endif
Peng Fanb2242e12018-10-18 14:28:27 +020062#if defined(CONFIG_ARCH_IMX8)
63 [7] = GPIO8_BASE_ADDR,
64#endif
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010065};
66
Stefano Babic7faee912011-08-21 10:45:44 +020067static int mxc_gpio_direction(unsigned int gpio,
68 enum mxc_gpio_direction direction)
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010069{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +000070 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +020071 struct gpio_regs *regs;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010072 u32 l;
73
74 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -060075 return -1;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010076
77 gpio &= 0x1f;
78
Stefano Babicd77fe992010-07-06 17:05:06 +020079 regs = (struct gpio_regs *)gpio_ports[port];
80
81 l = readl(&regs->gpio_dir);
82
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010083 switch (direction) {
Stefano Babicd77fe992010-07-06 17:05:06 +020084 case MXC_GPIO_DIRECTION_OUT:
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010085 l |= 1 << gpio;
86 break;
Stefano Babicd77fe992010-07-06 17:05:06 +020087 case MXC_GPIO_DIRECTION_IN:
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010088 l &= ~(1 << gpio);
89 }
Stefano Babicd77fe992010-07-06 17:05:06 +020090 writel(l, &regs->gpio_dir);
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010091
92 return 0;
93}
94
Joe Hershbergerf8928f12011-11-11 15:55:36 -060095int gpio_set_value(unsigned gpio, int value)
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010096{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +000097 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +020098 struct gpio_regs *regs;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +010099 u32 l;
100
101 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600102 return -1;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +0100103
104 gpio &= 0x1f;
105
Stefano Babicd77fe992010-07-06 17:05:06 +0200106 regs = (struct gpio_regs *)gpio_ports[port];
107
108 l = readl(&regs->gpio_dr);
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +0100109 if (value)
110 l |= 1 << gpio;
111 else
112 l &= ~(1 << gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +0200113 writel(l, &regs->gpio_dr);
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600114
115 return 0;
Guennadi Liakhovetski7c6a8562009-02-07 01:18:07 +0100116}
Stefano Babica44d2a52010-04-13 12:07:00 +0200117
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600118int gpio_get_value(unsigned gpio)
Stefano Babica44d2a52010-04-13 12:07:00 +0200119{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +0000120 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babicd77fe992010-07-06 17:05:06 +0200121 struct gpio_regs *regs;
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600122 u32 val;
Stefano Babica44d2a52010-04-13 12:07:00 +0200123
124 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600125 return -1;
Stefano Babica44d2a52010-04-13 12:07:00 +0200126
127 gpio &= 0x1f;
128
Stefano Babicd77fe992010-07-06 17:05:06 +0200129 regs = (struct gpio_regs *)gpio_ports[port];
130
Benoît Thébaudeaudaaf7a92012-08-20 10:55:41 +0000131 val = (readl(&regs->gpio_psr) >> gpio) & 0x01;
Stefano Babica44d2a52010-04-13 12:07:00 +0200132
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600133 return val;
Stefano Babica44d2a52010-04-13 12:07:00 +0200134}
Stefano Babic7faee912011-08-21 10:45:44 +0200135
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600136int gpio_request(unsigned gpio, const char *label)
Stefano Babic7faee912011-08-21 10:45:44 +0200137{
Vikram Narayananfbdf6bc2012-04-10 04:26:20 +0000138 unsigned int port = GPIO_TO_PORT(gpio);
Stefano Babic7faee912011-08-21 10:45:44 +0200139 if (port >= ARRAY_SIZE(gpio_ports))
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600140 return -1;
Stefano Babic7faee912011-08-21 10:45:44 +0200141 return 0;
142}
143
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600144int gpio_free(unsigned gpio)
Stefano Babic7faee912011-08-21 10:45:44 +0200145{
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600146 return 0;
Stefano Babic7faee912011-08-21 10:45:44 +0200147}
148
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600149int gpio_direction_input(unsigned gpio)
Stefano Babic7faee912011-08-21 10:45:44 +0200150{
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600151 return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN);
Stefano Babic7faee912011-08-21 10:45:44 +0200152}
153
Joe Hershbergerf8928f12011-11-11 15:55:36 -0600154int gpio_direction_output(unsigned gpio, int value)
Stefano Babic7faee912011-08-21 10:45:44 +0200155{
Dirk Behme1e0803f2013-07-15 15:58:27 +0200156 int ret = gpio_set_value(gpio, value);
Stefano Babic7faee912011-08-21 10:45:44 +0200157
158 if (ret < 0)
159 return ret;
160
Dirk Behme1e0803f2013-07-15 15:58:27 +0200161 return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT);
Stefano Babic7faee912011-08-21 10:45:44 +0200162}
Simon Glass2772b4d2014-10-01 19:57:26 -0600163#endif
164
Simon Glassfa4689a2019-12-06 21:41:35 -0700165#if CONFIG_IS_ENABLED(DM_GPIO)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800166#include <fdtdec.h>
Simon Glass2772b4d2014-10-01 19:57:26 -0600167static int mxc_gpio_is_output(struct gpio_regs *regs, int offset)
168{
169 u32 val;
170
171 val = readl(&regs->gpio_dir);
172
173 return val & (1 << offset) ? 1 : 0;
174}
175
176static void mxc_gpio_bank_direction(struct gpio_regs *regs, int offset,
177 enum mxc_gpio_direction direction)
178{
179 u32 l;
180
181 l = readl(&regs->gpio_dir);
182
183 switch (direction) {
184 case MXC_GPIO_DIRECTION_OUT:
185 l |= 1 << offset;
186 break;
187 case MXC_GPIO_DIRECTION_IN:
188 l &= ~(1 << offset);
189 }
190 writel(l, &regs->gpio_dir);
191}
192
193static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset,
194 int value)
195{
196 u32 l;
197
198 l = readl(&regs->gpio_dr);
199 if (value)
200 l |= 1 << offset;
201 else
202 l &= ~(1 << offset);
203 writel(l, &regs->gpio_dr);
204}
205
206static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset)
207{
208 return (readl(&regs->gpio_psr) >> offset) & 0x01;
209}
210
Simon Glass2772b4d2014-10-01 19:57:26 -0600211/* set GPIO pin 'gpio' as an input */
212static int mxc_gpio_direction_input(struct udevice *dev, unsigned offset)
213{
214 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600215
216 /* Configure GPIO direction as input. */
217 mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_IN);
218
219 return 0;
220}
221
222/* set GPIO pin 'gpio' as an output, with polarity 'value' */
223static int mxc_gpio_direction_output(struct udevice *dev, unsigned offset,
224 int value)
225{
226 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600227
228 /* Configure GPIO output value. */
229 mxc_gpio_bank_set_value(bank->regs, offset, value);
230
231 /* Configure GPIO direction as output. */
232 mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_OUT);
233
234 return 0;
235}
236
237/* read GPIO IN value of pin 'gpio' */
238static int mxc_gpio_get_value(struct udevice *dev, unsigned offset)
239{
240 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600241
242 return mxc_gpio_bank_get_value(bank->regs, offset);
243}
244
245/* write GPIO OUT value to pin 'gpio' */
246static int mxc_gpio_set_value(struct udevice *dev, unsigned offset,
247 int value)
248{
249 struct mxc_bank_info *bank = dev_get_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600250
251 mxc_gpio_bank_set_value(bank->regs, offset, value);
252
253 return 0;
254}
255
Simon Glass2772b4d2014-10-01 19:57:26 -0600256static int mxc_gpio_get_function(struct udevice *dev, unsigned offset)
257{
258 struct mxc_bank_info *bank = dev_get_priv(dev);
259
Simon Glass2772b4d2014-10-01 19:57:26 -0600260 /* GPIOF_FUNC is not implemented yet */
261 if (mxc_gpio_is_output(bank->regs, offset))
262 return GPIOF_OUTPUT;
263 else
264 return GPIOF_INPUT;
265}
266
267static const struct dm_gpio_ops gpio_mxc_ops = {
Simon Glass2772b4d2014-10-01 19:57:26 -0600268 .direction_input = mxc_gpio_direction_input,
269 .direction_output = mxc_gpio_direction_output,
270 .get_value = mxc_gpio_get_value,
271 .set_value = mxc_gpio_set_value,
272 .get_function = mxc_gpio_get_function,
Simon Glass2772b4d2014-10-01 19:57:26 -0600273};
274
Simon Glass2772b4d2014-10-01 19:57:26 -0600275static int mxc_gpio_probe(struct udevice *dev)
276{
277 struct mxc_bank_info *bank = dev_get_priv(dev);
278 struct mxc_gpio_plat *plat = dev_get_platdata(dev);
Simon Glassde0977b2015-03-05 12:25:20 -0700279 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
Simon Glass2772b4d2014-10-01 19:57:26 -0600280 int banknum;
281 char name[18], *str;
282
Peng Fan86be4262015-02-10 14:46:33 +0800283 banknum = plat->bank_index;
Simon Glass2772b4d2014-10-01 19:57:26 -0600284 sprintf(name, "GPIO%d_", banknum + 1);
285 str = strdup(name);
286 if (!str)
287 return -ENOMEM;
288 uc_priv->bank_name = str;
289 uc_priv->gpio_count = GPIO_PER_BANK;
290 bank->regs = plat->regs;
291
292 return 0;
293}
294
Peng Fan0ed2cb12015-02-10 14:46:34 +0800295static int mxc_gpio_bind(struct udevice *dev)
296{
297 struct mxc_gpio_plat *plat = dev->platdata;
298 fdt_addr_t addr;
299
300 /*
301 * If platdata already exsits, directly return.
302 * Actually only when DT is not supported, platdata
303 * is statically initialized in U_BOOT_DEVICES.Here
304 * will return.
305 */
306 if (plat)
307 return 0;
308
Simon Glassba1dea42017-05-17 17:18:05 -0600309 addr = devfdt_get_addr(dev);
Peng Fan0ed2cb12015-02-10 14:46:34 +0800310 if (addr == FDT_ADDR_T_NONE)
Simon Glassf44b4bf2017-09-17 16:54:53 -0600311 return -EINVAL;
Peng Fan0ed2cb12015-02-10 14:46:34 +0800312
313 /*
314 * TODO:
315 * When every board is converted to driver model and DT is supported,
316 * this can be done by auto-alloc feature, but not using calloc
317 * to alloc memory for platdata.
Simon Glass87f4cb72017-09-17 16:54:52 -0600318 *
319 * For example mxc_plat below uses platform data rather than device
320 * tree.
321 *
322 * NOTE: DO NOT COPY this code if you are using device tree.
Peng Fan0ed2cb12015-02-10 14:46:34 +0800323 */
324 plat = calloc(1, sizeof(*plat));
325 if (!plat)
326 return -ENOMEM;
327
328 plat->regs = (struct gpio_regs *)addr;
329 plat->bank_index = dev->req_seq;
330 dev->platdata = plat;
331
332 return 0;
333}
334
335static const struct udevice_id mxc_gpio_ids[] = {
336 { .compatible = "fsl,imx35-gpio" },
337 { }
338};
339
Simon Glass2772b4d2014-10-01 19:57:26 -0600340U_BOOT_DRIVER(gpio_mxc) = {
341 .name = "gpio_mxc",
342 .id = UCLASS_GPIO,
343 .ops = &gpio_mxc_ops,
344 .probe = mxc_gpio_probe,
345 .priv_auto_alloc_size = sizeof(struct mxc_bank_info),
Peng Fan0ed2cb12015-02-10 14:46:34 +0800346 .of_match = mxc_gpio_ids,
347 .bind = mxc_gpio_bind,
348};
349
Masahiro Yamada366b24f2015-08-12 07:31:55 +0900350#if !CONFIG_IS_ENABLED(OF_CONTROL)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800351static const struct mxc_gpio_plat mxc_plat[] = {
352 { 0, (struct gpio_regs *)GPIO1_BASE_ADDR },
353 { 1, (struct gpio_regs *)GPIO2_BASE_ADDR },
354 { 2, (struct gpio_regs *)GPIO3_BASE_ADDR },
355#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
Peng Fanfa94fb42018-01-10 13:20:42 +0800356 defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000357 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800358 { 3, (struct gpio_regs *)GPIO4_BASE_ADDR },
359#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800360#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000361 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800362 { 4, (struct gpio_regs *)GPIO5_BASE_ADDR },
Peng Fan39945c12018-11-20 10:19:25 +0000363#ifndef CONFIG_IMX8M
Peng Fan0ed2cb12015-02-10 14:46:34 +0800364 { 5, (struct gpio_regs *)GPIO6_BASE_ADDR },
365#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800366#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200367#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
Peng Fan0ed2cb12015-02-10 14:46:34 +0800368 { 6, (struct gpio_regs *)GPIO7_BASE_ADDR },
369#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200370#if defined(CONFIG_ARCH_IMX8)
371 { 7, (struct gpio_regs *)GPIO8_BASE_ADDR },
372#endif
Simon Glass2772b4d2014-10-01 19:57:26 -0600373};
374
375U_BOOT_DEVICES(mxc_gpios) = {
376 { "gpio_mxc", &mxc_plat[0] },
377 { "gpio_mxc", &mxc_plat[1] },
378 { "gpio_mxc", &mxc_plat[2] },
379#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
Peng Fanfa94fb42018-01-10 13:20:42 +0800380 defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000381 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Simon Glass2772b4d2014-10-01 19:57:26 -0600382 { "gpio_mxc", &mxc_plat[3] },
383#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800384#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
Peng Fan39945c12018-11-20 10:19:25 +0000385 defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
Simon Glass2772b4d2014-10-01 19:57:26 -0600386 { "gpio_mxc", &mxc_plat[4] },
Peng Fan39945c12018-11-20 10:19:25 +0000387#ifndef CONFIG_IMX8M
Simon Glass2772b4d2014-10-01 19:57:26 -0600388 { "gpio_mxc", &mxc_plat[5] },
389#endif
Peng Fanfa94fb42018-01-10 13:20:42 +0800390#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200391#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
Simon Glass2772b4d2014-10-01 19:57:26 -0600392 { "gpio_mxc", &mxc_plat[6] },
393#endif
Peng Fanb2242e12018-10-18 14:28:27 +0200394#if defined(CONFIG_ARCH_IMX8)
395 { "gpio_mxc", &mxc_plat[7] },
396#endif
Simon Glass2772b4d2014-10-01 19:57:26 -0600397};
398#endif
Peng Fan0ed2cb12015-02-10 14:46:34 +0800399#endif