blob: 0933b900b8ea0dfc0fc149a8a0c504b19ac534cf [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +02002/*
3 * (C) Copyright 2016
Mario Sixd38826a2018-03-06 08:04:58 +01004 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +02005 *
6 * based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is
7 *
8 * Copyright 2010 eXMeritus, A Boeing Company
hui.songd5d6b542020-09-24 21:40:50 +08009 * Copyright 2020 NXP
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020010 */
11
12#include <common.h>
13#include <dm.h>
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020014#include <mapmem.h>
Mario Sixf9c7fde2018-01-15 11:07:49 +010015#include <asm/gpio.h>
hui.songd5d6b542020-09-24 21:40:50 +080016#include <asm/io.h>
17#include <dm/of_access.h>
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020018
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020019struct ccsr_gpio {
20 u32 gpdir;
21 u32 gpodr;
22 u32 gpdat;
23 u32 gpier;
24 u32 gpimr;
25 u32 gpicr;
hui.songd5d6b542020-09-24 21:40:50 +080026 u32 gpibe;
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020027};
28
Mario Six3c216832018-01-15 11:07:48 +010029struct mpc8xxx_gpio_data {
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020030 /* The bank's register base in memory */
31 struct ccsr_gpio __iomem *base;
32 /* The address of the registers; used to identify the bank */
33 ulong addr;
34 /* The GPIO count of the bank */
35 uint gpio_count;
36 /* The GPDAT register cannot be used to determine the value of output
37 * pins on MPC8572/MPC8536, so we shadow it and use the shadowed value
Mario Sixaadc5e62018-01-15 11:07:46 +010038 * for output pins
39 */
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020040 u32 dat_shadow;
Mario Sixf9c7fde2018-01-15 11:07:49 +010041 ulong type;
hui.songd5d6b542020-09-24 21:40:50 +080042 bool little_endian;
Mario Sixf9c7fde2018-01-15 11:07:49 +010043};
44
45enum {
46 MPC8XXX_GPIO_TYPE,
47 MPC5121_GPIO_TYPE,
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020048};
49
Mario Sixaadc5e62018-01-15 11:07:46 +010050inline u32 gpio_mask(uint gpio)
51{
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020052 return (1U << (31 - (gpio)));
53}
54
hui.songd5d6b542020-09-24 21:40:50 +080055static inline u32 mpc8xxx_gpio_get_val(struct udevice *dev, u32 mask)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020056{
hui.songd5d6b542020-09-24 21:40:50 +080057 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
58
59 if (data->little_endian)
60 return in_le32(&data->base->gpdat) & mask;
61 else
62 return in_be32(&data->base->gpdat) & mask;
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020063}
64
hui.songd5d6b542020-09-24 21:40:50 +080065static inline u32 mpc8xxx_gpio_get_dir(struct udevice *dev, u32 mask)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020066{
hui.songd5d6b542020-09-24 21:40:50 +080067 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
68
69 if (data->little_endian)
70 return in_le32(&data->base->gpdir) & mask;
71 else
72 return in_be32(&data->base->gpdir) & mask;
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +020073}
74
hui.songd5d6b542020-09-24 21:40:50 +080075static inline int mpc8xxx_gpio_open_drain_val(struct udevice *dev, u32 mask)
mario.six@gdsys.cc51781782016-05-25 15:15:22 +020076{
hui.songd5d6b542020-09-24 21:40:50 +080077 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
78
79 if (data->little_endian)
80 return in_le32(&data->base->gpodr) & mask;
81 else
82 return in_be32(&data->base->gpodr) & mask;
mario.six@gdsys.cc51781782016-05-25 15:15:22 +020083}
84
hui.songd5d6b542020-09-24 21:40:50 +080085static inline void mpc8xxx_gpio_open_drain_on(struct udevice *dev, u32
mario.six@gdsys.cc51781782016-05-25 15:15:22 +020086 gpios)
87{
hui.songd5d6b542020-09-24 21:40:50 +080088 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
mario.six@gdsys.cc51781782016-05-25 15:15:22 +020089 /* GPODR register 1 -> open drain on */
hui.songd5d6b542020-09-24 21:40:50 +080090 if (data->little_endian)
91 setbits_le32(&data->base->gpodr, gpios);
92 else
93 setbits_be32(&data->base->gpodr, gpios);
mario.six@gdsys.cc51781782016-05-25 15:15:22 +020094}
95
hui.songd5d6b542020-09-24 21:40:50 +080096static inline void mpc8xxx_gpio_open_drain_off(struct udevice *dev,
mario.six@gdsys.cc51781782016-05-25 15:15:22 +020097 u32 gpios)
98{
hui.songd5d6b542020-09-24 21:40:50 +080099 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
mario.six@gdsys.cc51781782016-05-25 15:15:22 +0200100 /* GPODR register 0 -> open drain off (actively driven) */
hui.songd5d6b542020-09-24 21:40:50 +0800101 if (data->little_endian)
102 clrbits_le32(&data->base->gpodr, gpios);
103 else
104 clrbits_be32(&data->base->gpodr, gpios);
mario.six@gdsys.cc51781782016-05-25 15:15:22 +0200105}
106
Mario Six3c216832018-01-15 11:07:48 +0100107static int mpc8xxx_gpio_direction_input(struct udevice *dev, uint gpio)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200108{
Mario Six3c216832018-01-15 11:07:48 +0100109 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
Rasmus Villemoes1d7ad9f2020-01-28 12:04:33 +0000110 u32 mask = gpio_mask(gpio);
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200111
Rasmus Villemoes1d7ad9f2020-01-28 12:04:33 +0000112 /* GPDIR register 0 -> input */
hui.songd5d6b542020-09-24 21:40:50 +0800113 if (data->little_endian)
114 clrbits_le32(&data->base->gpdir, mask);
115 else
116 clrbits_be32(&data->base->gpdir, mask);
Rasmus Villemoes1d7ad9f2020-01-28 12:04:33 +0000117
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200118 return 0;
119}
120
Mario Six3c216832018-01-15 11:07:48 +0100121static int mpc8xxx_gpio_set_value(struct udevice *dev, uint gpio, int value)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200122{
Mario Six3c216832018-01-15 11:07:48 +0100123 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
Rasmus Villemoesdd4cf532020-01-28 12:04:34 +0000124 struct ccsr_gpio *base = data->base;
125 u32 mask = gpio_mask(gpio);
126 u32 gpdir;
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200127
128 if (value) {
Rasmus Villemoesdd4cf532020-01-28 12:04:34 +0000129 data->dat_shadow |= mask;
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200130 } else {
Rasmus Villemoesdd4cf532020-01-28 12:04:34 +0000131 data->dat_shadow &= ~mask;
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200132 }
Rasmus Villemoesdd4cf532020-01-28 12:04:34 +0000133
hui.songd5d6b542020-09-24 21:40:50 +0800134 if (data->little_endian)
135 gpdir = in_le32(&base->gpdir);
136 else
137 gpdir = in_be32(&base->gpdir);
138
Rasmus Villemoesdd4cf532020-01-28 12:04:34 +0000139 gpdir |= gpio_mask(gpio);
hui.songd5d6b542020-09-24 21:40:50 +0800140
141 if (data->little_endian) {
142 out_le32(&base->gpdat, gpdir & data->dat_shadow);
143 out_le32(&base->gpdir, gpdir);
144 } else {
145 out_be32(&base->gpdat, gpdir & data->dat_shadow);
146 out_be32(&base->gpdir, gpdir);
147 }
Rasmus Villemoesdd4cf532020-01-28 12:04:34 +0000148
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200149 return 0;
150}
151
Mario Six3c216832018-01-15 11:07:48 +0100152static int mpc8xxx_gpio_direction_output(struct udevice *dev, uint gpio,
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200153 int value)
154{
Mario Sixf9c7fde2018-01-15 11:07:49 +0100155 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
156
157 /* GPIO 28..31 are input only on MPC5121 */
158 if (data->type == MPC5121_GPIO_TYPE && gpio >= 28)
159 return -EINVAL;
160
Mario Six3c216832018-01-15 11:07:48 +0100161 return mpc8xxx_gpio_set_value(dev, gpio, value);
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200162}
163
Mario Six3c216832018-01-15 11:07:48 +0100164static int mpc8xxx_gpio_get_value(struct udevice *dev, uint gpio)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200165{
Mario Six3c216832018-01-15 11:07:48 +0100166 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200167
hui.songd5d6b542020-09-24 21:40:50 +0800168 if (!!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio))) {
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200169 /* Output -> use shadowed value */
170 return !!(data->dat_shadow & gpio_mask(gpio));
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200171 }
Mario Sixaadc5e62018-01-15 11:07:46 +0100172
173 /* Input -> read value from GPDAT register */
hui.songd5d6b542020-09-24 21:40:50 +0800174 return !!mpc8xxx_gpio_get_val(dev, gpio_mask(gpio));
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200175}
176
Mario Six3c216832018-01-15 11:07:48 +0100177static int mpc8xxx_gpio_get_function(struct udevice *dev, uint gpio)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200178{
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200179 int dir;
180
hui.songd5d6b542020-09-24 21:40:50 +0800181 dir = !!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio));
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200182 return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
183}
184
Hamish Martin4b689f02016-06-14 10:17:05 +1200185#if CONFIG_IS_ENABLED(OF_CONTROL)
Mario Six3c216832018-01-15 11:07:48 +0100186static int mpc8xxx_gpio_ofdata_to_platdata(struct udevice *dev)
Mario Sixaadc5e62018-01-15 11:07:46 +0100187{
Simon Glassc69cda22020-12-03 16:55:20 -0700188 struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
hui.songd5d6b542020-09-24 21:40:50 +0800189 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200190 fdt_addr_t addr;
hui.songd5d6b542020-09-24 21:40:50 +0800191 u32 i;
192 u32 reg[4];
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200193
hui.songd5d6b542020-09-24 21:40:50 +0800194 if (ofnode_read_bool(dev->node, "little-endian"))
195 data->little_endian = true;
196
197 if (data->little_endian)
198 dev_read_u32_array(dev, "reg", reg, 4);
199 else
200 dev_read_u32_array(dev, "reg", reg, 2);
201
202 if (data->little_endian) {
203 for (i = 0; i < 2; i++)
204 reg[i] = be32_to_cpu(reg[i]);
205 }
206
Mario Sixf5ac4f22018-01-15 11:07:50 +0100207 addr = dev_translate_address(dev, reg);
208
Hamish Martin4b689f02016-06-14 10:17:05 +1200209 plat->addr = addr;
hui.songd5d6b542020-09-24 21:40:50 +0800210
211 if (data->little_endian)
212 plat->size = reg[3];
213 else
214 plat->size = reg[1];
215
Mario Sixf5ac4f22018-01-15 11:07:50 +0100216 plat->ngpios = dev_read_u32_default(dev, "ngpios", 32);
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200217
Hamish Martin4b689f02016-06-14 10:17:05 +1200218 return 0;
219}
220#endif
221
Mario Six3c216832018-01-15 11:07:48 +0100222static int mpc8xxx_gpio_platdata_to_priv(struct udevice *dev)
Hamish Martin4b689f02016-06-14 10:17:05 +1200223{
Mario Six3c216832018-01-15 11:07:48 +0100224 struct mpc8xxx_gpio_data *priv = dev_get_priv(dev);
Simon Glassc69cda22020-12-03 16:55:20 -0700225 struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
Hamish Martin4b689f02016-06-14 10:17:05 +1200226 unsigned long size = plat->size;
Mario Sixf9c7fde2018-01-15 11:07:49 +0100227 ulong driver_data = dev_get_driver_data(dev);
Hamish Martin4b689f02016-06-14 10:17:05 +1200228
229 if (size == 0)
230 size = 0x100;
231
232 priv->addr = plat->addr;
Mario Sixf5ac4f22018-01-15 11:07:50 +0100233 priv->base = map_sysmem(plat->addr, size);
Hamish Martin4b689f02016-06-14 10:17:05 +1200234
235 if (!priv->base)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200236 return -ENOMEM;
237
Hamish Martin4b689f02016-06-14 10:17:05 +1200238 priv->gpio_count = plat->ngpios;
239 priv->dat_shadow = 0;
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200240
Mario Six3c216832018-01-15 11:07:48 +0100241 priv->type = driver_data;
242
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200243 return 0;
244}
245
Mario Six3c216832018-01-15 11:07:48 +0100246static int mpc8xxx_gpio_probe(struct udevice *dev)
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200247{
248 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
Mario Six3c216832018-01-15 11:07:48 +0100249 struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200250 char name[32], *str;
251
Mario Six3c216832018-01-15 11:07:48 +0100252 mpc8xxx_gpio_platdata_to_priv(dev);
Hamish Martin4b689f02016-06-14 10:17:05 +1200253
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200254 snprintf(name, sizeof(name), "MPC@%lx_", data->addr);
255 str = strdup(name);
256
257 if (!str)
258 return -ENOMEM;
259
hui.songd5d6b542020-09-24 21:40:50 +0800260 if (ofnode_device_is_compatible(dev->node, "fsl,qoriq-gpio")) {
261 unsigned long gpibe = data->addr + sizeof(struct ccsr_gpio)
262 - sizeof(u32);
263
264 out_be32((unsigned int *)gpibe, 0xffffffff);
265 }
266
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200267 uc_priv->bank_name = str;
268 uc_priv->gpio_count = data->gpio_count;
269
270 return 0;
271}
272
Mario Six3c216832018-01-15 11:07:48 +0100273static const struct dm_gpio_ops gpio_mpc8xxx_ops = {
274 .direction_input = mpc8xxx_gpio_direction_input,
275 .direction_output = mpc8xxx_gpio_direction_output,
276 .get_value = mpc8xxx_gpio_get_value,
277 .set_value = mpc8xxx_gpio_set_value,
Mario Six3c216832018-01-15 11:07:48 +0100278 .get_function = mpc8xxx_gpio_get_function,
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200279};
280
Mario Six3c216832018-01-15 11:07:48 +0100281static const struct udevice_id mpc8xxx_gpio_ids[] = {
Mario Sixf9c7fde2018-01-15 11:07:49 +0100282 { .compatible = "fsl,pq3-gpio", .data = MPC8XXX_GPIO_TYPE },
283 { .compatible = "fsl,mpc8308-gpio", .data = MPC8XXX_GPIO_TYPE },
284 { .compatible = "fsl,mpc8349-gpio", .data = MPC8XXX_GPIO_TYPE },
285 { .compatible = "fsl,mpc8572-gpio", .data = MPC8XXX_GPIO_TYPE},
286 { .compatible = "fsl,mpc8610-gpio", .data = MPC8XXX_GPIO_TYPE},
287 { .compatible = "fsl,mpc5121-gpio", .data = MPC5121_GPIO_TYPE, },
288 { .compatible = "fsl,qoriq-gpio", .data = MPC8XXX_GPIO_TYPE },
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200289 { /* sentinel */ }
290};
291
Mario Six3c216832018-01-15 11:07:48 +0100292U_BOOT_DRIVER(gpio_mpc8xxx) = {
293 .name = "gpio_mpc8xxx",
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200294 .id = UCLASS_GPIO,
Mario Six3c216832018-01-15 11:07:48 +0100295 .ops = &gpio_mpc8xxx_ops,
Hamish Martin4b689f02016-06-14 10:17:05 +1200296#if CONFIG_IS_ENABLED(OF_CONTROL)
Mario Six3c216832018-01-15 11:07:48 +0100297 .ofdata_to_platdata = mpc8xxx_gpio_ofdata_to_platdata,
Simon Glasscaa4daa2020-12-03 16:55:18 -0700298 .plat_auto = sizeof(struct mpc8xxx_gpio_plat),
Mario Six3c216832018-01-15 11:07:48 +0100299 .of_match = mpc8xxx_gpio_ids,
Hamish Martin4b689f02016-06-14 10:17:05 +1200300#endif
Mario Six3c216832018-01-15 11:07:48 +0100301 .probe = mpc8xxx_gpio_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700302 .priv_auto = sizeof(struct mpc8xxx_gpio_data),
mario.six@gdsys.cc07d31f82016-05-25 15:15:20 +0200303};