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 351 DRIVER_MODULE(lbggpiocm, p2sb, lbggpiocm_driver, NULL, NULL); 352