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