]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
07d31f8f | 2 | /* |
3 | * (C) Copyright 2016 | |
d38826a3 | 4 | * Mario Six, Guntermann & Drunck GmbH, [email protected] |
07d31f8f | 5 | * |
6 | * based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is | |
7 | * | |
8 | * Copyright 2010 eXMeritus, A Boeing Company | |
78118809 | 9 | * Copyright 2020-2021 NXP |
07d31f8f | 10 | */ |
11 | ||
07d31f8f | 12 | #include <dm.h> |
07d31f8f | 13 | #include <mapmem.h> |
f9c7fde2 | 14 | #include <asm/gpio.h> |
d5d6b548 | 15 | #include <asm/io.h> |
16 | #include <dm/of_access.h> | |
07d31f8f | 17 | |
3c216834 | 18 | struct mpc8xxx_gpio_data { |
07d31f8f | 19 | /* The bank's register base in memory */ |
20 | struct ccsr_gpio __iomem *base; | |
21 | /* The address of the registers; used to identify the bank */ | |
271a87b4 | 22 | phys_addr_t addr; |
07d31f8f | 23 | /* The GPIO count of the bank */ |
24 | uint gpio_count; | |
25 | /* The GPDAT register cannot be used to determine the value of output | |
26 | * pins on MPC8572/MPC8536, so we shadow it and use the shadowed value | |
aadc5e67 MS |
27 | * for output pins |
28 | */ | |
07d31f8f | 29 | u32 dat_shadow; |
f9c7fde2 | 30 | ulong type; |
d5d6b548 | 31 | bool little_endian; |
f9c7fde2 MS |
32 | }; |
33 | ||
34 | enum { | |
35 | MPC8XXX_GPIO_TYPE, | |
36 | MPC5121_GPIO_TYPE, | |
07d31f8f | 37 | }; |
38 | ||
aadc5e67 MS |
39 | inline u32 gpio_mask(uint gpio) |
40 | { | |
07d31f8f | 41 | return (1U << (31 - (gpio))); |
42 | } | |
43 | ||
d5d6b548 | 44 | static inline u32 mpc8xxx_gpio_get_val(struct udevice *dev, u32 mask) |
07d31f8f | 45 | { |
d5d6b548 | 46 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
47 | ||
48 | if (data->little_endian) | |
49 | return in_le32(&data->base->gpdat) & mask; | |
50 | else | |
51 | return in_be32(&data->base->gpdat) & mask; | |
07d31f8f | 52 | } |
53 | ||
d5d6b548 | 54 | static inline u32 mpc8xxx_gpio_get_dir(struct udevice *dev, u32 mask) |
07d31f8f | 55 | { |
d5d6b548 | 56 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
57 | ||
58 | if (data->little_endian) | |
59 | return in_le32(&data->base->gpdir) & mask; | |
60 | else | |
61 | return in_be32(&data->base->gpdir) & mask; | |
07d31f8f | 62 | } |
63 | ||
d5d6b548 | 64 | static inline int mpc8xxx_gpio_open_drain_val(struct udevice *dev, u32 mask) |
51781783 | 65 | { |
d5d6b548 | 66 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
67 | ||
68 | if (data->little_endian) | |
69 | return in_le32(&data->base->gpodr) & mask; | |
70 | else | |
71 | return in_be32(&data->base->gpodr) & mask; | |
51781783 | 72 | } |
73 | ||
d5d6b548 | 74 | static inline void mpc8xxx_gpio_open_drain_on(struct udevice *dev, u32 |
51781783 | 75 | gpios) |
76 | { | |
d5d6b548 | 77 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
51781783 | 78 | /* GPODR register 1 -> open drain on */ |
d5d6b548 | 79 | if (data->little_endian) |
80 | setbits_le32(&data->base->gpodr, gpios); | |
81 | else | |
82 | setbits_be32(&data->base->gpodr, gpios); | |
51781783 | 83 | } |
84 | ||
d5d6b548 | 85 | static inline void mpc8xxx_gpio_open_drain_off(struct udevice *dev, |
51781783 | 86 | u32 gpios) |
87 | { | |
d5d6b548 | 88 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
51781783 | 89 | /* GPODR register 0 -> open drain off (actively driven) */ |
d5d6b548 | 90 | if (data->little_endian) |
91 | clrbits_le32(&data->base->gpodr, gpios); | |
92 | else | |
93 | clrbits_be32(&data->base->gpodr, gpios); | |
51781783 | 94 | } |
95 | ||
3c216834 | 96 | static int mpc8xxx_gpio_direction_input(struct udevice *dev, uint gpio) |
07d31f8f | 97 | { |
3c216834 | 98 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
1d7ad9fa RV |
99 | u32 mask = gpio_mask(gpio); |
100 | ||
101 | /* GPDIR register 0 -> input */ | |
d5d6b548 | 102 | if (data->little_endian) |
103 | clrbits_le32(&data->base->gpdir, mask); | |
104 | else | |
105 | clrbits_be32(&data->base->gpdir, mask); | |
07d31f8f | 106 | |
07d31f8f | 107 | return 0; |
108 | } | |
109 | ||
3c216834 | 110 | static int mpc8xxx_gpio_set_value(struct udevice *dev, uint gpio, int value) |
07d31f8f | 111 | { |
3c216834 | 112 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
dd4cf53f RV |
113 | struct ccsr_gpio *base = data->base; |
114 | u32 mask = gpio_mask(gpio); | |
115 | u32 gpdir; | |
07d31f8f | 116 | |
117 | if (value) { | |
dd4cf53f | 118 | data->dat_shadow |= mask; |
07d31f8f | 119 | } else { |
dd4cf53f | 120 | data->dat_shadow &= ~mask; |
07d31f8f | 121 | } |
dd4cf53f | 122 | |
d5d6b548 | 123 | if (data->little_endian) |
124 | gpdir = in_le32(&base->gpdir); | |
125 | else | |
126 | gpdir = in_be32(&base->gpdir); | |
127 | ||
dd4cf53f | 128 | gpdir |= gpio_mask(gpio); |
d5d6b548 | 129 | |
130 | if (data->little_endian) { | |
131 | out_le32(&base->gpdat, gpdir & data->dat_shadow); | |
132 | out_le32(&base->gpdir, gpdir); | |
133 | } else { | |
134 | out_be32(&base->gpdat, gpdir & data->dat_shadow); | |
135 | out_be32(&base->gpdir, gpdir); | |
136 | } | |
dd4cf53f | 137 | |
07d31f8f | 138 | return 0; |
139 | } | |
140 | ||
3c216834 | 141 | static int mpc8xxx_gpio_direction_output(struct udevice *dev, uint gpio, |
07d31f8f | 142 | int value) |
143 | { | |
f9c7fde2 MS |
144 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
145 | ||
146 | /* GPIO 28..31 are input only on MPC5121 */ | |
147 | if (data->type == MPC5121_GPIO_TYPE && gpio >= 28) | |
148 | return -EINVAL; | |
149 | ||
3c216834 | 150 | return mpc8xxx_gpio_set_value(dev, gpio, value); |
07d31f8f | 151 | } |
152 | ||
3c216834 | 153 | static int mpc8xxx_gpio_get_value(struct udevice *dev, uint gpio) |
07d31f8f | 154 | { |
3c216834 | 155 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
07d31f8f | 156 | |
d5d6b548 | 157 | if (!!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio))) { |
07d31f8f | 158 | /* Output -> use shadowed value */ |
159 | return !!(data->dat_shadow & gpio_mask(gpio)); | |
07d31f8f | 160 | } |
aadc5e67 MS |
161 | |
162 | /* Input -> read value from GPDAT register */ | |
d5d6b548 | 163 | return !!mpc8xxx_gpio_get_val(dev, gpio_mask(gpio)); |
07d31f8f | 164 | } |
165 | ||
3c216834 | 166 | static int mpc8xxx_gpio_get_function(struct udevice *dev, uint gpio) |
07d31f8f | 167 | { |
07d31f8f | 168 | int dir; |
169 | ||
d5d6b548 | 170 | dir = !!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio)); |
07d31f8f | 171 | return dir ? GPIOF_OUTPUT : GPIOF_INPUT; |
172 | } | |
173 | ||
4b689f02 | 174 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
d1998a9f | 175 | static int mpc8xxx_gpio_of_to_plat(struct udevice *dev) |
aadc5e67 | 176 | { |
c69cda25 | 177 | struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev); |
d5d6b548 | 178 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
d5d6b548 | 179 | |
78118809 | 180 | if (dev_read_bool(dev, "little-endian")) |
d5d6b548 | 181 | data->little_endian = true; |
182 | ||
271a87b4 | 183 | plat->addr = dev_read_addr_size_index(dev, 0, (fdt_size_t *)&plat->size); |
f5ac4f2e | 184 | plat->ngpios = dev_read_u32_default(dev, "ngpios", 32); |
07d31f8f | 185 | |
4b689f02 HM |
186 | return 0; |
187 | } | |
188 | #endif | |
189 | ||
8a8d24bd | 190 | static int mpc8xxx_gpio_plat_to_priv(struct udevice *dev) |
4b689f02 | 191 | { |
3c216834 | 192 | struct mpc8xxx_gpio_data *priv = dev_get_priv(dev); |
c69cda25 | 193 | struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev); |
4b689f02 | 194 | unsigned long size = plat->size; |
f9c7fde2 | 195 | ulong driver_data = dev_get_driver_data(dev); |
4b689f02 HM |
196 | |
197 | if (size == 0) | |
198 | size = 0x100; | |
199 | ||
200 | priv->addr = plat->addr; | |
f5ac4f2e | 201 | priv->base = map_sysmem(plat->addr, size); |
4b689f02 HM |
202 | |
203 | if (!priv->base) | |
07d31f8f | 204 | return -ENOMEM; |
205 | ||
4b689f02 HM |
206 | priv->gpio_count = plat->ngpios; |
207 | priv->dat_shadow = 0; | |
07d31f8f | 208 | |
3c216834 MS |
209 | priv->type = driver_data; |
210 | ||
07d31f8f | 211 | return 0; |
212 | } | |
213 | ||
3c216834 | 214 | static int mpc8xxx_gpio_probe(struct udevice *dev) |
07d31f8f | 215 | { |
216 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); | |
3c216834 | 217 | struct mpc8xxx_gpio_data *data = dev_get_priv(dev); |
07d31f8f | 218 | char name[32], *str; |
219 | ||
8a8d24bd | 220 | mpc8xxx_gpio_plat_to_priv(dev); |
4b689f02 | 221 | |
271a87b4 BM |
222 | snprintf(name, sizeof(name), "MPC@%.8llx", |
223 | (unsigned long long)data->addr); | |
07d31f8f | 224 | str = strdup(name); |
225 | ||
226 | if (!str) | |
227 | return -ENOMEM; | |
228 | ||
78118809 BL |
229 | if (device_is_compatible(dev, "fsl,qoriq-gpio")) { |
230 | if (data->little_endian) | |
231 | out_le32(&data->base->gpibe, 0xffffffff); | |
232 | else | |
233 | out_be32(&data->base->gpibe, 0xffffffff); | |
d5d6b548 | 234 | } |
235 | ||
07d31f8f | 236 | uc_priv->bank_name = str; |
237 | uc_priv->gpio_count = data->gpio_count; | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
3c216834 MS |
242 | static const struct dm_gpio_ops gpio_mpc8xxx_ops = { |
243 | .direction_input = mpc8xxx_gpio_direction_input, | |
244 | .direction_output = mpc8xxx_gpio_direction_output, | |
245 | .get_value = mpc8xxx_gpio_get_value, | |
246 | .set_value = mpc8xxx_gpio_set_value, | |
3c216834 | 247 | .get_function = mpc8xxx_gpio_get_function, |
07d31f8f | 248 | }; |
249 | ||
3c216834 | 250 | static const struct udevice_id mpc8xxx_gpio_ids[] = { |
f9c7fde2 MS |
251 | { .compatible = "fsl,pq3-gpio", .data = MPC8XXX_GPIO_TYPE }, |
252 | { .compatible = "fsl,mpc8308-gpio", .data = MPC8XXX_GPIO_TYPE }, | |
253 | { .compatible = "fsl,mpc8349-gpio", .data = MPC8XXX_GPIO_TYPE }, | |
254 | { .compatible = "fsl,mpc8572-gpio", .data = MPC8XXX_GPIO_TYPE}, | |
255 | { .compatible = "fsl,mpc8610-gpio", .data = MPC8XXX_GPIO_TYPE}, | |
256 | { .compatible = "fsl,mpc5121-gpio", .data = MPC5121_GPIO_TYPE, }, | |
257 | { .compatible = "fsl,qoriq-gpio", .data = MPC8XXX_GPIO_TYPE }, | |
07d31f8f | 258 | { /* sentinel */ } |
259 | }; | |
260 | ||
3c216834 MS |
261 | U_BOOT_DRIVER(gpio_mpc8xxx) = { |
262 | .name = "gpio_mpc8xxx", | |
07d31f8f | 263 | .id = UCLASS_GPIO, |
3c216834 | 264 | .ops = &gpio_mpc8xxx_ops, |
4b689f02 | 265 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
d1998a9f | 266 | .of_to_plat = mpc8xxx_gpio_of_to_plat, |
caa4daa2 | 267 | .plat_auto = sizeof(struct mpc8xxx_gpio_plat), |
3c216834 | 268 | .of_match = mpc8xxx_gpio_ids, |
4b689f02 | 269 | #endif |
3c216834 | 270 | .probe = mpc8xxx_gpio_probe, |
41575d8e | 271 | .priv_auto = sizeof(struct mpc8xxx_gpio_data), |
07d31f8f | 272 | }; |