1/*- 2 * Copyright 2015 John Wehle <john@feith.com> 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/* 28 * Amlogic aml8726 pinctrl driver. 29 * 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/conf.h> 38#include <sys/bus.h> 39#include <sys/kernel.h> 40#include <sys/module.h> 41#include <sys/lock.h> 42#include <sys/mutex.h> 43#include <sys/resource.h> 44#include <sys/rman.h> 45 46#include <machine/bus.h> 47 48#include <dev/ofw/ofw_bus.h> 49#include <dev/ofw/ofw_bus_subr.h> 50#include <dev/fdt/fdt_pinctrl.h> 51 52#include <arm/amlogic/aml8726/aml8726_soc.h> 53#include <arm/amlogic/aml8726/aml8726_pinctrl.h> 54 55struct aml8726_pinctrl_softc { 56 device_t dev; 57 struct { 58 struct aml8726_pinctrl_function *func; 59 struct aml8726_pinctrl_pkg_pin *ppin; 60 boolean_t pud_ctrl; 61 } soc; 62 struct resource *res[6]; 63 struct mtx mtx; 64}; 65 66static struct resource_spec aml8726_pinctrl_spec[] = { 67 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* mux */ 68 { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE }, /* pu/pd */ 69 { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_SHAREABLE }, /* pull enable */ 70 { SYS_RES_MEMORY, 3, RF_ACTIVE }, /* ao mux */ 71 { SYS_RES_MEMORY, 4, RF_ACTIVE | RF_SHAREABLE }, /* ao pu/pd */ 72 { SYS_RES_MEMORY, 5, RF_ACTIVE | RF_SHAREABLE }, /* ao pull enable */ 73 { -1, 0 } 74}; 75 76#define AML_PINCTRL_LOCK(sc) mtx_lock(&(sc)->mtx) 77#define AML_PINCTRL_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 78#define AML_PINCTRL_LOCK_INIT(sc) \ 79 mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ 80 "pinctrl", MTX_DEF) 81#define AML_PINCTRL_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); 82 83#define MUX_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) 84#define MUX_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) 85 86#define PUD_WRITE_4(sc, reg, val) bus_write_4((sc)->res[1], reg, (val)) 87#define PUD_READ_4(sc, reg) bus_read_4((sc)->res[1], reg) 88 89#define PEN_WRITE_4(sc, reg, val) bus_write_4((sc)->res[2], reg, (val)) 90#define PEN_READ_4(sc, reg) bus_read_4((sc)->res[2], reg) 91 92#define AOMUX_WRITE_4(sc, reg, val) bus_write_4((sc)->res[3], reg, (val)) 93#define AOMUX_READ_4(sc, reg) bus_read_4((sc)->res[3], reg) 94 95#define AOPUD_WRITE_4(sc, reg, val) bus_write_4((sc)->res[4], reg, (val)) 96#define AOPUD_READ_4(sc, reg) bus_read_4((sc)->res[4], reg) 97 98#define AOPEN_WRITE_4(sc, reg, val) bus_write_4((sc)->res[5], reg, (val)) 99#define AOPEN_READ_4(sc, reg) bus_read_4((sc)->res[5], reg) 100 101static int 102aml8726_pinctrl_probe(device_t dev) 103{ 104 105 if (!ofw_bus_status_okay(dev)) 106 return (ENXIO); 107 108 if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-pinctrl")) 109 return (ENXIO); 110 111 device_set_desc(dev, "Amlogic aml8726 pinctrl"); 112 113 return (BUS_PROBE_DEFAULT); 114} 115 116static int 117aml8726_pinctrl_attach(device_t dev) 118{ 119 struct aml8726_pinctrl_softc *sc = device_get_softc(dev); 120 121 sc->dev = dev; 122 123 sc->soc.pud_ctrl = false; 124 125 switch (aml8726_soc_hw_rev) { 126 case AML_SOC_HW_REV_M3: 127 sc->soc.func = aml8726_m3_pinctrl; 128 sc->soc.ppin = aml8726_m3_pkg_pin; 129 break; 130 case AML_SOC_HW_REV_M6: 131 sc->soc.func = aml8726_m6_pinctrl; 132 sc->soc.ppin = aml8726_m6_pkg_pin; 133 break; 134 case AML_SOC_HW_REV_M8: 135 sc->soc.func = aml8726_m8_pinctrl; 136 sc->soc.ppin = aml8726_m8_pkg_pin; 137 sc->soc.pud_ctrl = true; 138 break; 139 case AML_SOC_HW_REV_M8B: 140 sc->soc.func = aml8726_m8b_pinctrl; 141 sc->soc.ppin = aml8726_m8b_pkg_pin; 142 sc->soc.pud_ctrl = true; 143 break; 144 default: 145 device_printf(dev, "unsupported SoC\n"); 146 return (ENXIO); 147 /* NOTREACHED */ 148 } 149 150 if (bus_alloc_resources(dev, aml8726_pinctrl_spec, sc->res)) { 151 device_printf(dev, "could not allocate resources for device\n"); 152 return (ENXIO); 153 } 154 155 AML_PINCTRL_LOCK_INIT(sc); 156 157 fdt_pinctrl_register(dev, "amlogic,pins"); 158 fdt_pinctrl_configure_tree(dev); 159 160 return (0); 161} 162 163static int 164aml8726_pinctrl_detach(device_t dev) 165{ 166 struct aml8726_pinctrl_softc *sc = device_get_softc(dev); 167 168 AML_PINCTRL_LOCK_DESTROY(sc); 169 170 bus_release_resources(dev, aml8726_pinctrl_spec, sc->res); 171 172 return (0); 173} 174 175 176static int 177aml8726_pinctrl_configure_pins(device_t dev, phandle_t cfgxref) 178{ 179 struct aml8726_pinctrl_softc *sc = device_get_softc(dev); 180 struct aml8726_pinctrl_function *cf; 181 struct aml8726_pinctrl_function *f; 182 struct aml8726_pinctrl_pkg_pin *pp; 183 struct aml8726_pinctrl_pin *cp; 184 struct aml8726_pinctrl_pin *p; 185 enum aml8726_pinctrl_pull_mode pm; 186 char *function_name; 187 char *pins; 188 char *pin_name; 189 char *pull; 190 phandle_t node; 191 ssize_t len; 192 uint32_t value; 193 194 node = OF_node_from_xref(cfgxref); 195 196 len = OF_getprop_alloc(node, "amlogic,function", 197 (void **)&function_name); 198 199 if (len < 0) { 200 device_printf(dev, 201 "missing amlogic,function attribute in FDT\n"); 202 return (ENXIO); 203 } 204 205 for (f = sc->soc.func; f->name != NULL; f++) 206 if (strncmp(f->name, function_name, len) == 0) 207 break; 208 209 if (f->name == NULL) { 210 device_printf(dev, "unknown function attribute %.*s in FDT\n", 211 len, function_name); 212 OF_prop_free(function_name); 213 return (ENXIO); 214 } 215 216 OF_prop_free(function_name); 217 218 len = OF_getprop_alloc(node, "amlogic,pull", 219 (void **)&pull); 220 221 pm = aml8726_unknown_pm; 222 223 if (len > 0) { 224 if (strncmp(pull, "enable", len) == 0) 225 pm = aml8726_enable_pm; 226 else if (strncmp(pull, "disable", len) == 0) 227 pm = aml8726_disable_pm; 228 else if (strncmp(pull, "down", len) == 0) 229 pm = aml8726_enable_down_pm; 230 else if (strncmp(pull, "up", len) == 0) 231 pm = aml8726_enable_up_pm; 232 else { 233 device_printf(dev, 234 "unknown pull attribute %.*s in FDT\n", 235 len, pull); 236 OF_prop_free(pull); 237 return (ENXIO); 238 } 239 } 240 241 OF_prop_free(pull); 242 243 /* 244 * Setting the pull direction isn't supported on all SoC. 245 */ 246 switch (pm) { 247 case aml8726_enable_down_pm: 248 case aml8726_enable_up_pm: 249 if (sc->soc.pud_ctrl == false) { 250 device_printf(dev, 251 "SoC doesn't support setting pull direction.\n"); 252 return (ENXIO); 253 } 254 break; 255 default: 256 break; 257 } 258 259 len = OF_getprop_alloc(node, "amlogic,pins", 260 (void **)&pins); 261 262 if (len < 0) { 263 device_printf(dev, "missing amlogic,pins attribute in FDT\n"); 264 return (ENXIO); 265 } 266 267 pin_name = pins; 268 269 while (len) { 270 for (p = f->pins; p->name != NULL; p++) 271 if (strncmp(p->name, pin_name, len) == 0) 272 break; 273 274 if (p->name == NULL) { 275 /* display message prior to queuing up next string */ 276 device_printf(dev, "unknown pin attribute %.*s in FDT\n", 277 len, pin_name); 278 } 279 280 /* queue up next string */ 281 while (*pin_name && len) { 282 pin_name++; 283 len--; 284 } 285 if (len) { 286 pin_name++; 287 len--; 288 } 289 290 if (p->name == NULL) 291 continue; 292 293 for (pp = sc->soc.ppin; pp->pkg_name != NULL; pp++) 294 if (strcmp(pp->pkg_name, p->pkg_name) == 0) 295 break; 296 297 if (pp->pkg_name == NULL) { 298 device_printf(dev, 299 "missing entry for package pin %s\n", 300 p->pkg_name); 301 continue; 302 } 303 304 if (pm != aml8726_unknown_pm && pp->pull_bits == 0x00000000) { 305 device_printf(dev, 306 "missing pull info for package pin %s\n", 307 p->pkg_name); 308 continue; 309 } 310 311 AML_PINCTRL_LOCK(sc); 312 313 /* 314 * First clear all other mux bits associated with this 315 * package pin. This may briefly configure the pin as 316 * GPIO ... however this should be fine since after 317 * reset the default GPIO mode is input. 318 */ 319 320 for (cf = sc->soc.func; cf->name != NULL; cf++) 321 for (cp = cf->pins; cp->name != NULL; cp++) { 322 if (cp == p) 323 continue; 324 if (strcmp(cp->pkg_name, p->pkg_name) != 0) 325 continue; 326 if (cp->mux_bits == 0) 327 continue; 328 if (pp->aobus == false) { 329 value = MUX_READ_4(sc, cp->mux_addr); 330 value &= ~cp->mux_bits; 331 MUX_WRITE_4(sc, cp->mux_addr, value); 332 } else { 333 value = AOMUX_READ_4(sc, cp->mux_addr); 334 value &= ~cp->mux_bits; 335 AOMUX_WRITE_4(sc, cp->mux_addr, value); 336 } 337 } 338 339 /* 340 * Now set the desired mux bits. 341 * 342 * In the case of GPIO there's no bits to set. 343 */ 344 345 if (p->mux_bits != 0) { 346 if (pp->aobus == false) { 347 value = MUX_READ_4(sc, p->mux_addr); 348 value |= p->mux_bits; 349 MUX_WRITE_4(sc, p->mux_addr, value); 350 } else { 351 value = AOMUX_READ_4(sc, p->mux_addr); 352 value |= p->mux_bits; 353 AOMUX_WRITE_4(sc, p->mux_addr, value); 354 } 355 } 356 357 /* 358 * Finally set the pull mode if it was specified. 359 */ 360 361 switch (pm) { 362 case aml8726_enable_down_pm: 363 case aml8726_enable_up_pm: 364 if (pp->aobus == false) { 365 value = PUD_READ_4(sc, pp->pull_addr); 366 if (pm == aml8726_enable_down_pm) 367 value &= ~pp->pull_bits; 368 else 369 value |= pp->pull_bits; 370 PUD_WRITE_4(sc, pp->pull_addr, value); 371 } else { 372 value = AOPUD_READ_4(sc, pp->pull_addr); 373 if (pm == aml8726_enable_down_pm) 374 value &= ~(pp->pull_bits << 16); 375 else 376 value |= (pp->pull_bits << 16); 377 AOPUD_WRITE_4(sc, pp->pull_addr, value); 378 } 379 /* FALLTHROUGH */ 380 case aml8726_disable_pm: 381 case aml8726_enable_pm: 382 if (pp->aobus == false) { 383 value = PEN_READ_4(sc, pp->pull_addr); 384 if (pm == aml8726_disable_pm) 385 value &= ~pp->pull_bits; 386 else 387 value |= pp->pull_bits; 388 PEN_WRITE_4(sc, pp->pull_addr, value); 389 } else { 390 value = AOPEN_READ_4(sc, pp->pull_addr); 391 if (pm == aml8726_disable_pm) 392 value &= ~pp->pull_bits; 393 else 394 value |= pp->pull_bits; 395 AOPEN_WRITE_4(sc, pp->pull_addr, value); 396 } 397 break; 398 default: 399 break; 400 } 401 402 AML_PINCTRL_UNLOCK(sc); 403 } 404 405 OF_prop_free(pins); 406 407 return (0); 408} 409 410 411static device_method_t aml8726_pinctrl_methods[] = { 412 /* Device interface */ 413 DEVMETHOD(device_probe, aml8726_pinctrl_probe), 414 DEVMETHOD(device_attach, aml8726_pinctrl_attach), 415 DEVMETHOD(device_detach, aml8726_pinctrl_detach), 416 417 /* fdt_pinctrl interface */ 418 DEVMETHOD(fdt_pinctrl_configure,aml8726_pinctrl_configure_pins), 419 420 DEVMETHOD_END 421}; 422 423static driver_t aml8726_pinctrl_driver = { 424 "pinctrl", 425 aml8726_pinctrl_methods, 426 sizeof(struct aml8726_pinctrl_softc), 427}; 428 429static devclass_t aml8726_pinctrl_devclass; 430 431EARLY_DRIVER_MODULE(pinctrl, simplebus, aml8726_pinctrl_driver, 432 aml8726_pinctrl_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); 433