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 *
lbggpiocm_get_group(struct lbggpiocm_softc * sc,device_t child)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
lbggpiocm_getpad(struct lbggpiocm_softc * sc,uint32_t pin)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
lbggpiocm_get_group_npins(device_t dev,device_t child)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
lbggpiocm_get_group_name(device_t dev,device_t child)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
lbggpiocm_pin2cpin(struct lbggpiocm_softc * sc,device_t child,uint32_t pin)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
lbggpiocm_pin_setflags(device_t dev,device_t child,uint32_t pin,uint32_t flags)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
lbggpiocm_pin_get(device_t dev,device_t child,uint32_t pin,uint32_t * value)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
lbggpiocm_pin_set(device_t dev,device_t child,uint32_t pin,uint32_t value)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
lbggpiocm_pin_toggle(device_t dev,device_t child,uint32_t pin)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
lbggpiocm_probe(device_t dev)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
lbggpiocm_attach(device_t dev)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 bus_attach_children(dev);
322 return (0);
323 }
324
325 static int
lbggpiocm_detach(device_t dev)326 lbggpiocm_detach(device_t dev)
327 {
328 int error;
329
330 error = device_delete_children(dev);
331 if (error)
332 return (error);
333
334 return (bus_generic_detach(dev));
335 }
336
337 static device_method_t lbggpiocm_methods[] = {
338 /* Device interface */
339 DEVMETHOD(device_probe, lbggpiocm_probe),
340 DEVMETHOD(device_attach, lbggpiocm_attach),
341 DEVMETHOD(device_detach, lbggpiocm_detach),
342
343 DEVMETHOD_END
344 };
345
346 static driver_t lbggpiocm_driver = {
347 "lbggpiocm",
348 lbggpiocm_methods,
349 sizeof(struct lbggpiocm_softc)
350 };
351
352 DRIVER_MODULE(lbggpiocm, p2sb, lbggpiocm_driver, NULL, NULL);
353