1176772Sraj/*- 2176772Sraj * Copyright 2006 by Juniper Networks. 3176772Sraj * All rights reserved. 4176772Sraj * 5176772Sraj * Redistribution and use in source and binary forms, with or without 6176772Sraj * modification, are permitted provided that the following conditions 7176772Sraj * are met: 8176772Sraj * 1. Redistributions of source code must retain the above copyright 9176772Sraj * notice, this list of conditions and the following disclaimer. 10176772Sraj * 2. Redistributions in binary form must reproduce the above copyright 11176772Sraj * notice, this list of conditions and the following disclaimer in the 12176772Sraj * documentation and/or other materials provided with the distribution. 13176772Sraj * 3. The name of the author may not be used to endorse or promote products 14176772Sraj * derived from this software without specific prior written permission. 15176772Sraj * 16176772Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17176772Sraj * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18176772Sraj * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19176772Sraj * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20176772Sraj * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21176772Sraj * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22176772Sraj * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23176772Sraj * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24176772Sraj * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25176772Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26176772Sraj * SUCH DAMAGE. 27176772Sraj */ 28176772Sraj 29176772Sraj#include <sys/cdefs.h> 30176772Sraj__FBSDID("$FreeBSD$"); 31176772Sraj 32176772Sraj#include <sys/param.h> 33176772Sraj#include <sys/systm.h> 34176772Sraj#include <sys/bus.h> 35176772Sraj#include <sys/conf.h> 36176772Sraj#include <sys/endian.h> 37176772Sraj#include <sys/kernel.h> 38176772Sraj#include <sys/malloc.h> 39176772Sraj#include <sys/queue.h> 40176772Sraj#include <sys/serial.h> 41176772Sraj 42176772Sraj#include <machine/bus.h> 43176772Sraj#include <machine/resource.h> 44176772Sraj#include <sys/rman.h> 45176772Sraj 46176772Sraj#include <dev/ic/quicc.h> 47176772Sraj 48176772Sraj#include <dev/quicc/quicc_bfe.h> 49176772Sraj#include <dev/quicc/quicc_bus.h> 50176772Sraj 51176772Sraj#define quicc_read2(r, o) \ 52176772Sraj bus_space_read_2((r)->r_bustag, (r)->r_bushandle, o) 53176772Sraj#define quicc_read4(r, o) \ 54176772Sraj bus_space_read_4((r)->r_bustag, (r)->r_bushandle, o) 55176772Sraj 56176772Sraj#define quicc_write2(r, o, v) \ 57176772Sraj bus_space_write_2((r)->r_bustag, (r)->r_bushandle, o, v) 58176772Sraj#define quicc_write4(r, o, v) \ 59176772Sraj bus_space_write_4((r)->r_bustag, (r)->r_bushandle, o, v) 60176772Sraj 61176772Srajdevclass_t quicc_devclass; 62176772Srajchar quicc_driver_name[] = "quicc"; 63176772Sraj 64227293Sedstatic MALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver"); 65176772Sraj 66176772Srajstruct quicc_device { 67176772Sraj struct rman *qd_rman; 68176772Sraj struct resource_list qd_rlist; 69176772Sraj device_t qd_dev; 70176772Sraj int qd_devtype; 71176772Sraj 72176772Sraj driver_filter_t *qd_ih; 73176772Sraj void *qd_ih_arg; 74176772Sraj}; 75176772Sraj 76176772Srajstatic int 77176772Srajquicc_bfe_intr(void *arg) 78176772Sraj{ 79176772Sraj struct quicc_device *qd; 80176772Sraj struct quicc_softc *sc = arg; 81176772Sraj uint32_t sipnr; 82176772Sraj 83176772Sraj sipnr = quicc_read4(sc->sc_rres, QUICC_REG_SIPNR_L); 84176772Sraj if (sipnr & 0x00f00000) 85176772Sraj qd = sc->sc_device; 86176772Sraj else 87176772Sraj qd = NULL; 88176772Sraj 89176772Sraj if (qd == NULL || qd->qd_ih == NULL) { 90176772Sraj device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr); 91176772Sraj return (FILTER_STRAY); 92176772Sraj } 93176772Sraj 94176772Sraj return ((*qd->qd_ih)(qd->qd_ih_arg)); 95176772Sraj} 96176772Sraj 97176772Srajint 98176772Srajquicc_bfe_attach(device_t dev) 99176772Sraj{ 100176772Sraj struct quicc_device *qd; 101176772Sraj struct quicc_softc *sc; 102176772Sraj struct resource_list_entry *rle; 103176772Sraj const char *sep; 104294883Sjhibbits rman_res_t size, start; 105176772Sraj int error; 106176772Sraj 107176772Sraj sc = device_get_softc(dev); 108176772Sraj 109176772Sraj /* 110176772Sraj * Re-allocate. We expect that the softc contains the information 111176772Sraj * collected by quicc_bfe_probe() intact. 112176772Sraj */ 113295790Sjhibbits sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid, 114295790Sjhibbits RF_ACTIVE); 115176772Sraj if (sc->sc_rres == NULL) 116176772Sraj return (ENXIO); 117176772Sraj 118176772Sraj start = rman_get_start(sc->sc_rres); 119176772Sraj size = rman_get_size(sc->sc_rres); 120176772Sraj 121176772Sraj sc->sc_rman.rm_start = start; 122176772Sraj sc->sc_rman.rm_end = start + size - 1; 123176772Sraj sc->sc_rman.rm_type = RMAN_ARRAY; 124176772Sraj sc->sc_rman.rm_descr = "QUICC resources"; 125176772Sraj error = rman_init(&sc->sc_rman); 126176772Sraj if (!error) 127176772Sraj error = rman_manage_region(&sc->sc_rman, start, 128176772Sraj start + size - 1); 129176772Sraj if (error) { 130176772Sraj bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, 131176772Sraj sc->sc_rres); 132176772Sraj return (error); 133176772Sraj } 134176772Sraj 135176772Sraj /* 136176772Sraj * Allocate interrupt resource. 137176772Sraj */ 138176772Sraj sc->sc_irid = 0; 139176772Sraj sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, 140176772Sraj RF_ACTIVE | RF_SHAREABLE); 141176772Sraj 142176772Sraj if (sc->sc_ires != NULL) { 143176772Sraj error = bus_setup_intr(dev, sc->sc_ires, 144176772Sraj INTR_TYPE_TTY, quicc_bfe_intr, NULL, sc, &sc->sc_icookie); 145176772Sraj if (error) { 146176772Sraj error = bus_setup_intr(dev, sc->sc_ires, 147176772Sraj INTR_TYPE_TTY | INTR_MPSAFE, NULL, 148176772Sraj (driver_intr_t *)quicc_bfe_intr, sc, 149176772Sraj &sc->sc_icookie); 150176772Sraj } else 151176772Sraj sc->sc_fastintr = 1; 152176772Sraj if (error) { 153176772Sraj device_printf(dev, "could not activate interrupt\n"); 154176772Sraj bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, 155176772Sraj sc->sc_ires); 156176772Sraj sc->sc_ires = NULL; 157176772Sraj } 158176772Sraj } 159176772Sraj 160176772Sraj if (sc->sc_ires == NULL) 161176772Sraj sc->sc_polled = 1; 162176772Sraj 163176772Sraj if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { 164176772Sraj sep = ""; 165176772Sraj device_print_prettyname(dev); 166176772Sraj if (sc->sc_fastintr) { 167176772Sraj printf("%sfast interrupt", sep); 168176772Sraj sep = ", "; 169176772Sraj } 170176772Sraj if (sc->sc_polled) { 171176772Sraj printf("%spolled mode", sep); 172176772Sraj sep = ", "; 173176772Sraj } 174176772Sraj printf("\n"); 175176772Sraj } 176176772Sraj 177176772Sraj sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC, 178176772Sraj M_WAITOK | M_ZERO); 179176772Sraj 180176772Sraj qd->qd_devtype = QUICC_DEVTYPE_SCC; 181176772Sraj qd->qd_rman = &sc->sc_rman; 182176772Sraj resource_list_init(&qd->qd_rlist); 183176772Sraj 184176772Sraj resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start, 185176772Sraj start + size - 1, size); 186176772Sraj 187176772Sraj resource_list_add(&qd->qd_rlist, SYS_RES_IRQ, 0, 0xf00, 0xf00, 1); 188176772Sraj rle = resource_list_find(&qd->qd_rlist, SYS_RES_IRQ, 0); 189176772Sraj rle->res = sc->sc_ires; 190176772Sraj 191176772Sraj qd->qd_dev = device_add_child(dev, NULL, -1); 192176772Sraj device_set_ivars(qd->qd_dev, (void *)qd); 193176772Sraj error = device_probe_and_attach(qd->qd_dev); 194176772Sraj 195176772Sraj /* Enable all SCC interrupts. */ 196176772Sraj quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000); 197176772Sraj 198176772Sraj /* Clear all pending interrupts. */ 199176772Sraj quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_H, ~0); 200176772Sraj quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_L, ~0); 201176772Sraj return (error); 202176772Sraj} 203176772Sraj 204176772Srajint 205176772Srajquicc_bfe_detach(device_t dev) 206176772Sraj{ 207176772Sraj struct quicc_softc *sc; 208176772Sraj 209176772Sraj sc = device_get_softc(dev); 210176772Sraj 211176772Sraj bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); 212176772Sraj bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); 213176772Sraj bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); 214176772Sraj return (0); 215176772Sraj} 216176772Sraj 217176772Srajint 218176772Srajquicc_bfe_probe(device_t dev, u_int clock) 219176772Sraj{ 220176772Sraj struct quicc_softc *sc; 221176772Sraj uint16_t rev; 222176772Sraj 223176772Sraj sc = device_get_softc(dev); 224176772Sraj sc->sc_dev = dev; 225176772Sraj if (device_get_desc(dev) == NULL) 226176772Sraj device_set_desc(dev, 227176772Sraj "Quad integrated communications controller"); 228176772Sraj 229176772Sraj sc->sc_rrid = 0; 230176772Sraj sc->sc_rtype = SYS_RES_MEMORY; 231295790Sjhibbits sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid, 232295790Sjhibbits RF_ACTIVE); 233176772Sraj if (sc->sc_rres == NULL) { 234176772Sraj sc->sc_rrid = 0; 235176772Sraj sc->sc_rtype = SYS_RES_IOPORT; 236295790Sjhibbits sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, 237295790Sjhibbits &sc->sc_rrid, RF_ACTIVE); 238176772Sraj if (sc->sc_rres == NULL) 239176772Sraj return (ENXIO); 240176772Sraj } 241176772Sraj 242176772Sraj sc->sc_clock = clock; 243176772Sraj 244176772Sraj /* 245176772Sraj * Check that the microcode revision is 0x00e8, as documented 246176772Sraj * in the MPC8555E PowerQUICC III Integrated Processor Family 247176772Sraj * Reference Manual. 248176772Sraj */ 249176772Sraj rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM); 250176772Sraj 251176772Sraj bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); 252176772Sraj return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO); 253176772Sraj} 254176772Sraj 255176772Srajstruct resource * 256176772Srajquicc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid, 257294883Sjhibbits rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 258176772Sraj{ 259176772Sraj struct quicc_device *qd; 260176772Sraj struct resource_list_entry *rle; 261176772Sraj 262176772Sraj if (device_get_parent(child) != dev) 263176772Sraj return (NULL); 264176772Sraj 265176772Sraj /* We only support default allocations. */ 266296298Sjhibbits if (!RMAN_IS_DEFAULT_RANGE(start, end)) 267176772Sraj return (NULL); 268176772Sraj 269176772Sraj qd = device_get_ivars(child); 270176772Sraj rle = resource_list_find(&qd->qd_rlist, type, *rid); 271176772Sraj if (rle == NULL) 272176772Sraj return (NULL); 273176772Sraj 274176772Sraj if (rle->res == NULL) { 275176772Sraj rle->res = rman_reserve_resource(qd->qd_rman, rle->start, 276176772Sraj rle->start + rle->count - 1, rle->count, flags, child); 277176772Sraj if (rle->res != NULL) { 278176772Sraj rman_set_bustag(rle->res, &bs_be_tag); 279176772Sraj rman_set_bushandle(rle->res, rle->start); 280176772Sraj } 281176772Sraj } 282176772Sraj return (rle->res); 283176772Sraj} 284176772Sraj 285176772Srajint 286176772Srajquicc_bus_get_resource(device_t dev, device_t child, int type, int rid, 287294883Sjhibbits rman_res_t *startp, rman_res_t *countp) 288176772Sraj{ 289176772Sraj struct quicc_device *qd; 290176772Sraj struct resource_list_entry *rle; 291176772Sraj 292176772Sraj if (device_get_parent(child) != dev) 293176772Sraj return (EINVAL); 294176772Sraj 295176772Sraj qd = device_get_ivars(child); 296176772Sraj rle = resource_list_find(&qd->qd_rlist, type, rid); 297176772Sraj if (rle == NULL) 298176772Sraj return (EINVAL); 299176772Sraj 300176772Sraj if (startp != NULL) 301176772Sraj *startp = rle->start; 302176772Sraj if (countp != NULL) 303176772Sraj *countp = rle->count; 304176772Sraj return (0); 305176772Sraj} 306176772Sraj 307176772Srajint 308176772Srajquicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 309176772Sraj{ 310176772Sraj struct quicc_device *qd; 311176772Sraj struct quicc_softc *sc; 312176772Sraj uint32_t sccr; 313176772Sraj 314176772Sraj if (device_get_parent(child) != dev) 315176772Sraj return (EINVAL); 316176772Sraj 317176772Sraj sc = device_get_softc(dev); 318176772Sraj qd = device_get_ivars(child); 319176772Sraj 320176772Sraj switch (index) { 321176772Sraj case QUICC_IVAR_CLOCK: 322176772Sraj *result = sc->sc_clock; 323176772Sraj break; 324176772Sraj case QUICC_IVAR_BRGCLK: 325176772Sraj sccr = quicc_read4(sc->sc_rres, QUICC_REG_SCCR) & 3; 326176772Sraj *result = sc->sc_clock / ((1 << (sccr + 1)) << sccr); 327176772Sraj break; 328176772Sraj case QUICC_IVAR_DEVTYPE: 329176772Sraj *result = qd->qd_devtype; 330176772Sraj break; 331176772Sraj default: 332176772Sraj return (EINVAL); 333176772Sraj } 334176772Sraj return (0); 335176772Sraj} 336176772Sraj 337176772Srajint 338176772Srajquicc_bus_release_resource(device_t dev, device_t child, int type, int rid, 339176772Sraj struct resource *res) 340176772Sraj{ 341176772Sraj struct quicc_device *qd; 342176772Sraj struct resource_list_entry *rle; 343176772Sraj 344176772Sraj if (device_get_parent(child) != dev) 345176772Sraj return (EINVAL); 346176772Sraj 347176772Sraj qd = device_get_ivars(child); 348176772Sraj rle = resource_list_find(&qd->qd_rlist, type, rid); 349176772Sraj return ((rle == NULL) ? EINVAL : 0); 350176772Sraj} 351176772Sraj 352176772Srajint 353176772Srajquicc_bus_setup_intr(device_t dev, device_t child, struct resource *r, 354176772Sraj int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg, 355176772Sraj void **cookiep) 356176772Sraj{ 357176772Sraj struct quicc_device *qd; 358176772Sraj struct quicc_softc *sc; 359176772Sraj 360176772Sraj if (device_get_parent(child) != dev) 361176772Sraj return (EINVAL); 362176772Sraj 363176772Sraj /* Interrupt handlers must be FAST or MPSAFE. */ 364176772Sraj if (filt == NULL && !(flags & INTR_MPSAFE)) 365176772Sraj return (EINVAL); 366176772Sraj 367176772Sraj sc = device_get_softc(dev); 368176772Sraj if (sc->sc_polled) 369176772Sraj return (ENXIO); 370176772Sraj 371176772Sraj if (sc->sc_fastintr && filt == NULL) { 372176772Sraj sc->sc_fastintr = 0; 373176772Sraj bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); 374176772Sraj bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, 375176772Sraj NULL, (driver_intr_t *)quicc_bfe_intr, sc, &sc->sc_icookie); 376176772Sraj } 377176772Sraj 378176772Sraj qd = device_get_ivars(child); 379176772Sraj qd->qd_ih = (filt != NULL) ? filt : (driver_filter_t *)ihand; 380176772Sraj qd->qd_ih_arg = arg; 381176772Sraj *cookiep = ihand; 382176772Sraj return (0); 383176772Sraj} 384176772Sraj 385176772Srajint 386176772Srajquicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r, 387176772Sraj void *cookie) 388176772Sraj{ 389176772Sraj struct quicc_device *qd; 390176772Sraj 391176772Sraj if (device_get_parent(child) != dev) 392176772Sraj return (EINVAL); 393176772Sraj 394176772Sraj qd = device_get_ivars(child); 395176772Sraj if (qd->qd_ih != cookie) 396176772Sraj return (EINVAL); 397176772Sraj 398176772Sraj qd->qd_ih = NULL; 399176772Sraj qd->qd_ih_arg = NULL; 400176772Sraj return (0); 401176772Sraj} 402