190731Sjhay/*- 2158124Smarcel * Copyright (c) 2006 Marcel Moolenaar 3158124Smarcel * All rights reserved. 490731Sjhay * 590731Sjhay * Redistribution and use in source and binary forms, with or without 690731Sjhay * modification, are permitted provided that the following conditions 790731Sjhay * are met: 890731Sjhay * 990731Sjhay * 1. Redistributions of source code must retain the above copyright 1090731Sjhay * notice, this list of conditions and the following disclaimer. 1190731Sjhay * 2. Redistributions in binary form must reproduce the above copyright 1290731Sjhay * notice, this list of conditions and the following disclaimer in the 1390731Sjhay * documentation and/or other materials provided with the distribution. 1490731Sjhay * 1590731Sjhay * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1690731Sjhay * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1790731Sjhay * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1890731Sjhay * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1990731Sjhay * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2090731Sjhay * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2190731Sjhay * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2290731Sjhay * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2390731Sjhay * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2490731Sjhay * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2590731Sjhay */ 2690731Sjhay 2790731Sjhay#include <sys/cdefs.h> 2890731Sjhay__FBSDID("$FreeBSD: releng/11.0/sys/dev/puc/puc.c 296298 2016-03-02 03:26:56Z jhibbits $"); 2990731Sjhay 3090731Sjhay#include <sys/param.h> 3190731Sjhay#include <sys/systm.h> 3290731Sjhay#include <sys/kernel.h> 3390731Sjhay#include <sys/bus.h> 3490731Sjhay#include <sys/conf.h> 3590731Sjhay#include <sys/malloc.h> 36158124Smarcel#include <sys/mutex.h> 37263109Srstone#include <sys/sysctl.h> 3890731Sjhay 3990731Sjhay#include <machine/bus.h> 4090731Sjhay#include <machine/resource.h> 4190731Sjhay#include <sys/rman.h> 4290731Sjhay 4390731Sjhay#include <dev/pci/pcireg.h> 4490731Sjhay#include <dev/pci/pcivar.h> 45102714Sphk 46158124Smarcel#include <dev/puc/puc_bus.h> 47158124Smarcel#include <dev/puc/puc_cfg.h> 48160030Sobrien#include <dev/puc/puc_bfe.h> 4990731Sjhay 50158124Smarcel#define PUC_ISRCCNT 5 5190731Sjhay 52158124Smarcelstruct puc_port { 53158124Smarcel struct puc_bar *p_bar; 54158124Smarcel struct resource *p_rres; 55158124Smarcel struct resource *p_ires; 56158124Smarcel device_t p_dev; 57158124Smarcel int p_nr; 58158124Smarcel int p_type; 59158124Smarcel int p_rclk; 6090731Sjhay 61158124Smarcel int p_hasintr:1; 6290731Sjhay 63158124Smarcel serdev_intr_t *p_ihsrc[PUC_ISRCCNT]; 64158124Smarcel void *p_iharg; 65158124Smarcel 66158124Smarcel int p_ipend; 67158124Smarcel}; 68158124Smarcel 69102893Sphkdevclass_t puc_devclass; 70158124Smarcelconst char puc_driver_name[] = "puc"; 71102893Sphk 72227293Sedstatic MALLOC_DEFINE(M_PUC, "PUC", "PUC driver"); 73158124Smarcel 74263109SrstoneSYSCTL_NODE(_hw, OID_AUTO, puc, CTLFLAG_RD, 0, "puc(9) driver configuration"); 75263109Srstone 76158124Smarcelstruct puc_bar * 77158124Smarcelpuc_get_bar(struct puc_softc *sc, int rid) 78102734Sphk{ 79158124Smarcel struct puc_bar *bar; 80158124Smarcel struct rman *rm; 81294883Sjhibbits rman_res_t end, start; 82158124Smarcel int error, i; 83102734Sphk 84158124Smarcel /* Find the BAR entry with the given RID. */ 85158124Smarcel i = 0; 86158124Smarcel while (i < PUC_PCI_BARS && sc->sc_bar[i].b_rid != rid) 87158124Smarcel i++; 88158124Smarcel if (i < PUC_PCI_BARS) 89158124Smarcel return (&sc->sc_bar[i]); 90158124Smarcel 91158124Smarcel /* Not found. If we're looking for an unused entry, return NULL. */ 92158124Smarcel if (rid == -1) 93158124Smarcel return (NULL); 94158124Smarcel 95158124Smarcel /* Get an unused entry for us to fill. */ 96158124Smarcel bar = puc_get_bar(sc, -1); 97158124Smarcel if (bar == NULL) 98158124Smarcel return (NULL); 99158124Smarcel bar->b_rid = rid; 100158124Smarcel bar->b_type = SYS_RES_IOPORT; 101158124Smarcel bar->b_res = bus_alloc_resource_any(sc->sc_dev, bar->b_type, 102158124Smarcel &bar->b_rid, RF_ACTIVE); 103158124Smarcel if (bar->b_res == NULL) { 104158124Smarcel bar->b_rid = rid; 105158124Smarcel bar->b_type = SYS_RES_MEMORY; 106158124Smarcel bar->b_res = bus_alloc_resource_any(sc->sc_dev, bar->b_type, 107158124Smarcel &bar->b_rid, RF_ACTIVE); 108158124Smarcel if (bar->b_res == NULL) { 109158124Smarcel bar->b_rid = -1; 110158124Smarcel return (NULL); 111158124Smarcel } 112102734Sphk } 113158124Smarcel 114158124Smarcel /* Update our managed space. */ 115158124Smarcel rm = (bar->b_type == SYS_RES_IOPORT) ? &sc->sc_ioport : &sc->sc_iomem; 116158124Smarcel start = rman_get_start(bar->b_res); 117158124Smarcel end = rman_get_end(bar->b_res); 118158124Smarcel error = rman_manage_region(rm, start, end); 119158124Smarcel if (error) { 120158124Smarcel bus_release_resource(sc->sc_dev, bar->b_type, bar->b_rid, 121158124Smarcel bar->b_res); 122158124Smarcel bar->b_res = NULL; 123158124Smarcel bar->b_rid = -1; 124158124Smarcel bar = NULL; 125142502Ssam } 126158124Smarcel 127158124Smarcel return (bar); 128102734Sphk} 129102734Sphk 130166901Spisostatic int 131158124Smarcelpuc_intr(void *arg) 132112270Ssobomax{ 133158124Smarcel struct puc_port *port; 134158124Smarcel struct puc_softc *sc = arg; 135200397Smarcel u_long ds, dev, devs; 136200397Smarcel int i, idx, ipend, isrc, nints; 137158124Smarcel uint8_t ilr; 138112270Ssobomax 139200397Smarcel nints = 0; 140200397Smarcel while (1) { 141200397Smarcel /* 142200397Smarcel * Obtain the set of devices with pending interrupts. 143200397Smarcel */ 144200397Smarcel devs = sc->sc_serdevs; 145200397Smarcel if (sc->sc_ilr == PUC_ILR_DIGI) { 146200397Smarcel idx = 0; 147200397Smarcel while (devs & (0xfful << idx)) { 148200397Smarcel ilr = ~bus_read_1(sc->sc_port[idx].p_rres, 7); 149200397Smarcel devs &= ~0ul ^ ((u_long)ilr << idx); 150200397Smarcel idx += 8; 151200397Smarcel } 152200397Smarcel } else if (sc->sc_ilr == PUC_ILR_QUATECH) { 153200397Smarcel /* 154200397Smarcel * Don't trust the value if it's the same as the option 155200397Smarcel * register. It may mean that the ILR is not active and 156200397Smarcel * we're reading the option register instead. This may 157200397Smarcel * lead to false positives on 8-port boards. 158200397Smarcel */ 159200397Smarcel ilr = bus_read_1(sc->sc_port[0].p_rres, 7); 160200397Smarcel if (ilr != (sc->sc_cfg_data & 0xff)) 161200397Smarcel devs &= (u_long)ilr; 162112270Ssobomax } 163200397Smarcel if (devs == 0UL) 164200397Smarcel break; 165200397Smarcel 166158124Smarcel /* 167200397Smarcel * Obtain the set of interrupt sources from those devices 168200397Smarcel * that have pending interrupts. 169158124Smarcel */ 170200397Smarcel ipend = 0; 171158124Smarcel idx = 0, dev = 1UL; 172200397Smarcel ds = devs; 173200397Smarcel while (ds != 0UL) { 174200397Smarcel while ((ds & dev) == 0UL) 175158124Smarcel idx++, dev <<= 1; 176200397Smarcel ds &= ~dev; 177158124Smarcel port = &sc->sc_port[idx]; 178200397Smarcel port->p_ipend = SERDEV_IPEND(port->p_dev); 179200397Smarcel ipend |= port->p_ipend; 180158124Smarcel } 181200397Smarcel if (ipend == 0) 182200397Smarcel break; 183200397Smarcel 184200397Smarcel i = 0, isrc = SER_INT_OVERRUN; 185200397Smarcel while (ipend) { 186200397Smarcel while (i < PUC_ISRCCNT && !(ipend & isrc)) 187200397Smarcel i++, isrc <<= 1; 188200397Smarcel KASSERT(i < PUC_ISRCCNT, ("%s", __func__)); 189200397Smarcel ipend &= ~isrc; 190200397Smarcel idx = 0, dev = 1UL; 191200397Smarcel ds = devs; 192200397Smarcel while (ds != 0UL) { 193200397Smarcel while ((ds & dev) == 0UL) 194200397Smarcel idx++, dev <<= 1; 195200397Smarcel ds &= ~dev; 196200397Smarcel port = &sc->sc_port[idx]; 197200397Smarcel if (!(port->p_ipend & isrc)) 198200397Smarcel continue; 199200397Smarcel if (port->p_ihsrc[i] != NULL) 200200397Smarcel (*port->p_ihsrc[i])(port->p_iharg); 201200397Smarcel nints++; 202200397Smarcel } 203200397Smarcel } 204158124Smarcel } 205200397Smarcel 206200397Smarcel return ((nints > 0) ? FILTER_HANDLED : FILTER_STRAY); 207112270Ssobomax} 208112270Ssobomax 209102714Sphkint 210158124Smarcelpuc_bfe_attach(device_t dev) 21190731Sjhay{ 212158124Smarcel char buffer[64]; 213158124Smarcel struct puc_bar *bar; 214158124Smarcel struct puc_port *port; 21590731Sjhay struct puc_softc *sc; 216158124Smarcel struct rman *rm; 217158124Smarcel intptr_t res; 218158124Smarcel bus_addr_t ofs, start; 219158124Smarcel bus_size_t size; 220158124Smarcel bus_space_handle_t bsh; 221158124Smarcel bus_space_tag_t bst; 222158124Smarcel int error, idx; 22390731Sjhay 224158124Smarcel sc = device_get_softc(dev); 225119814Smarcel 226158124Smarcel for (idx = 0; idx < PUC_PCI_BARS; idx++) 227158124Smarcel sc->sc_bar[idx].b_rid = -1; 22890731Sjhay 229158124Smarcel do { 230158124Smarcel sc->sc_ioport.rm_type = RMAN_ARRAY; 231158124Smarcel error = rman_init(&sc->sc_ioport); 232158124Smarcel if (!error) { 233158124Smarcel sc->sc_iomem.rm_type = RMAN_ARRAY; 234158124Smarcel error = rman_init(&sc->sc_iomem); 235158124Smarcel if (!error) { 236158124Smarcel sc->sc_irq.rm_type = RMAN_ARRAY; 237158124Smarcel error = rman_init(&sc->sc_irq); 238158124Smarcel if (!error) 239158124Smarcel break; 240158124Smarcel rman_fini(&sc->sc_iomem); 241158124Smarcel } 242158124Smarcel rman_fini(&sc->sc_ioport); 243158124Smarcel } 244158124Smarcel return (error); 245158124Smarcel } while (0); 24690731Sjhay 247158124Smarcel snprintf(buffer, sizeof(buffer), "%s I/O port mapping", 248158124Smarcel device_get_nameunit(dev)); 249158124Smarcel sc->sc_ioport.rm_descr = strdup(buffer, M_PUC); 250158124Smarcel snprintf(buffer, sizeof(buffer), "%s I/O memory mapping", 251158124Smarcel device_get_nameunit(dev)); 252158124Smarcel sc->sc_iomem.rm_descr = strdup(buffer, M_PUC); 253158124Smarcel snprintf(buffer, sizeof(buffer), "%s port numbers", 254158124Smarcel device_get_nameunit(dev)); 255158124Smarcel sc->sc_irq.rm_descr = strdup(buffer, M_PUC); 25690731Sjhay 257158124Smarcel error = puc_config(sc, PUC_CFG_GET_NPORTS, 0, &res); 258158124Smarcel KASSERT(error == 0, ("%s %d", __func__, __LINE__)); 259158124Smarcel sc->sc_nports = (int)res; 260158124Smarcel sc->sc_port = malloc(sc->sc_nports * sizeof(struct puc_port), 261158124Smarcel M_PUC, M_WAITOK|M_ZERO); 26290731Sjhay 263158124Smarcel error = rman_manage_region(&sc->sc_irq, 1, sc->sc_nports); 264158124Smarcel if (error) 265158124Smarcel goto fail; 26690731Sjhay 267158124Smarcel error = puc_config(sc, PUC_CFG_SETUP, 0, &res); 268158124Smarcel if (error) 269158124Smarcel goto fail; 270109458Smarcel 271158124Smarcel for (idx = 0; idx < sc->sc_nports; idx++) { 272158124Smarcel port = &sc->sc_port[idx]; 273158124Smarcel port->p_nr = idx + 1; 274158124Smarcel error = puc_config(sc, PUC_CFG_GET_TYPE, idx, &res); 275158124Smarcel if (error) 276158124Smarcel goto fail; 277158124Smarcel port->p_type = res; 278158124Smarcel error = puc_config(sc, PUC_CFG_GET_RID, idx, &res); 279158124Smarcel if (error) 280158124Smarcel goto fail; 281158124Smarcel bar = puc_get_bar(sc, res); 282158124Smarcel if (bar == NULL) { 283158124Smarcel error = ENXIO; 284158124Smarcel goto fail; 285119814Smarcel } 286158124Smarcel port->p_bar = bar; 287158124Smarcel start = rman_get_start(bar->b_res); 288158124Smarcel error = puc_config(sc, PUC_CFG_GET_OFS, idx, &res); 289158124Smarcel if (error) 290158124Smarcel goto fail; 291158124Smarcel ofs = res; 292158124Smarcel error = puc_config(sc, PUC_CFG_GET_LEN, idx, &res); 293158124Smarcel if (error) 294158124Smarcel goto fail; 295158124Smarcel size = res; 296158124Smarcel rm = (bar->b_type == SYS_RES_IOPORT) 297158124Smarcel ? &sc->sc_ioport: &sc->sc_iomem; 298158124Smarcel port->p_rres = rman_reserve_resource(rm, start + ofs, 299158124Smarcel start + ofs + size - 1, size, 0, NULL); 300158124Smarcel if (port->p_rres != NULL) { 301158124Smarcel bsh = rman_get_bushandle(bar->b_res); 302158124Smarcel bst = rman_get_bustag(bar->b_res); 303158124Smarcel bus_space_subregion(bst, bsh, ofs, size, &bsh); 304158124Smarcel rman_set_bushandle(port->p_rres, bsh); 305158124Smarcel rman_set_bustag(port->p_rres, bst); 30690731Sjhay } 307158124Smarcel port->p_ires = rman_reserve_resource(&sc->sc_irq, port->p_nr, 308158124Smarcel port->p_nr, 1, 0, NULL); 309158124Smarcel if (port->p_ires == NULL) { 310158124Smarcel error = ENXIO; 311158124Smarcel goto fail; 312112270Ssobomax } 313158124Smarcel error = puc_config(sc, PUC_CFG_GET_CLOCK, idx, &res); 314158124Smarcel if (error) 315158124Smarcel goto fail; 316158124Smarcel port->p_rclk = res; 31790731Sjhay 318158124Smarcel port->p_dev = device_add_child(dev, NULL, -1); 319158124Smarcel if (port->p_dev != NULL) 320158124Smarcel device_set_ivars(port->p_dev, (void *)port); 321102734Sphk } 32290731Sjhay 323158124Smarcel error = puc_config(sc, PUC_CFG_GET_ILR, 0, &res); 324158124Smarcel if (error) 325158124Smarcel goto fail; 326158124Smarcel sc->sc_ilr = res; 327158124Smarcel if (bootverbose && sc->sc_ilr != 0) 328158124Smarcel device_printf(dev, "using interrupt latch register\n"); 32990731Sjhay 330158124Smarcel sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, 331158124Smarcel RF_ACTIVE|RF_SHAREABLE); 332158124Smarcel if (sc->sc_ires != NULL) { 333158124Smarcel error = bus_setup_intr(dev, sc->sc_ires, 334166901Spiso INTR_TYPE_TTY, puc_intr, NULL, sc, &sc->sc_icookie); 335158124Smarcel if (error) 336158124Smarcel error = bus_setup_intr(dev, sc->sc_ires, 337166901Spiso INTR_TYPE_TTY | INTR_MPSAFE, NULL, 338166901Spiso (driver_intr_t *)puc_intr, sc, &sc->sc_icookie); 339158124Smarcel else 340158124Smarcel sc->sc_fastintr = 1; 34190731Sjhay 342158124Smarcel if (error) { 343158124Smarcel device_printf(dev, "could not activate interrupt\n"); 344158124Smarcel bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, 345158124Smarcel sc->sc_ires); 346158124Smarcel sc->sc_ires = NULL; 34790731Sjhay } 348158124Smarcel } 349158124Smarcel if (sc->sc_ires == NULL) { 350158124Smarcel /* XXX no interrupt resource. Force polled mode. */ 351158124Smarcel sc->sc_polled = 1; 352158124Smarcel } 35390731Sjhay 354158124Smarcel /* Probe and attach our children. */ 355158124Smarcel for (idx = 0; idx < sc->sc_nports; idx++) { 356158124Smarcel port = &sc->sc_port[idx]; 357158124Smarcel if (port->p_dev == NULL) 35890731Sjhay continue; 359158124Smarcel error = device_probe_and_attach(port->p_dev); 360158124Smarcel if (error) { 361158124Smarcel device_delete_child(dev, port->p_dev); 362158124Smarcel port->p_dev = NULL; 36390925Snyan } 36490731Sjhay } 36590731Sjhay 366158124Smarcel /* 367158124Smarcel * If there are no serdev devices, then our interrupt handler 368158124Smarcel * will do nothing. Tear it down. 369158124Smarcel */ 370158124Smarcel if (sc->sc_serdevs == 0UL) 371158124Smarcel bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); 372158124Smarcel 37390731Sjhay return (0); 374158124Smarcel 375158124Smarcelfail: 376158124Smarcel for (idx = 0; idx < sc->sc_nports; idx++) { 377158124Smarcel port = &sc->sc_port[idx]; 378158124Smarcel if (port->p_dev != NULL) 379158124Smarcel device_delete_child(dev, port->p_dev); 380158124Smarcel if (port->p_rres != NULL) 381158124Smarcel rman_release_resource(port->p_rres); 382158124Smarcel if (port->p_ires != NULL) 383158124Smarcel rman_release_resource(port->p_ires); 384158124Smarcel } 385158124Smarcel for (idx = 0; idx < PUC_PCI_BARS; idx++) { 386158124Smarcel bar = &sc->sc_bar[idx]; 387158124Smarcel if (bar->b_res != NULL) 388158124Smarcel bus_release_resource(sc->sc_dev, bar->b_type, 389158124Smarcel bar->b_rid, bar->b_res); 390158124Smarcel } 391158124Smarcel rman_fini(&sc->sc_irq); 392158124Smarcel free(__DECONST(void *, sc->sc_irq.rm_descr), M_PUC); 393158124Smarcel rman_fini(&sc->sc_iomem); 394158124Smarcel free(__DECONST(void *, sc->sc_iomem.rm_descr), M_PUC); 395158124Smarcel rman_fini(&sc->sc_ioport); 396158124Smarcel free(__DECONST(void *, sc->sc_ioport.rm_descr), M_PUC); 397158124Smarcel free(sc->sc_port, M_PUC); 398158124Smarcel return (error); 39990731Sjhay} 40090731Sjhay 401158124Smarcelint 402158124Smarcelpuc_bfe_detach(device_t dev) 403112270Ssobomax{ 404158124Smarcel struct puc_bar *bar; 405158124Smarcel struct puc_port *port; 406158124Smarcel struct puc_softc *sc; 407158124Smarcel int error, idx; 408112270Ssobomax 409158124Smarcel sc = device_get_softc(dev); 410112270Ssobomax 411158124Smarcel /* Detach our children. */ 412158124Smarcel error = 0; 413158124Smarcel for (idx = 0; idx < sc->sc_nports; idx++) { 414158124Smarcel port = &sc->sc_port[idx]; 415158124Smarcel if (port->p_dev == NULL) 416158124Smarcel continue; 417158124Smarcel if (device_detach(port->p_dev) == 0) { 418158124Smarcel device_delete_child(dev, port->p_dev); 419158124Smarcel if (port->p_rres != NULL) 420158124Smarcel rman_release_resource(port->p_rres); 421158124Smarcel if (port->p_ires != NULL) 422158124Smarcel rman_release_resource(port->p_ires); 423158124Smarcel } else 424158124Smarcel error = ENXIO; 425112270Ssobomax } 426158124Smarcel if (error) 427158124Smarcel return (error); 428112270Ssobomax 429158124Smarcel if (sc->sc_serdevs != 0UL) 430158124Smarcel bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); 431158124Smarcel bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); 43290731Sjhay 433158124Smarcel for (idx = 0; idx < PUC_PCI_BARS; idx++) { 434158124Smarcel bar = &sc->sc_bar[idx]; 435158124Smarcel if (bar->b_res != NULL) 436158124Smarcel bus_release_resource(sc->sc_dev, bar->b_type, 437158124Smarcel bar->b_rid, bar->b_res); 438158124Smarcel } 439158124Smarcel 440158124Smarcel rman_fini(&sc->sc_irq); 441158124Smarcel free(__DECONST(void *, sc->sc_irq.rm_descr), M_PUC); 442158124Smarcel rman_fini(&sc->sc_iomem); 443158124Smarcel free(__DECONST(void *, sc->sc_iomem.rm_descr), M_PUC); 444158124Smarcel rman_fini(&sc->sc_ioport); 445158124Smarcel free(__DECONST(void *, sc->sc_ioport.rm_descr), M_PUC); 446158124Smarcel free(sc->sc_port, M_PUC); 447158124Smarcel return (0); 44890731Sjhay} 44990731Sjhay 450158124Smarcelint 451158124Smarcelpuc_bfe_probe(device_t dev, const struct puc_cfg *cfg) 45290731Sjhay{ 453158124Smarcel struct puc_softc *sc; 454158124Smarcel intptr_t res; 455158124Smarcel int error; 45690731Sjhay 457158124Smarcel sc = device_get_softc(dev); 458158124Smarcel sc->sc_dev = dev; 459158124Smarcel sc->sc_cfg = cfg; 46090731Sjhay 461158124Smarcel /* We don't attach to single-port serial cards. */ 462158124Smarcel if (cfg->ports == PUC_PORT_1S || cfg->ports == PUC_PORT_1P) 463158124Smarcel return (EDOOFUS); 464158124Smarcel error = puc_config(sc, PUC_CFG_GET_NPORTS, 0, &res); 465158124Smarcel if (error) 466158124Smarcel return (error); 467158124Smarcel error = puc_config(sc, PUC_CFG_GET_DESC, 0, &res); 468158124Smarcel if (error) 469158124Smarcel return (error); 470158124Smarcel if (res != 0) 471158124Smarcel device_set_desc(dev, (const char *)res); 472158124Smarcel return (BUS_PROBE_DEFAULT); 47390731Sjhay} 47490731Sjhay 475102714Sphkstruct resource * 476158124Smarcelpuc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid, 477294883Sjhibbits rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 47890731Sjhay{ 479158124Smarcel struct puc_port *port; 480158124Smarcel struct resource *res; 481158124Smarcel device_t assigned, originator; 482158124Smarcel int error; 48390731Sjhay 484158124Smarcel /* Get our immediate child. */ 485158124Smarcel originator = child; 486158124Smarcel while (child != NULL && device_get_parent(child) != dev) 487158124Smarcel child = device_get_parent(child); 488158124Smarcel if (child == NULL) 489158124Smarcel return (NULL); 490118292Sambrisko 491158124Smarcel port = device_get_ivars(child); 492158124Smarcel KASSERT(port != NULL, ("%s %d", __func__, __LINE__)); 49390731Sjhay 494158124Smarcel if (rid == NULL || *rid != 0) 495158124Smarcel return (NULL); 496158124Smarcel 497158124Smarcel /* We only support default allocations. */ 498296298Sjhibbits if (!RMAN_IS_DEFAULT_RANGE(start, end)) 499158124Smarcel return (NULL); 500158124Smarcel 501158124Smarcel if (type == port->p_bar->b_type) 502158124Smarcel res = port->p_rres; 503158124Smarcel else if (type == SYS_RES_IRQ) 504158124Smarcel res = port->p_ires; 505118292Sambrisko else 506158124Smarcel return (NULL); 50790731Sjhay 508158124Smarcel if (res == NULL) 509158124Smarcel return (NULL); 510158124Smarcel 511158124Smarcel assigned = rman_get_device(res); 512158124Smarcel if (assigned == NULL) /* Not allocated */ 513158124Smarcel rman_set_device(res, originator); 514158124Smarcel else if (assigned != originator) 515158124Smarcel return (NULL); 516158124Smarcel 517158124Smarcel if (flags & RF_ACTIVE) { 518158124Smarcel error = rman_activate_resource(res); 519158124Smarcel if (error) { 520158124Smarcel if (assigned == NULL) 521158124Smarcel rman_set_device(res, NULL); 522158124Smarcel return (NULL); 523158124Smarcel } 524158124Smarcel } 525158124Smarcel 526158124Smarcel return (res); 52790731Sjhay} 52890731Sjhay 529102714Sphkint 530158124Smarcelpuc_bus_release_resource(device_t dev, device_t child, int type, int rid, 53190731Sjhay struct resource *res) 53290731Sjhay{ 533158124Smarcel struct puc_port *port; 534158124Smarcel device_t originator; 535158124Smarcel 536158124Smarcel /* Get our immediate child. */ 537158124Smarcel originator = child; 538158124Smarcel while (child != NULL && device_get_parent(child) != dev) 539158124Smarcel child = device_get_parent(child); 540158124Smarcel if (child == NULL) 541158124Smarcel return (EINVAL); 542158124Smarcel 543158124Smarcel port = device_get_ivars(child); 544158124Smarcel KASSERT(port != NULL, ("%s %d", __func__, __LINE__)); 545158124Smarcel 546158124Smarcel if (rid != 0 || res == NULL) 547158124Smarcel return (EINVAL); 548158124Smarcel 549158124Smarcel if (type == port->p_bar->b_type) { 550158124Smarcel if (res != port->p_rres) 551158124Smarcel return (EINVAL); 552158124Smarcel } else if (type == SYS_RES_IRQ) { 553158124Smarcel if (res != port->p_ires) 554158124Smarcel return (EINVAL); 555158124Smarcel if (port->p_hasintr) 556158124Smarcel return (EBUSY); 557158124Smarcel } else 558158124Smarcel return (EINVAL); 559158124Smarcel 560158124Smarcel if (rman_get_device(res) != originator) 561158124Smarcel return (ENXIO); 562158124Smarcel if (rman_get_flags(res) & RF_ACTIVE) 563158124Smarcel rman_deactivate_resource(res); 564158124Smarcel rman_set_device(res, NULL); 56590731Sjhay return (0); 56690731Sjhay} 56790731Sjhay 568102714Sphkint 569158124Smarcelpuc_bus_get_resource(device_t dev, device_t child, int type, int rid, 570294883Sjhibbits rman_res_t *startp, rman_res_t *countp) 57190731Sjhay{ 572158124Smarcel struct puc_port *port; 573158124Smarcel struct resource *res; 574294883Sjhibbits rman_res_t start; 57590731Sjhay 576158124Smarcel /* Get our immediate child. */ 577158124Smarcel while (child != NULL && device_get_parent(child) != dev) 578158124Smarcel child = device_get_parent(child); 579158124Smarcel if (child == NULL) 580158124Smarcel return (EINVAL); 58190731Sjhay 582158124Smarcel port = device_get_ivars(child); 583158124Smarcel KASSERT(port != NULL, ("%s %d", __func__, __LINE__)); 584158124Smarcel 585158124Smarcel if (type == port->p_bar->b_type) 586158124Smarcel res = port->p_rres; 587158124Smarcel else if (type == SYS_RES_IRQ) 588158124Smarcel res = port->p_ires; 589158124Smarcel else 590158124Smarcel return (ENXIO); 591158124Smarcel 592158124Smarcel if (rid != 0 || res == NULL) 593158124Smarcel return (ENXIO); 594158124Smarcel 595158124Smarcel start = rman_get_start(res); 596158124Smarcel if (startp != NULL) 597158124Smarcel *startp = start; 598158124Smarcel if (countp != NULL) 599158124Smarcel *countp = rman_get_end(res) - start + 1; 600158124Smarcel return (0); 60190731Sjhay} 60290731Sjhay 603102714Sphkint 604158124Smarcelpuc_bus_setup_intr(device_t dev, device_t child, struct resource *res, 605166901Spiso int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg, void **cookiep) 60690731Sjhay{ 607158124Smarcel struct puc_port *port; 60890731Sjhay struct puc_softc *sc; 609158124Smarcel device_t originator; 610158124Smarcel int i, isrc, serdev; 61190731Sjhay 612158124Smarcel sc = device_get_softc(dev); 613158124Smarcel 614158124Smarcel /* Get our immediate child. */ 615158124Smarcel originator = child; 616158124Smarcel while (child != NULL && device_get_parent(child) != dev) 617158124Smarcel child = device_get_parent(child); 618158124Smarcel if (child == NULL) 619158124Smarcel return (EINVAL); 620158124Smarcel 621158124Smarcel port = device_get_ivars(child); 622158124Smarcel KASSERT(port != NULL, ("%s %d", __func__, __LINE__)); 623158124Smarcel 624170386Spiso if (cookiep == NULL || res != port->p_ires) 625158124Smarcel return (EINVAL); 626170386Spiso /* We demand that serdev devices use filter_only interrupts. */ 627245471Sjhb if (port->p_type == PUC_TYPE_SERIAL && ihand != NULL) 628170386Spiso return (ENXIO); 629158124Smarcel if (rman_get_device(port->p_ires) != originator) 630102895Sphk return (ENXIO); 631158124Smarcel 632158124Smarcel /* 633158124Smarcel * Have non-serdev ports handled by the bus implementation. It 634158124Smarcel * supports multiple handlers for a single interrupt as it is, 635158124Smarcel * so we wouldn't add value if we did it ourselves. 636158124Smarcel */ 637158124Smarcel serdev = 0; 638158124Smarcel if (port->p_type == PUC_TYPE_SERIAL) { 639158124Smarcel i = 0, isrc = SER_INT_OVERRUN; 640158124Smarcel while (i < PUC_ISRCCNT) { 641158124Smarcel port->p_ihsrc[i] = SERDEV_IHAND(originator, isrc); 642158124Smarcel if (port->p_ihsrc[i] != NULL) 643158124Smarcel serdev = 1; 644158124Smarcel i++, isrc <<= 1; 64590731Sjhay } 64690731Sjhay } 647158124Smarcel if (!serdev) 648158124Smarcel return (BUS_SETUP_INTR(device_get_parent(dev), originator, 649166901Spiso sc->sc_ires, flags, filt, ihand, arg, cookiep)); 650158124Smarcel 651158124Smarcel sc->sc_serdevs |= 1UL << (port->p_nr - 1); 652158124Smarcel 653158124Smarcel port->p_hasintr = 1; 654158124Smarcel port->p_iharg = arg; 655158124Smarcel 656158124Smarcel *cookiep = port; 657158124Smarcel return (0); 65890731Sjhay} 65990731Sjhay 660102714Sphkint 661158124Smarcelpuc_bus_teardown_intr(device_t dev, device_t child, struct resource *res, 662158124Smarcel void *cookie) 66390731Sjhay{ 664158124Smarcel struct puc_port *port; 665158124Smarcel struct puc_softc *sc; 666158124Smarcel device_t originator; 66790731Sjhay int i; 66890731Sjhay 669158124Smarcel sc = device_get_softc(dev); 670158124Smarcel 671158124Smarcel /* Get our immediate child. */ 672158124Smarcel originator = child; 673158124Smarcel while (child != NULL && device_get_parent(child) != dev) 674158124Smarcel child = device_get_parent(child); 675158124Smarcel if (child == NULL) 676158124Smarcel return (EINVAL); 677158124Smarcel 678158124Smarcel port = device_get_ivars(child); 679158124Smarcel KASSERT(port != NULL, ("%s %d", __func__, __LINE__)); 680158124Smarcel 681158124Smarcel if (res != port->p_ires) 682158124Smarcel return (EINVAL); 683158124Smarcel if (rman_get_device(port->p_ires) != originator) 684158124Smarcel return (ENXIO); 685158124Smarcel 686158124Smarcel if (!port->p_hasintr) 687158124Smarcel return (BUS_TEARDOWN_INTR(device_get_parent(dev), originator, 688158124Smarcel sc->sc_ires, cookie)); 689158124Smarcel 690158124Smarcel if (cookie != port) 691158124Smarcel return (EINVAL); 692158124Smarcel 693158124Smarcel port->p_hasintr = 0; 694158124Smarcel port->p_iharg = NULL; 695158124Smarcel 696158124Smarcel for (i = 0; i < PUC_ISRCCNT; i++) 697158124Smarcel port->p_ihsrc[i] = NULL; 698158124Smarcel 699158124Smarcel return (0); 70090731Sjhay} 70190731Sjhay 702102714Sphkint 703158124Smarcelpuc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 70490731Sjhay{ 705158124Smarcel struct puc_port *port; 70690731Sjhay 707158124Smarcel /* Get our immediate child. */ 708158124Smarcel while (child != NULL && device_get_parent(child) != dev) 709158124Smarcel child = device_get_parent(child); 710158124Smarcel if (child == NULL) 711158124Smarcel return (EINVAL); 71290731Sjhay 713158124Smarcel port = device_get_ivars(child); 714158124Smarcel KASSERT(port != NULL, ("%s %d", __func__, __LINE__)); 715158124Smarcel 716158124Smarcel if (result == NULL) 717158124Smarcel return (EINVAL); 718158124Smarcel 71990731Sjhay switch(index) { 720158124Smarcel case PUC_IVAR_CLOCK: 721158124Smarcel *result = port->p_rclk; 72290731Sjhay break; 723158124Smarcel case PUC_IVAR_TYPE: 724158124Smarcel *result = port->p_type; 725119814Smarcel break; 72690731Sjhay default: 72790731Sjhay return (ENOENT); 72890731Sjhay } 72990731Sjhay return (0); 73090731Sjhay} 731223091Sjhb 732223091Sjhbint 733223091Sjhbpuc_bus_print_child(device_t dev, device_t child) 734223091Sjhb{ 735223091Sjhb struct puc_port *port; 736223091Sjhb int retval; 737223091Sjhb 738223091Sjhb port = device_get_ivars(child); 739223091Sjhb retval = 0; 740223091Sjhb 741223091Sjhb retval += bus_print_child_header(dev, child); 742223091Sjhb retval += printf(" at port %d", port->p_nr); 743223091Sjhb retval += bus_print_child_footer(dev, child); 744223091Sjhb 745223091Sjhb return (retval); 746223091Sjhb} 747223091Sjhb 748223091Sjhbint 749223091Sjhbpuc_bus_child_location_str(device_t dev, device_t child, char *buf, 750223091Sjhb size_t buflen) 751223091Sjhb{ 752223091Sjhb struct puc_port *port; 753223091Sjhb 754223091Sjhb port = device_get_ivars(child); 755223091Sjhb snprintf(buf, buflen, "port=%d", port->p_nr); 756223091Sjhb return (0); 757223091Sjhb} 758223091Sjhb 759223091Sjhbint 760223091Sjhbpuc_bus_child_pnpinfo_str(device_t dev, device_t child, char *buf, 761223091Sjhb size_t buflen) 762223091Sjhb{ 763223091Sjhb struct puc_port *port; 764223091Sjhb 765223091Sjhb port = device_get_ivars(child); 766223091Sjhb snprintf(buf, buflen, "type=%d", port->p_type); 767223091Sjhb return (0); 768223091Sjhb} 769