blob: e2b12f8b56c744707b19f0377e43e35511845523 [file] [log] [blame]
Christophe Leroyb7df9082022-10-14 10:01:41 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2020 CS Group
4 * Charles Frey <charles.frey@c-s.fr>
5 *
6 * based on driver/gpio/mpc8xxx_gpio.c, which is
7 * Copyright 2016 Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
8 *
9 * based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is
10 * Copyright 2010 eXMeritus, A Boeing Company
11 */
12
Christophe Leroyb7df9082022-10-14 10:01:41 +020013#include <asm/io.h>
14#include <dm.h>
15#include <mapmem.h>
16#include <asm/gpio.h>
17#include <malloc.h>
18
19enum {
20 MPC8XX_CPM1_PORTA,
21 MPC8XX_CPM1_PORTB,
22 MPC8XX_CPM1_PORTC,
23 MPC8XX_CPM1_PORTD,
24 MPC8XX_CPM1_PORTE,
25};
26
27/*
28 * The MPC885 CPU CPM has 5 I/O ports, and each ports has different
29 * register length : 16 bits for ports A,C,D and 32 bits for ports
30 * B and E.
31 *
32 * This structure allows us to select the accessors according to the
33 * port we are configuring.
34 */
35struct mpc8xx_gpio_data {
36 /* The bank's register base in memory */
37 void __iomem *base;
38 /* The address of the registers; used to identify the bank */
39 ulong addr;
40 /* The GPIO count of the bank */
41 uint gpio_count;
42 /* Type needed to use the correct accessors */
43 int type;
44};
45
46/* Structure for ports A, C, D */
47struct iop_16 {
48 u16 pdir;
49 u16 ppar;
50 u16 podr;
51 u16 pdat;
52};
53
54/* Port B */
55struct iop_32_b {
56 u32 pdir;
57 u32 ppar;
58 u32 podr;
59 u32 pdat;
60};
61
62/* Port E */
63struct iop_32_e {
64 u32 pdir;
65 u32 ppar;
66 u32 psor;
67 u32 podr;
68 u32 pdat;
69};
70
71union iop_32 {
72 struct iop_32_b b;
73 struct iop_32_e e;
74};
75
76inline u32 gpio_mask(uint gpio, int type)
77{
78 if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
79 return 1U << (31 - (gpio));
80 else
81 return 1U << (15 - (gpio));
82}
83
84static inline u16 gpio16_get_val(void __iomem *base, u16 mask, int type)
85{
86 struct iop_16 *regs = base;
87
88 return in_be16(&regs->pdat) & mask;
89}
90
91static inline u16 gpio16_get_dir(void __iomem *base, u16 mask, int type)
92{
93 struct iop_16 *regs = base;
94
95 return in_be16(&regs->pdir) & mask;
96}
97
98static inline void gpio16_set_in(void __iomem *base, u16 gpios, int type)
99{
100 struct iop_16 *regs = base;
101
102 clrbits_be16(&regs->pdat, gpios);
103 /* GPDIR register 0 -> input */
104 clrbits_be16(&regs->pdir, gpios);
105}
106
107static inline void gpio16_set_lo(void __iomem *base, u16 gpios, int type)
108{
109 struct iop_16 *regs = base;
110
111 clrbits_be16(&regs->pdat, gpios);
112 /* GPDIR register 1 -> output */
113 setbits_be16(&regs->pdir, gpios);
114}
115
116static inline void gpio16_set_hi(void __iomem *base, u16 gpios, int type)
117{
118 struct iop_16 *regs = base;
119
120 setbits_be16(&regs->pdat, gpios);
121 /* GPDIR register 1 -> output */
122 setbits_be16(&regs->pdir, gpios);
123}
124
125/* PORT B AND E */
126static inline u32 gpio32_get_val(void __iomem *base, u32 mask, int type)
127{
128 union iop_32 __iomem *regs = base;
129
130 if (type == MPC8XX_CPM1_PORTB)
131 return in_be32(&regs->b.pdat) & mask;
132 else
133 return in_be32(&regs->e.pdat) & mask;
134}
135
136static inline u32 gpio32_get_dir(void __iomem *base, u32 mask, int type)
137{
138 union iop_32 __iomem *regs = base;
139
140 if (type == MPC8XX_CPM1_PORTB)
141 return in_be32(&regs->b.pdir) & mask;
142 else
143 return in_be32(&regs->e.pdir) & mask;
144}
145
146static inline void gpio32_set_in(void __iomem *base, u32 gpios, int type)
147{
148 union iop_32 __iomem *regs = base;
149
150 if (type == MPC8XX_CPM1_PORTB) {
151 clrbits_be32(&regs->b.pdat, gpios);
152 /* GPDIR register 0 -> input */
153 clrbits_be32(&regs->b.pdir, gpios);
154 } else { /* Port E */
155 clrbits_be32(&regs->e.pdat, gpios);
156 /* GPDIR register 0 -> input */
157 clrbits_be32(&regs->e.pdir, gpios);
158 }
159}
160
161static inline void gpio32_set_lo(void __iomem *base, u32 gpios, int type)
162{
163 union iop_32 __iomem *regs = base;
164
165 if (type == MPC8XX_CPM1_PORTB) {
166 clrbits_be32(&regs->b.pdat, gpios);
167 /* GPDIR register 1 -> output */
168 setbits_be32(&regs->b.pdir, gpios);
169 } else {
170 clrbits_be32(&regs->e.pdat, gpios);
171 /* GPDIR register 1 -> output */
172 setbits_be32(&regs->e.pdir, gpios);
173 }
174}
175
176static inline void gpio32_set_hi(void __iomem *base, u32 gpios, int type)
177{
178 union iop_32 __iomem *regs = base;
179
180 if (type == MPC8XX_CPM1_PORTB) {
181 setbits_be32(&regs->b.pdat, gpios);
182 /* GPDIR register 1 -> output */
183 setbits_be32(&regs->b.pdir, gpios);
184 } else {
185 setbits_be32(&regs->e.pdat, gpios);
186 /* GPDIR register 1 -> output */
187 setbits_be32(&regs->e.pdir, gpios);
188 }
189}
190
191static int mpc8xx_gpio_direction_input(struct udevice *dev, uint gpio)
192{
193 struct mpc8xx_gpio_data *data = dev_get_priv(dev);
194 int type = data->type;
195
196 if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
197 gpio32_set_in(data->base, gpio_mask(gpio, type), type);
198 else
199 gpio16_set_in(data->base, gpio_mask(gpio, type), type);
200
201 return 0;
202}
203
204static int mpc8xx_gpio_set_value(struct udevice *dev, uint gpio, int value)
205{
206 struct mpc8xx_gpio_data *data = dev_get_priv(dev);
207 int type = data->type;
208
209 if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE) {
210 if (value)
211 gpio32_set_hi(data->base, gpio_mask(gpio, type), type);
212 else
213 gpio32_set_lo(data->base, gpio_mask(gpio, type), type);
214 } else {
215 if (value)
216 gpio16_set_hi(data->base, gpio_mask(gpio, type), type);
217 else
218 gpio16_set_lo(data->base, gpio_mask(gpio, type), type);
219 }
220
221 return 0;
222}
223
224static int mpc8xx_gpio_direction_output(struct udevice *dev, uint gpio,
225 int value)
226{
227 return mpc8xx_gpio_set_value(dev, gpio, value);
228}
229
230static int mpc8xx_gpio_get_value(struct udevice *dev, uint gpio)
231{
232 struct mpc8xx_gpio_data *data = dev_get_priv(dev);
233 int type = data->type;
234
235 /* Input -> read value from GPDAT register */
236 if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
237 return gpio32_get_val(data->base, gpio_mask(gpio, type), type);
238 else
239 return gpio16_get_val(data->base, gpio_mask(gpio, type), type);
240}
241
242static int mpc8xx_gpio_get_function(struct udevice *dev, uint gpio)
243{
244 struct mpc8xx_gpio_data *data = dev_get_priv(dev);
245 int type = data->type;
246 int dir;
247
248 if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
249 dir = gpio32_get_dir(data->base, gpio_mask(gpio, type), type);
250 else
251 dir = gpio16_get_dir(data->base, gpio_mask(gpio, type), type);
252 return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
253}
254
255static int mpc8xx_gpio_ofdata_to_platdata(struct udevice *dev)
256{
257 struct mpc8xx_gpio_plat *plat = dev_get_plat(dev);
258 fdt_addr_t addr;
259 u32 reg[2];
260
261 dev_read_u32_array(dev, "reg", reg, 2);
262 addr = dev_translate_address(dev, reg);
263
264 plat->addr = addr;
265 plat->size = reg[1];
266 plat->ngpios = dev_read_u32_default(dev, "ngpios", 32);
267
268 return 0;
269}
270
271static int mpc8xx_gpio_platdata_to_priv(struct udevice *dev)
272{
273 struct mpc8xx_gpio_data *priv = dev_get_priv(dev);
274 struct mpc8xx_gpio_plat *plat = dev_get_plat(dev);
275 unsigned long size = plat->size;
276 int type;
277
278 if (size == 0)
279 size = 0x100;
280
281 priv->addr = plat->addr;
282 priv->base = map_sysmem(plat->addr, size);
283
284 if (!priv->base)
285 return -ENOMEM;
286
287 priv->gpio_count = plat->ngpios;
288
289 type = dev_get_driver_data(dev);
290
291 if ((type == MPC8XX_CPM1_PORTA || type == MPC8XX_CPM1_PORTC ||
292 type == MPC8XX_CPM1_PORTD) && plat->ngpios == 32)
293 priv->gpio_count = 16;
294
295 priv->type = type;
296
297 return 0;
298}
299
300static int mpc8xx_gpio_probe(struct udevice *dev)
301{
302 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
303 struct mpc8xx_gpio_data *data = dev_get_priv(dev);
304 char name[32], *str;
305
306 mpc8xx_gpio_platdata_to_priv(dev);
307
308 snprintf(name, sizeof(name), "MPC@%lx_", data->addr);
309 str = strdup(name);
310
311 if (!str)
312 return -ENOMEM;
313
314 uc_priv->bank_name = str;
315 uc_priv->gpio_count = data->gpio_count;
316
317 return 0;
318}
319
320static const struct dm_gpio_ops gpio_mpc8xx_ops = {
321 .direction_input = mpc8xx_gpio_direction_input,
322 .direction_output = mpc8xx_gpio_direction_output,
323 .get_value = mpc8xx_gpio_get_value,
324 .set_value = mpc8xx_gpio_set_value,
325 .get_function = mpc8xx_gpio_get_function,
326};
327
328static const struct udevice_id mpc8xx_gpio_ids[] = {
329 { .compatible = "fsl,cpm1-pario-bank-a", .data = MPC8XX_CPM1_PORTA },
330 { .compatible = "fsl,cpm1-pario-bank-b", .data = MPC8XX_CPM1_PORTB },
331 { .compatible = "fsl,cpm1-pario-bank-c", .data = MPC8XX_CPM1_PORTC },
332 { .compatible = "fsl,cpm1-pario-bank-d", .data = MPC8XX_CPM1_PORTD },
333 { .compatible = "fsl,cpm1-pario-bank-e", .data = MPC8XX_CPM1_PORTE },
334 { /* sentinel */ }
335};
336
337U_BOOT_DRIVER(gpio_mpc8xx) = {
338 .name = "gpio_mpc8xx",
339 .id = UCLASS_GPIO,
340 .ops = &gpio_mpc8xx_ops,
341 .of_to_plat = mpc8xx_gpio_ofdata_to_platdata,
342 .plat_auto = sizeof(struct mpc8xx_gpio_plat),
343 .of_match = mpc8xx_gpio_ids,
344 .probe = mpc8xx_gpio_probe,
345 .priv_auto = sizeof(struct mpc8xx_gpio_data),
346};