quicc_core.c revision 176772
1238106Sdes/*- 2238106Sdes * Copyright 2006 by Juniper Networks. 3238106Sdes * All rights reserved. 4238106Sdes * 5238106Sdes * Redistribution and use in source and binary forms, with or without 6238106Sdes * modification, are permitted provided that the following conditions 7238106Sdes * are met: 8238106Sdes * 1. Redistributions of source code must retain the above copyright 9238106Sdes * notice, this list of conditions and the following disclaimer. 10238106Sdes * 2. Redistributions in binary form must reproduce the above copyright 11238106Sdes * notice, this list of conditions and the following disclaimer in the 12238106Sdes * documentation and/or other materials provided with the distribution. 13238106Sdes * 3. The name of the author may not be used to endorse or promote products 14238106Sdes * derived from this software without specific prior written permission. 15238106Sdes * 16238106Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17238106Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18238106Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19238106Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20238106Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21238106Sdes * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22238106Sdes * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23238106Sdes * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24238106Sdes * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25238106Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26238106Sdes * SUCH DAMAGE. 27238106Sdes */ 28238106Sdes 29238106Sdes#include <sys/cdefs.h> 30238106Sdes__FBSDID("$FreeBSD: head/sys/dev/quicc/quicc_core.c 176772 2008-03-03 18:20:17Z raj $"); 31238106Sdes 32238106Sdes#include <sys/param.h> 33238106Sdes#include <sys/systm.h> 34238106Sdes#include <sys/bus.h> 35238106Sdes#include <sys/conf.h> 36238106Sdes#include <sys/endian.h> 37238106Sdes#include <sys/kernel.h> 38238106Sdes#include <sys/malloc.h> 39238106Sdes#include <sys/queue.h> 40238106Sdes#include <sys/serial.h> 41238106Sdes 42238106Sdes#include <machine/bus.h> 43238106Sdes#include <machine/resource.h> 44238106Sdes#include <sys/rman.h> 45238106Sdes 46238106Sdes#include <dev/ic/quicc.h> 47238106Sdes 48238106Sdes#include <dev/quicc/quicc_bfe.h> 49238106Sdes#include <dev/quicc/quicc_bus.h> 50238106Sdes 51238106Sdes#define quicc_read2(r, o) \ 52238106Sdes bus_space_read_2((r)->r_bustag, (r)->r_bushandle, o) 53238106Sdes#define quicc_read4(r, o) \ 54238106Sdes bus_space_read_4((r)->r_bustag, (r)->r_bushandle, o) 55238106Sdes 56238106Sdes#define quicc_write2(r, o, v) \ 57238106Sdes bus_space_write_2((r)->r_bustag, (r)->r_bushandle, o, v) 58238106Sdes#define quicc_write4(r, o, v) \ 59238106Sdes bus_space_write_4((r)->r_bustag, (r)->r_bushandle, o, v) 60238106Sdes 61238106Sdesdevclass_t quicc_devclass; 62238106Sdeschar quicc_driver_name[] = "quicc"; 63238106Sdes 64238106SdesMALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver"); 65238106Sdes 66238106Sdesstruct quicc_device { 67238106Sdes struct rman *qd_rman; 68238106Sdes struct resource_list qd_rlist; 69238106Sdes device_t qd_dev; 70238106Sdes int qd_devtype; 71238106Sdes 72238106Sdes driver_filter_t *qd_ih; 73238106Sdes void *qd_ih_arg; 74238106Sdes}; 75238106Sdes 76238106Sdesstatic int 77238106Sdesquicc_bfe_intr(void *arg) 78238106Sdes{ 79238106Sdes struct quicc_device *qd; 80238106Sdes struct quicc_softc *sc = arg; 81238106Sdes uint32_t sipnr; 82238106Sdes 83238106Sdes sipnr = quicc_read4(sc->sc_rres, QUICC_REG_SIPNR_L); 84238106Sdes if (sipnr & 0x00f00000) 85238106Sdes qd = sc->sc_device; 86238106Sdes else 87238106Sdes qd = NULL; 88238106Sdes 89238106Sdes if (qd == NULL || qd->qd_ih == NULL) { 90238106Sdes device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr); 91238106Sdes return (FILTER_STRAY); 92238106Sdes } 93238106Sdes 94238106Sdes return ((*qd->qd_ih)(qd->qd_ih_arg)); 95238106Sdes} 96238106Sdes 97238106Sdesint 98238106Sdesquicc_bfe_attach(device_t dev) 99238106Sdes{ 100238106Sdes struct quicc_device *qd; 101238106Sdes struct quicc_softc *sc; 102238106Sdes struct resource_list_entry *rle; 103238106Sdes const char *sep; 104238106Sdes u_long size, start; 105238106Sdes int error; 106238106Sdes 107238106Sdes sc = device_get_softc(dev); 108238106Sdes 109238106Sdes /* 110238106Sdes * Re-allocate. We expect that the softc contains the information 111238106Sdes * collected by quicc_bfe_probe() intact. 112238106Sdes */ 113238106Sdes sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid, 114238106Sdes 0, ~0, 0, RF_ACTIVE); 115238106Sdes if (sc->sc_rres == NULL) 116238106Sdes return (ENXIO); 117238106Sdes 118238106Sdes start = rman_get_start(sc->sc_rres); 119238106Sdes size = rman_get_size(sc->sc_rres); 120238106Sdes 121238106Sdes sc->sc_rman.rm_start = start; 122238106Sdes sc->sc_rman.rm_end = start + size - 1; 123238106Sdes sc->sc_rman.rm_type = RMAN_ARRAY; 124238106Sdes sc->sc_rman.rm_descr = "QUICC resources"; 125238106Sdes error = rman_init(&sc->sc_rman); 126238106Sdes if (!error) 127238106Sdes error = rman_manage_region(&sc->sc_rman, start, 128238106Sdes start + size - 1); 129238106Sdes if (error) { 130238106Sdes bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, 131238106Sdes sc->sc_rres); 132238106Sdes return (error); 133238106Sdes } 134238106Sdes 135238106Sdes /* 136238106Sdes * Allocate interrupt resource. 137238106Sdes */ 138238106Sdes sc->sc_irid = 0; 139238106Sdes sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, 140238106Sdes RF_ACTIVE | RF_SHAREABLE); 141238106Sdes 142238106Sdes if (sc->sc_ires != NULL) { 143238106Sdes error = bus_setup_intr(dev, sc->sc_ires, 144238106Sdes INTR_TYPE_TTY, quicc_bfe_intr, NULL, sc, &sc->sc_icookie); 145238106Sdes if (error) { 146238106Sdes error = bus_setup_intr(dev, sc->sc_ires, 147238106Sdes INTR_TYPE_TTY | INTR_MPSAFE, NULL, 148238106Sdes (driver_intr_t *)quicc_bfe_intr, sc, 149238106Sdes &sc->sc_icookie); 150238106Sdes } else 151238106Sdes sc->sc_fastintr = 1; 152238106Sdes if (error) { 153238106Sdes device_printf(dev, "could not activate interrupt\n"); 154238106Sdes bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, 155238106Sdes sc->sc_ires); 156238106Sdes sc->sc_ires = NULL; 157238106Sdes } 158238106Sdes } 159238106Sdes 160238106Sdes if (sc->sc_ires == NULL) 161238106Sdes sc->sc_polled = 1; 162238106Sdes 163238106Sdes if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { 164238106Sdes sep = ""; 165238106Sdes device_print_prettyname(dev); 166238106Sdes if (sc->sc_fastintr) { 167238106Sdes printf("%sfast interrupt", sep); 168238106Sdes sep = ", "; 169238106Sdes } 170238106Sdes if (sc->sc_polled) { 171238106Sdes printf("%spolled mode", sep); 172238106Sdes sep = ", "; 173238106Sdes } 174238106Sdes printf("\n"); 175238106Sdes } 176238106Sdes 177238106Sdes sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC, 178238106Sdes M_WAITOK | M_ZERO); 179238106Sdes 180238106Sdes qd->qd_devtype = QUICC_DEVTYPE_SCC; 181238106Sdes qd->qd_rman = &sc->sc_rman; 182238106Sdes resource_list_init(&qd->qd_rlist); 183238106Sdes 184238106Sdes resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start, 185238106Sdes start + size - 1, size); 186238106Sdes 187238106Sdes resource_list_add(&qd->qd_rlist, SYS_RES_IRQ, 0, 0xf00, 0xf00, 1); 188238106Sdes rle = resource_list_find(&qd->qd_rlist, SYS_RES_IRQ, 0); 189238106Sdes rle->res = sc->sc_ires; 190238106Sdes 191238106Sdes qd->qd_dev = device_add_child(dev, NULL, -1); 192238106Sdes device_set_ivars(qd->qd_dev, (void *)qd); 193238106Sdes error = device_probe_and_attach(qd->qd_dev); 194238106Sdes 195238106Sdes /* Enable all SCC interrupts. */ 196238106Sdes quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000); 197238106Sdes 198238106Sdes /* Clear all pending interrupts. */ 199238106Sdes quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_H, ~0); 200238106Sdes quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_L, ~0); 201238106Sdes return (error); 202238106Sdes} 203238106Sdes 204238106Sdesint 205238106Sdesquicc_bfe_detach(device_t dev) 206238106Sdes{ 207238106Sdes struct quicc_softc *sc; 208238106Sdes 209238106Sdes sc = device_get_softc(dev); 210238106Sdes 211238106Sdes bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); 212238106Sdes bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); 213238106Sdes bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); 214238106Sdes return (0); 215238106Sdes} 216238106Sdes 217238106Sdesint 218238106Sdesquicc_bfe_probe(device_t dev, u_int clock) 219238106Sdes{ 220238106Sdes struct quicc_softc *sc; 221238106Sdes uint16_t rev; 222238106Sdes 223238106Sdes sc = device_get_softc(dev); 224238106Sdes sc->sc_dev = dev; 225238106Sdes if (device_get_desc(dev) == NULL) 226238106Sdes device_set_desc(dev, 227238106Sdes "Quad integrated communications controller"); 228238106Sdes 229238106Sdes sc->sc_rrid = 0; 230238106Sdes sc->sc_rtype = SYS_RES_MEMORY; 231238106Sdes sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid, 232238106Sdes 0, ~0, 0, RF_ACTIVE); 233238106Sdes if (sc->sc_rres == NULL) { 234238106Sdes sc->sc_rrid = 0; 235238106Sdes sc->sc_rtype = SYS_RES_IOPORT; 236238106Sdes sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, 237238106Sdes &sc->sc_rrid, 0, ~0, 0, RF_ACTIVE); 238238106Sdes if (sc->sc_rres == NULL) 239238106Sdes return (ENXIO); 240238106Sdes } 241238106Sdes 242238106Sdes sc->sc_clock = clock; 243238106Sdes 244238106Sdes /* 245238106Sdes * Check that the microcode revision is 0x00e8, as documented 246238106Sdes * in the MPC8555E PowerQUICC III Integrated Processor Family 247238106Sdes * Reference Manual. 248238106Sdes */ 249238106Sdes rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM); 250238106Sdes 251238106Sdes bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); 252238106Sdes return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO); 253238106Sdes} 254238106Sdes 255238106Sdesstruct resource * 256238106Sdesquicc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid, 257238106Sdes u_long start, u_long end, u_long count, u_int flags) 258238106Sdes{ 259238106Sdes struct quicc_device *qd; 260238106Sdes struct resource_list_entry *rle; 261238106Sdes 262238106Sdes if (device_get_parent(child) != dev) 263238106Sdes return (NULL); 264238106Sdes 265238106Sdes /* We only support default allocations. */ 266238106Sdes if (start != 0UL || end != ~0UL) 267238106Sdes return (NULL); 268238106Sdes 269238106Sdes qd = device_get_ivars(child); 270238106Sdes rle = resource_list_find(&qd->qd_rlist, type, *rid); 271238106Sdes if (rle == NULL) 272238106Sdes return (NULL); 273238106Sdes 274238106Sdes if (rle->res == NULL) { 275238106Sdes rle->res = rman_reserve_resource(qd->qd_rman, rle->start, 276238106Sdes rle->start + rle->count - 1, rle->count, flags, child); 277238106Sdes if (rle->res != NULL) { 278238106Sdes rman_set_bustag(rle->res, &bs_be_tag); 279238106Sdes rman_set_bushandle(rle->res, rle->start); 280238106Sdes } 281238106Sdes } 282238106Sdes return (rle->res); 283238106Sdes} 284238106Sdes 285238106Sdesint 286238106Sdesquicc_bus_get_resource(device_t dev, device_t child, int type, int rid, 287238106Sdes u_long *startp, u_long *countp) 288238106Sdes{ 289238106Sdes struct quicc_device *qd; 290238106Sdes struct resource_list_entry *rle; 291238106Sdes 292238106Sdes if (device_get_parent(child) != dev) 293238106Sdes return (EINVAL); 294238106Sdes 295238106Sdes qd = device_get_ivars(child); 296238106Sdes rle = resource_list_find(&qd->qd_rlist, type, rid); 297238106Sdes if (rle == NULL) 298238106Sdes return (EINVAL); 299238106Sdes 300238106Sdes if (startp != NULL) 301238106Sdes *startp = rle->start; 302238106Sdes if (countp != NULL) 303238106Sdes *countp = rle->count; 304238106Sdes return (0); 305238106Sdes} 306238106Sdes 307238106Sdesint 308238106Sdesquicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 309238106Sdes{ 310238106Sdes struct quicc_device *qd; 311238106Sdes struct quicc_softc *sc; 312238106Sdes uint32_t sccr; 313238106Sdes 314238106Sdes if (device_get_parent(child) != dev) 315238106Sdes return (EINVAL); 316238106Sdes 317238106Sdes sc = device_get_softc(dev); 318238106Sdes qd = device_get_ivars(child); 319238106Sdes 320238106Sdes switch (index) { 321238106Sdes case QUICC_IVAR_CLOCK: 322238106Sdes *result = sc->sc_clock; 323238106Sdes break; 324238106Sdes case QUICC_IVAR_BRGCLK: 325238106Sdes sccr = quicc_read4(sc->sc_rres, QUICC_REG_SCCR) & 3; 326238106Sdes *result = sc->sc_clock / ((1 << (sccr + 1)) << sccr); 327238106Sdes break; 328238106Sdes case QUICC_IVAR_DEVTYPE: 329238106Sdes *result = qd->qd_devtype; 330238106Sdes break; 331238106Sdes default: 332238106Sdes return (EINVAL); 333238106Sdes } 334238106Sdes return (0); 335238106Sdes} 336238106Sdes 337238106Sdesint 338238106Sdesquicc_bus_release_resource(device_t dev, device_t child, int type, int rid, 339238106Sdes struct resource *res) 340238106Sdes{ 341238106Sdes struct quicc_device *qd; 342238106Sdes struct resource_list_entry *rle; 343238106Sdes 344238106Sdes if (device_get_parent(child) != dev) 345238106Sdes return (EINVAL); 346238106Sdes 347238106Sdes qd = device_get_ivars(child); 348238106Sdes rle = resource_list_find(&qd->qd_rlist, type, rid); 349238106Sdes return ((rle == NULL) ? EINVAL : 0); 350238106Sdes} 351238106Sdes 352238106Sdesint 353238106Sdesquicc_bus_setup_intr(device_t dev, device_t child, struct resource *r, 354238106Sdes int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg, 355238106Sdes void **cookiep) 356238106Sdes{ 357238106Sdes struct quicc_device *qd; 358238106Sdes struct quicc_softc *sc; 359238106Sdes 360238106Sdes if (device_get_parent(child) != dev) 361238106Sdes return (EINVAL); 362238106Sdes 363238106Sdes /* Interrupt handlers must be FAST or MPSAFE. */ 364238106Sdes if (filt == NULL && !(flags & INTR_MPSAFE)) 365238106Sdes return (EINVAL); 366238106Sdes 367238106Sdes sc = device_get_softc(dev); 368238106Sdes if (sc->sc_polled) 369238106Sdes return (ENXIO); 370238106Sdes 371238106Sdes if (sc->sc_fastintr && filt == NULL) { 372238106Sdes sc->sc_fastintr = 0; 373238106Sdes bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); 374238106Sdes bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, 375238106Sdes NULL, (driver_intr_t *)quicc_bfe_intr, sc, &sc->sc_icookie); 376238106Sdes } 377238106Sdes 378238106Sdes qd = device_get_ivars(child); 379238106Sdes qd->qd_ih = (filt != NULL) ? filt : (driver_filter_t *)ihand; 380238106Sdes qd->qd_ih_arg = arg; 381238106Sdes *cookiep = ihand; 382238106Sdes return (0); 383238106Sdes} 384238106Sdes 385238106Sdesint 386238106Sdesquicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r, 387238106Sdes void *cookie) 388238106Sdes{ 389238106Sdes struct quicc_device *qd; 390238106Sdes 391238106Sdes if (device_get_parent(child) != dev) 392238106Sdes return (EINVAL); 393238106Sdes 394238106Sdes qd = device_get_ivars(child); 395238106Sdes if (qd->qd_ih != cookie) 396238106Sdes return (EINVAL); 397238106Sdes 398238106Sdes qd->qd_ih = NULL; 399238106Sdes qd->qd_ih_arg = NULL; 400238106Sdes return (0); 401238106Sdes} 402238106Sdes