xref: /freebsd/sys/dev/p2sb/lewisburg_gpiocm.c (revision ba58fd668a0e544b4af225f6dfee402e3213a8bf)
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 	return (bus_generic_attach(dev));
322 }
323 
324 static int
lbggpiocm_detach(device_t dev)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 
351 DRIVER_MODULE(lbggpiocm, p2sb, lbggpiocm_driver, NULL, NULL);
352