]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/p2sb/lewisburg_gpiocm.c
zfs: merge openzfs/zfs@4a1195ca5 (master) into main
[FreeBSD/FreeBSD.git] / sys / dev / p2sb / lewisburg_gpiocm.c
1 /*-
2  * Copyright (c) 2018 Stormshield
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/param.h>
28 #include <sys/module.h>
29 #include <sys/systm.h>
30 #include <sys/errno.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/bus.h>
34 #include <sys/gpio.h>
35
36 #include <machine/bus.h>
37 #include <sys/rman.h>
38 #include <machine/resource.h>
39
40 #include "gpio_if.h"
41
42 #include "lewisburg_gpiocm.h"
43 #include "p2sb.h"
44
45 #define PADBAR  0x00c
46
47 #define PADCFG0_GPIORXDIS       (1<<9)
48 #define PADCFG0_GPIOTXDIS       (1<<8)
49 #define PADCFG0_GPIORXSTATE     (1<<1)
50 #define PADCFG0_GPIOTXSTATE     (1<<0)
51
52 #define MAX_PAD_PER_GROUP       24
53
54 #define LBGGPIOCM_READ(sc, reg) p2sb_port_read_4(sc->p2sb, sc->port, reg)
55 #define LBGGPIOCM_WRITE(sc, reg, val) \
56         p2sb_port_write_4(sc->p2sb, sc->port, reg, val)
57 #define LBGGPIOCM_LOCK(sc) p2sb_lock(sc->p2sb)
58 #define LBGGPIOCM_UNLOCK(sc) p2sb_unlock(sc->p2sb)
59
60 struct lbggroup {
61         int groupid;
62         int npins;
63         int pins_off;
64         device_t dev;
65         char grpname;
66 };
67
68 struct lbgcommunity {
69         uint8_t npins;
70         const char *name;
71         uint32_t pad_off;
72         struct lbggroup groups[3];
73         int ngroups;
74         const char *grpnames;
75 };
76 #define LBG_COMMUNITY(n, np, g) \
77 { \
78         .name = n, \
79         .npins = np, \
80         .grpnames = g, \
81 }
82
83 static struct lbgcommunity lbg_communities[] = {
84         LBG_COMMUNITY("LewisBurg GPIO Community 0", 72, "ABF"),
85         LBG_COMMUNITY("LewisBurg GPIO Community 1", 61, "CDE"),
86         LBG_COMMUNITY("LewisBurg GPIO Community 2", 0, ""),
87         LBG_COMMUNITY("LewisBurg GPIO Community 3", 12, "I"),
88         LBG_COMMUNITY("LewisBurg GPIO Community 4", 36, "JK"),
89         LBG_COMMUNITY("LewisBurg GPIO Community 5", 66, "GHL"),
90 };
91
92 struct lbggpiocm_softc
93 {
94         int port;
95         device_t p2sb;
96         struct lbgcommunity *community;
97 };
98
99 static struct lbggroup *lbggpiocm_get_group(struct lbggpiocm_softc *sc,
100     device_t child);
101
102 static __inline struct lbggroup *
103 lbggpiocm_get_group(struct lbggpiocm_softc *sc, device_t child)
104 {
105         int i;
106
107         for (i = 0; i < sc->community->ngroups; ++i)
108                 if (sc->community->groups[i].dev == child)
109                         return (&sc->community->groups[i]);
110         return (NULL);
111 }
112
113
114 static __inline uint32_t
115 lbggpiocm_getpad(struct lbggpiocm_softc *sc, uint32_t pin)
116 {
117
118         if (pin >= sc->community->npins)
119                 return (0);
120
121         return (sc->community->pad_off + 2 * 4 * pin);
122 }
123
124 int
125 lbggpiocm_get_group_npins(device_t dev, device_t child)
126 {
127         struct lbggpiocm_softc *sc = device_get_softc(dev);
128         struct lbggroup *group;
129
130         group = lbggpiocm_get_group(sc, child);
131         if (group != NULL)
132                 return (group->npins);
133         return (-1);
134 }
135
136 char
137 lbggpiocm_get_group_name(device_t dev, device_t child)
138 {
139         struct lbggpiocm_softc *sc = device_get_softc(dev);
140         struct lbggroup *group;
141
142         group = lbggpiocm_get_group(sc, child);
143         if (group != NULL)
144                 return (group->grpname);
145         return ('\0');
146 }
147
148 static int
149 lbggpiocm_pin2cpin(struct lbggpiocm_softc *sc, device_t child, uint32_t pin)
150 {
151         struct lbggroup *group;
152
153         group = lbggpiocm_get_group(sc, child);
154         if (group != NULL)
155                 return (pin + group->pins_off);
156         return (-1);
157 }
158
159 int
160 lbggpiocm_pin_setflags(device_t dev, device_t child, uint32_t pin, uint32_t flags)
161 {
162         struct lbggpiocm_softc *sc = device_get_softc(dev);
163         uint32_t padreg, padval;
164         int rpin;
165
166         if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
167             (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
168                 return (EINVAL);
169
170         if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0)
171                 return (EINVAL);
172
173         rpin = lbggpiocm_pin2cpin(sc, child, pin);
174         if (rpin < 0)
175                 return (EINVAL);
176
177         padreg = lbggpiocm_getpad(sc, rpin);
178
179         LBGGPIOCM_LOCK(sc);
180         padval = LBGGPIOCM_READ(sc, padreg);
181
182         if (flags & GPIO_PIN_INPUT) {
183                 padval &= ~PADCFG0_GPIORXDIS;
184                 padval |= PADCFG0_GPIOTXDIS;
185         } else if (flags & GPIO_PIN_OUTPUT) {
186                 padval &= ~PADCFG0_GPIOTXDIS;
187                 padval |= PADCFG0_GPIORXDIS;
188         }
189
190         LBGGPIOCM_WRITE(sc, padreg, padval);
191         LBGGPIOCM_UNLOCK(sc);
192
193         return (0);
194 }
195
196 int
197 lbggpiocm_pin_get(device_t dev, device_t child, uint32_t pin, uint32_t *value)
198 {
199         struct lbggpiocm_softc *sc = device_get_softc(dev);
200         uint32_t padreg, val;
201         int rpin;
202
203         if (value == NULL)
204                 return (EINVAL);
205
206         rpin = lbggpiocm_pin2cpin(sc, child, pin);
207         if (rpin < 0)
208                 return (EINVAL);
209
210         padreg = lbggpiocm_getpad(sc, rpin);
211
212         LBGGPIOCM_LOCK(sc);
213         val = LBGGPIOCM_READ(sc, padreg);
214         LBGGPIOCM_UNLOCK(sc);
215
216         if (!(val & PADCFG0_GPIOTXDIS))
217                 *value = !!(val & PADCFG0_GPIOTXSTATE);
218         else
219                 *value = !!(val & PADCFG0_GPIORXSTATE);
220
221         return (0);
222 }
223
224 int
225 lbggpiocm_pin_set(device_t dev, device_t child, uint32_t pin, uint32_t value)
226 {
227         struct lbggpiocm_softc *sc = device_get_softc(dev);
228         uint32_t padreg, padcfg;
229         int rpin;
230
231         rpin = lbggpiocm_pin2cpin(sc, child, pin);
232         if (rpin < 0)
233                 return (EINVAL);
234
235         padreg = lbggpiocm_getpad(sc, rpin);
236
237         LBGGPIOCM_LOCK(sc);
238
239         padcfg = LBGGPIOCM_READ(sc, padreg);
240         if (value)
241                 padcfg |= PADCFG0_GPIOTXSTATE;
242         else
243                 padcfg &= ~PADCFG0_GPIOTXSTATE;
244         LBGGPIOCM_WRITE(sc, padreg, padcfg);
245
246         LBGGPIOCM_UNLOCK(sc);
247
248         return (0);
249 }
250
251 int
252 lbggpiocm_pin_toggle(device_t dev, device_t child, uint32_t pin)
253 {
254         struct lbggpiocm_softc *sc = device_get_softc(dev);
255         uint32_t padreg, padcfg;
256         int rpin;
257
258         rpin = lbggpiocm_pin2cpin(sc, child, pin);
259         if (rpin < 0)
260                 return (EINVAL);
261
262         padreg = lbggpiocm_getpad(sc, rpin);
263
264         LBGGPIOCM_LOCK(sc);
265         padcfg = LBGGPIOCM_READ(sc, padreg);
266         padcfg ^= PADCFG0_GPIOTXSTATE;
267         LBGGPIOCM_WRITE(sc, padreg, padcfg);
268
269         LBGGPIOCM_UNLOCK(sc);
270
271         return (0);
272 }
273
274 static int
275 lbggpiocm_probe(device_t dev)
276 {
277         struct lbggpiocm_softc *sc = device_get_softc(dev);
278         int unit;
279
280         sc->p2sb = device_get_parent(dev);
281         unit = device_get_unit(dev);
282         KASSERT(unit < nitems(lbg_communities), ("Wrong number of devices or communities"));
283         sc->port = p2sb_get_port(sc->p2sb, unit);
284         sc->community = &lbg_communities[unit];
285         if (sc->port < 0)
286                 return (ENXIO);
287
288         device_set_desc(dev, sc->community->name);
289         return (BUS_PROBE_DEFAULT);
290 }
291
292 static int
293 lbggpiocm_attach(device_t dev)
294 {
295         uint32_t npins;
296         struct lbggpiocm_softc *sc;
297         struct lbggroup *group;
298         int i;
299
300         sc = device_get_softc(dev);
301         if (sc->community->npins == 0)
302                 return (ENXIO);
303
304         LBGGPIOCM_LOCK(sc);
305         sc->community->pad_off = LBGGPIOCM_READ(sc, PADBAR);
306         LBGGPIOCM_UNLOCK(sc);
307
308         npins = sc->community->npins;
309         for (i = 0; i < nitems(sc->community->groups) && npins > 0; ++i) {
310                 group = &sc->community->groups[i];
311
312                 group->groupid = i;
313                 group->grpname = sc->community->grpnames[i];
314                 group->pins_off = i * MAX_PAD_PER_GROUP;
315                 group->npins = npins < MAX_PAD_PER_GROUP ? npins :
316                         MAX_PAD_PER_GROUP;
317                 npins -= group->npins;
318                 group->dev = device_add_child(dev, "gpio", -1);
319         }
320         sc->community->ngroups = i;
321         return (bus_generic_attach(dev));
322 }
323
324 static int
325 lbggpiocm_detach(device_t dev)
326 {
327         int error;
328
329         error = device_delete_children(dev);
330         if (error)
331                 return (error);
332
333         return (bus_generic_detach(dev));
334 }
335
336 static device_method_t lbggpiocm_methods[] = {
337         /* Device interface */
338         DEVMETHOD(device_probe,         lbggpiocm_probe),
339         DEVMETHOD(device_attach,        lbggpiocm_attach),
340         DEVMETHOD(device_detach,        lbggpiocm_detach),
341
342         DEVMETHOD_END
343 };
344
345 static driver_t lbggpiocm_driver = {
346         "lbggpiocm",
347         lbggpiocm_methods,
348         sizeof(struct lbggpiocm_softc)
349 };
350 static devclass_t lbggpiocm_devclass;
351 DRIVER_MODULE(lbggpiocm, p2sb, lbggpiocm_driver, lbggpiocm_devclass, NULL, NULL);