diff options
| author | joda <joda@NetBSD.org> | 2000-04-13 11:17:53 +0000 |
|---|---|---|
| committer | joda <joda@NetBSD.org> | 2000-04-13 11:17:53 +0000 |
| commit | 8fba56826496e6cc263d32dc0cf028cc867cc172 (patch) | |
| tree | f0f4128ef66d2d1f507777d7fe5fd1e508b55290 /sys/dev | |
| parent | a11e71c0f951e218a3fd39b75940b66d02f36975 (diff) | |
cardbus attachment for com devices (thanks to Chris Demetriou and
Darren Reed for constructive criticism and help with testing)
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/cardbus/com_cardbus.c | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/sys/dev/cardbus/com_cardbus.c b/sys/dev/cardbus/com_cardbus.c new file mode 100644 index 00000000000..24e2d5388f7 --- /dev/null +++ b/sys/dev/cardbus/com_cardbus.c @@ -0,0 +1,339 @@ +/* $NetBSD: com_cardbus.c,v 1.1 2000/04/13 11:17:53 joda Exp $ */ + +/* + * Copyright (c) 2000 Johan Danielsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of author nor the names of any contributors may + * be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* This is a driver for CardBus based serial devices. It is less + generic than it could be, but it keeps the complexity down. So far + it assumes that anything that reports itself as a `serial' device + is infact a 16x50 or 8250, which is not necessarily true (in + practice this shouldn't be a problem). It also does not handle + devices in the `multiport serial' or `modem' sub-classes, I've + never seen any of thise, so I don't know what they might look like. + + If the CardBus device only has one BAR (that is not also the CIS + BAR) listed in the CIS, it is assumed to be the one to use. For + devices with more than one BAR, the list of known devies has to be + updated below. */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/device.h> + +#include <dev/cardbus/cardbusvar.h> +#include <dev/cardbus/cardbusdevs.h> + +#include <dev/ic/comreg.h> +#include <dev/ic/comvar.h> + +struct com_cardbus_softc { + struct com_softc cc_com; + void *cc_ih; + cardbus_devfunc_t cc_ct; + bus_addr_t cc_addr; + cardbusreg_t cc_base; + bus_size_t cc_size; + cardbusreg_t cc_csr; + int cc_cben; + cardbustag_t cc_tag; + cardbusreg_t cc_reg; + int cc_type; +}; + +#define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname) + +static int com_cardbus_match (struct device*, struct cfdata*, void*); +static void com_cardbus_attach (struct device*, struct device*, void*); +static int com_cardbus_detach (struct device*, int); + +static void com_cardbus_setup(struct com_cardbus_softc*); +static int com_cardbus_enable (struct com_softc*); +static void com_cardbus_disable (struct com_softc*); + +struct cfattach com_cardbus_ca = { + sizeof(struct com_cardbus_softc), com_cardbus_match, + com_cardbus_attach, com_cardbus_detach, com_activate +}; + +static struct csdev { + int vendor; + int product; + cardbusreg_t reg; + int type; +} csdevs[] = { + /* example entry */ + { CARDBUS_VENDOR_XIRCOM, CARDBUS_PRODUCT_XIRCOM_MODEM56, + CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO } +}; + +static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]); + +static struct csdev* +find_csdev(struct cardbus_attach_args *ca) +{ + struct csdev *cp; + + for(cp = csdevs; cp < csdevs + ncsdevs; cp++) + if(cp->vendor == CARDBUS_VENDOR(ca->ca_id) && + cp->product == CARDBUS_PRODUCT(ca->ca_id)) + return cp; + return NULL; +} + +static int +com_cardbus_match(struct device *parent, struct cfdata *match, void *aux) +{ + struct cardbus_attach_args *ca = aux; + + if(CARDBUS_CLASS(ca->ca_class) == CARDBUS_CLASS_COMMUNICATIONS && + CARDBUS_SUBCLASS(ca->ca_class) == CARDBUS_SUBCLASS_COMMUNICATIONS_SERIAL) { + if(find_csdev(ca) != NULL) + return 10; + return 1; + } + return 0; +} + +static int +gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc) +{ + int i, index = -1; + cardbusreg_t cis_ptr; + struct csdev *cp; + + /* If this device is listed above, use the known values, */ + cp = find_csdev(ca); + if(cp != NULL) { + csc->cc_reg = cp->reg; + csc->cc_type = cp->type; + return 0; + } + + cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG); + + /* otherwise try to deduce which BAR and type to use from CIS. If + there is only one BAR, it must be the one we should use, if + there are more, we're out of luck. */ + for(i = 0; i < 7; i++) { + /* ignore zero sized BARs */ + if(ca->ca_cis.bar[i].size == 0) + continue; + /* ignore the CIS BAR */ + if(CARDBUS_CIS_ASI_BAR(cis_ptr) == + CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags)) + continue; + if(index != -1) + goto multi_bar; + index = i; + } + if(index == -1) { + printf(": couldn't find any base address tuple\n"); + return 1; + } + csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags); + if ((ca->ca_cis.bar[index].flags & 0x10) == 0) + csc->cc_type = CARDBUS_MAPREG_TYPE_MEM; + else + csc->cc_type = CARDBUS_MAPREG_TYPE_IO; + return 0; + + multi_bar: + printf(": there are more than one possible base\n"); + + printf("%s: address for this device, " + "please report the following information\n", + DEVNAME(csc)); + printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc), + CARDBUS_VENDOR(ca->ca_id), CARDBUS_PRODUCT(ca->ca_id)); + for(i = 0; i < 7; i++) { + /* ignore zero sized BARs */ + if(ca->ca_cis.bar[i].size == 0) + continue; + /* ignore the CIS BAR */ + if(CARDBUS_CIS_ASI_BAR(cis_ptr) == + CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags)) + continue; + printf("%s: base address %x type %s size %x\n", + DEVNAME(csc), + CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags), + (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem", + ca->ca_cis.bar[i].size); + } + return 1; +} + +static void +com_cardbus_attach (struct device *parent, struct device *self, void *aux) +{ + struct com_softc *sc = (struct com_softc*)self; + struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self; + struct cardbus_attach_args *ca = aux; + + csc->cc_ct = ca->ca_ct; + csc->cc_tag = Cardbus_make_tag(csc->cc_ct); + + if(gofigure(ca, csc) != 0) + return; + + if(Cardbus_mapreg_map(ca->ca_ct, + csc->cc_reg, + csc->cc_type, + 0, + &sc->sc_iot, + &sc->sc_ioh, + &csc->cc_addr, + &csc->cc_size) != 0) { + printf("failed to map memory"); + return; + } + + csc->cc_base = csc->cc_addr; + csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE; + if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) { + csc->cc_base |= CARDBUS_MAPREG_TYPE_IO; + csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE; + csc->cc_cben = CARDBUS_IO_ENABLE; + } else { + csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE; + csc->cc_cben = CARDBUS_MEM_ENABLE; + } + sc->sc_iobase = csc->cc_addr; + + sc->sc_frequency = COM_FREQ; + + sc->enable = com_cardbus_enable; + sc->disable = com_cardbus_disable; + sc->enabled = 0; + + if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) { + printf(": %s %s\n", ca->ca_cis.cis1_info[0], + ca->ca_cis.cis1_info[1]); + printf("%s", DEVNAME(csc)); + } + + com_cardbus_setup(csc); + + com_attach_subr(sc); +} + +static void +com_cardbus_setup(struct com_cardbus_softc *csc) +{ + cardbus_devfunc_t ct = csc->cc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + cardbusreg_t reg; + + Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base); + + /* enable accesses on cardbus bridge */ + (*cf->cardbus_ctrl)(cc, csc->cc_cben); + (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE); + + /* and the card itself */ + reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG); + reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE); + reg |= csc->cc_csr; + Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg); + + /* + * Make sure the latency timer is set to some reasonable + * value. + */ + reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG); + if (CARDBUS_LATTIMER(reg) < 0x20) { + reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT); + reg |= (0x20 << CARDBUS_LATTIMER_SHIFT); + cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg); + } +} + +static int +com_cardbus_enable(struct com_softc *sc) +{ + struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc; + struct cardbus_softc *psc = + (struct cardbus_softc *)sc->sc_dev.dv_parent; + cardbus_chipset_tag_t cc = psc->sc_cc; + cardbus_function_tag_t cf = psc->sc_cf; + + Cardbus_function_enable(csc->cc_ct); + + com_cardbus_setup(csc); + + /* establish the interrupt. */ + csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline, + IPL_SERIAL, comintr, sc); + if (csc->cc_ih == NULL) { + printf("%s: couldn't establish interrupt\n", + DEVNAME(csc)); + return 1; + } + + printf("%s: interrupting at irq %d\n", + DEVNAME(csc), psc->sc_intrline); + + return 0; +} + +static void +com_cardbus_disable(struct com_softc *sc) +{ + struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc; + struct cardbus_softc *psc = + (struct cardbus_softc *)sc->sc_dev.dv_parent; + cardbus_chipset_tag_t cc = psc->sc_cc; + cardbus_function_tag_t cf = psc->sc_cf; + + cardbus_intr_disestablish(cc, cf, csc->cc_ih); + Cardbus_function_disable(csc->cc_ct); +} + +static int +com_cardbus_detach(struct device *self, int flags) +{ + struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self; + struct com_softc *sc = (struct com_softc *) self; + struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent; + int error; + + if ((error = com_detach(self, flags)) != 0) + return error; + + cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih); + + Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh, + csc->cc_size); + + return 0; +} |
