int puc_bfe_probe(device_t dev, const struct puc_cfg *cfg) { struct puc_softc *sc; intptr_t res; int error; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_cfg = cfg; /* We don't attach to single-port serial cards. */ if (cfg->ports == PUC_PORT_1S || cfg->ports == PUC_PORT_1P) return (EDOOFUS); error = puc_config(sc, PUC_CFG_GET_NPORTS, 0, &res); if (error) return (error); error = puc_config(sc, PUC_CFG_GET_DESC, 0, &res); if (error) return (error); if (res != 0) device_set_desc(dev, (const char *)res); return (BUS_PROBE_DEFAULT); }
int puc_bfe_attach(device_t dev) { char buffer[64]; struct puc_bar *bar; struct puc_port *port; struct puc_softc *sc; struct rman *rm; intptr_t res; bus_addr_t ofs, start; bus_size_t size; bus_space_handle_t bsh; bus_space_tag_t bst; int error, idx; sc = device_get_softc(dev); for (idx = 0; idx < PUC_PCI_BARS; idx++) sc->sc_bar[idx].b_rid = -1; do { sc->sc_ioport.rm_type = RMAN_ARRAY; error = rman_init(&sc->sc_ioport); if (!error) { sc->sc_iomem.rm_type = RMAN_ARRAY; error = rman_init(&sc->sc_iomem); if (!error) { sc->sc_irq.rm_type = RMAN_ARRAY; error = rman_init(&sc->sc_irq); if (!error) break; rman_fini(&sc->sc_iomem); } rman_fini(&sc->sc_ioport); } return (error); } while (0); snprintf(buffer, sizeof(buffer), "%s I/O port mapping", device_get_nameunit(dev)); sc->sc_ioport.rm_descr = strdup(buffer, M_PUC); snprintf(buffer, sizeof(buffer), "%s I/O memory mapping", device_get_nameunit(dev)); sc->sc_iomem.rm_descr = strdup(buffer, M_PUC); snprintf(buffer, sizeof(buffer), "%s port numbers", device_get_nameunit(dev)); sc->sc_irq.rm_descr = strdup(buffer, M_PUC); error = puc_config(sc, PUC_CFG_GET_NPORTS, 0, &res); KASSERT(error == 0, ("%s %d", __func__, __LINE__)); sc->sc_nports = (int)res; sc->sc_port = malloc(sc->sc_nports * sizeof(struct puc_port), M_PUC, M_WAITOK|M_ZERO); error = rman_manage_region(&sc->sc_irq, 1, sc->sc_nports); if (error) goto fail; error = puc_config(sc, PUC_CFG_SETUP, 0, &res); if (error) goto fail; for (idx = 0; idx < sc->sc_nports; idx++) { port = &sc->sc_port[idx]; port->p_nr = idx + 1; error = puc_config(sc, PUC_CFG_GET_TYPE, idx, &res); if (error) goto fail; port->p_type = res; error = puc_config(sc, PUC_CFG_GET_RID, idx, &res); if (error) goto fail; bar = puc_get_bar(sc, res); if (bar == NULL) { error = ENXIO; goto fail; } port->p_bar = bar; start = rman_get_start(bar->b_res); error = puc_config(sc, PUC_CFG_GET_OFS, idx, &res); if (error) goto fail; ofs = res; error = puc_config(sc, PUC_CFG_GET_LEN, idx, &res); if (error) goto fail; size = res; rm = (bar->b_type == SYS_RES_IOPORT) ? &sc->sc_ioport: &sc->sc_iomem; port->p_rres = rman_reserve_resource(rm, start + ofs, start + ofs + size - 1, size, 0, NULL); if (port->p_rres != NULL) { bsh = rman_get_bushandle(bar->b_res); bst = rman_get_bustag(bar->b_res); bus_space_subregion(bst, bsh, ofs, size, &bsh); rman_set_bushandle(port->p_rres, bsh); rman_set_bustag(port->p_rres, bst); } port->p_ires = rman_reserve_resource(&sc->sc_irq, port->p_nr, port->p_nr, 1, 0, NULL); if (port->p_ires == NULL) { error = ENXIO; goto fail; } error = puc_config(sc, PUC_CFG_GET_CLOCK, idx, &res); if (error) goto fail; port->p_rclk = res; port->p_dev = device_add_child(dev, NULL, -1); if (port->p_dev != NULL) device_set_ivars(port->p_dev, (void *)port); } error = puc_config(sc, PUC_CFG_GET_ILR, 0, &res); if (error) goto fail; sc->sc_ilr = res; if (bootverbose && sc->sc_ilr != 0) device_printf(dev, "using interrupt latch register\n"); sc->sc_irid = 0; sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, RF_ACTIVE|RF_SHAREABLE); if (sc->sc_ires != NULL) { error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY, puc_intr, NULL, sc, &sc->sc_icookie); if (error) error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, NULL, (driver_intr_t *)puc_intr, sc, &sc->sc_icookie); else sc->sc_fastintr = 1; if (error) { device_printf(dev, "could not activate interrupt\n"); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); sc->sc_ires = NULL; } } if (sc->sc_ires == NULL) { /* XXX no interrupt resource. Force polled mode. */ sc->sc_polled = 1; } /* Probe and attach our children. */ for (idx = 0; idx < sc->sc_nports; idx++) { port = &sc->sc_port[idx]; if (port->p_dev == NULL) continue; error = device_probe_and_attach(port->p_dev); if (error) { device_delete_child(dev, port->p_dev); port->p_dev = NULL; } } /* * If there are no serdev devices, then our interrupt handler * will do nothing. Tear it down. */ if (sc->sc_serdevs == 0UL) bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); return (0); fail: for (idx = 0; idx < sc->sc_nports; idx++) { port = &sc->sc_port[idx]; if (port->p_dev != NULL) device_delete_child(dev, port->p_dev); if (port->p_rres != NULL) rman_release_resource(port->p_rres); if (port->p_ires != NULL) rman_release_resource(port->p_ires); } for (idx = 0; idx < PUC_PCI_BARS; idx++) { bar = &sc->sc_bar[idx]; if (bar->b_res != NULL) bus_release_resource(sc->sc_dev, bar->b_type, bar->b_rid, bar->b_res); } rman_fini(&sc->sc_irq); free(__DECONST(void *, sc->sc_irq.rm_descr), M_PUC); rman_fini(&sc->sc_iomem); free(__DECONST(void *, sc->sc_iomem.rm_descr), M_PUC); rman_fini(&sc->sc_ioport); free(__DECONST(void *, sc->sc_ioport.rm_descr), M_PUC); free(sc->sc_port, M_PUC); return (error); }
int puc_config(struct puc_softc *sc, enum puc_cfg_cmd cmd, int port, intptr_t *r) { const struct puc_cfg *cfg = sc->sc_cfg; int error; if (cfg->config_function != NULL) { error = cfg->config_function(sc, cmd, port, r); if (!error) return (0); } else error = EDOOFUS; switch (cmd) { case PUC_CFG_GET_CLOCK: if (cfg->clock < 0) return (error); *r = cfg->clock; return (0); case PUC_CFG_GET_DESC: if (cfg->desc == NULL) return (error); *r = (intptr_t)cfg->desc; return (0); case PUC_CFG_GET_ILR: *r = PUC_ILR_NONE; return (0); case PUC_CFG_GET_LEN: /* The length of bus space needed by the port. */ *r = 8; return (0); case PUC_CFG_GET_NPORTS: /* The number of ports on this card. */ switch (cfg->ports) { case PUC_PORT_NONSTANDARD: return (error); case PUC_PORT_1P: case PUC_PORT_1S: *r = 1; return (0); case PUC_PORT_1S1P: case PUC_PORT_2P: case PUC_PORT_2S: *r = 2; return (0); case PUC_PORT_1S2P: case PUC_PORT_2S1P: case PUC_PORT_3S: *r = 3; return (0); case PUC_PORT_4S: *r = 4; return (0); case PUC_PORT_4S1P: *r = 5; return (0); case PUC_PORT_6S: *r = 6; return (0); case PUC_PORT_8S: *r = 8; return (0); case PUC_PORT_12S: *r = 12; return (0); case PUC_PORT_16S: *r = 16; return (0); } break; case PUC_CFG_GET_OFS: /* The offset relative to the RID. */ if (cfg->d_ofs < 0) return (error); *r = port * cfg->d_ofs; return (0); case PUC_CFG_GET_RID: /* The RID for this port. */ if (port == 0) { if (cfg->rid < 0) return (error); *r = cfg->rid; return (0); } if (cfg->d_rid < 0) return (error); if (cfg->rid < 0) { error = puc_config(sc, PUC_CFG_GET_RID, 0, r); if (error) return (error); } else *r = cfg->rid; *r += port * cfg->d_rid; return (0); case PUC_CFG_GET_TYPE: /* The type of this port. */ if (cfg->ports == PUC_PORT_NONSTANDARD) return (error); switch (port) { case 0: if (cfg->ports == PUC_PORT_1P || cfg->ports == PUC_PORT_2P) *r = PUC_TYPE_PARALLEL; else *r = PUC_TYPE_SERIAL; return (0); case 1: if (cfg->ports == PUC_PORT_1S1P || cfg->ports == PUC_PORT_1S2P || cfg->ports == PUC_PORT_2P) *r = PUC_TYPE_PARALLEL; else *r = PUC_TYPE_SERIAL; return (0); case 2: if (cfg->ports == PUC_PORT_1S2P || cfg->ports == PUC_PORT_2S1P) *r = PUC_TYPE_PARALLEL; else *r = PUC_TYPE_SERIAL; return (0); case 4: if (cfg->ports == PUC_PORT_4S1P) *r = PUC_TYPE_PARALLEL; else *r = PUC_TYPE_SERIAL; return (0); } *r = PUC_TYPE_SERIAL; return (0); case PUC_CFG_SETUP: *r = ENXIO; return (0); } return (ENXIO); }