cardbus attachment for com devices (thanks to Chris Demetriou and
Darren Reed for constructive criticism and help with testing)
This commit is contained in:
parent
a11e71c0f9
commit
8fba568264
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue