Home | History | Annotate | Line # | Download | only in cardbus
com_cardbus.c revision 1.1
      1 /* $NetBSD: com_cardbus.c,v 1.1 2000/04/13 11:17:53 joda Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2000 Johan Danielsson
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  *
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  *
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * 3. Neither the name of author nor the names of any contributors may
     19  *    be used to endorse or promote products derived from this
     20  *    software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
     26  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 
     35 /* This is a driver for CardBus based serial devices. It is less
     36    generic than it could be, but it keeps the complexity down. So far
     37    it assumes that anything that reports itself as a `serial' device
     38    is infact a 16x50 or 8250, which is not necessarily true (in
     39    practice this shouldn't be a problem). It also does not handle
     40    devices in the `multiport serial' or `modem' sub-classes, I've
     41    never seen any of thise, so I don't know what they might look like.
     42 
     43    If the CardBus device only has one BAR (that is not also the CIS
     44    BAR) listed in the CIS, it is assumed to be the one to use. For
     45    devices with more than one BAR, the list of known devies has to be
     46    updated below.  */
     47 
     48 #include <sys/param.h>
     49 #include <sys/systm.h>
     50 #include <sys/tty.h>
     51 #include <sys/device.h>
     52 
     53 #include <dev/cardbus/cardbusvar.h>
     54 #include <dev/cardbus/cardbusdevs.h>
     55 
     56 #include <dev/ic/comreg.h>
     57 #include <dev/ic/comvar.h>
     58 
     59 struct com_cardbus_softc {
     60 	struct com_softc	cc_com;
     61 	void			*cc_ih;
     62 	cardbus_devfunc_t	cc_ct;
     63 	bus_addr_t		cc_addr;
     64 	cardbusreg_t		cc_base;
     65 	bus_size_t		cc_size;
     66 	cardbusreg_t		cc_csr;
     67 	int			cc_cben;
     68 	cardbustag_t		cc_tag;
     69 	cardbusreg_t		cc_reg;
     70 	int			cc_type;
     71 };
     72 
     73 #define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname)
     74 
     75 static int com_cardbus_match (struct device*, struct cfdata*, void*);
     76 static void com_cardbus_attach (struct device*, struct device*, void*);
     77 static int com_cardbus_detach (struct device*, int);
     78 
     79 static void com_cardbus_setup(struct com_cardbus_softc*);
     80 static int com_cardbus_enable (struct com_softc*);
     81 static void com_cardbus_disable (struct com_softc*);
     82 
     83 struct cfattach com_cardbus_ca = {
     84 	sizeof(struct com_cardbus_softc), com_cardbus_match,
     85 	com_cardbus_attach, com_cardbus_detach, com_activate
     86 };
     87 
     88 static struct csdev {
     89 	int		vendor;
     90 	int		product;
     91 	cardbusreg_t	reg;
     92 	int		type;
     93 } csdevs[] = {
     94 	/* example entry */
     95 	{ CARDBUS_VENDOR_XIRCOM, CARDBUS_PRODUCT_XIRCOM_MODEM56,
     96 	  CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO }
     97 };
     98 
     99 static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
    100 
    101 static struct csdev*
    102 find_csdev(struct cardbus_attach_args *ca)
    103 {
    104 	struct csdev *cp;
    105 
    106 	for(cp = csdevs; cp < csdevs + ncsdevs; cp++)
    107 		if(cp->vendor == CARDBUS_VENDOR(ca->ca_id) &&
    108 		   cp->product == CARDBUS_PRODUCT(ca->ca_id))
    109 			return cp;
    110 	return NULL;
    111 }
    112 
    113 static int
    114 com_cardbus_match(struct device *parent, struct cfdata *match, void *aux)
    115 {
    116 	struct cardbus_attach_args *ca = aux;
    117 
    118 	if(CARDBUS_CLASS(ca->ca_class) == CARDBUS_CLASS_COMMUNICATIONS &&
    119 	   CARDBUS_SUBCLASS(ca->ca_class) == CARDBUS_SUBCLASS_COMMUNICATIONS_SERIAL) {
    120 		if(find_csdev(ca) != NULL)
    121 			return 10;
    122 		return 1;
    123 	}
    124 	return 0;
    125 }
    126 
    127 static int
    128 gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc)
    129 {
    130 	int i, index = -1;
    131 	cardbusreg_t cis_ptr;
    132 	struct csdev *cp;
    133 
    134 	/* If this device is listed above, use the known values, */
    135 	cp = find_csdev(ca);
    136 	if(cp != NULL) {
    137 		csc->cc_reg = cp->reg;
    138 		csc->cc_type = cp->type;
    139 		return 0;
    140 	}
    141 
    142 	cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG);
    143 
    144 	/* otherwise try to deduce which BAR and type to use from CIS.  If
    145 	   there is only one BAR, it must be the one we should use, if
    146 	   there are more, we're out of luck.  */
    147 	for(i = 0; i < 7; i++) {
    148 		/* ignore zero sized BARs */
    149 		if(ca->ca_cis.bar[i].size == 0)
    150 			continue;
    151 		/* ignore the CIS BAR */
    152 		if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
    153 		   CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
    154 			continue;
    155 		if(index != -1)
    156 			goto multi_bar;
    157 		index = i;
    158 	}
    159 	if(index == -1) {
    160 		printf(": couldn't find any base address tuple\n");
    161 		return 1;
    162 	}
    163 	csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
    164 	if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
    165 		csc->cc_type = CARDBUS_MAPREG_TYPE_MEM;
    166 	else
    167 		csc->cc_type = CARDBUS_MAPREG_TYPE_IO;
    168 	return 0;
    169 
    170   multi_bar:
    171 	printf(": there are more than one possible base\n");
    172 
    173 	printf("%s: address for this device, "
    174 	       "please report the following information\n",
    175 	       DEVNAME(csc));
    176 	printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
    177 	       CARDBUS_VENDOR(ca->ca_id), CARDBUS_PRODUCT(ca->ca_id));
    178 	for(i = 0; i < 7; i++) {
    179 		/* ignore zero sized BARs */
    180 		if(ca->ca_cis.bar[i].size == 0)
    181 			continue;
    182 		/* ignore the CIS BAR */
    183 		if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
    184 		   CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
    185 			continue;
    186 		printf("%s: base address %x type %s size %x\n",
    187 		       DEVNAME(csc),
    188 		       CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
    189 		       (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
    190 		       ca->ca_cis.bar[i].size);
    191 	}
    192 	return 1;
    193 }
    194 
    195 static void
    196 com_cardbus_attach (struct device *parent, struct device *self, void *aux)
    197 {
    198 	struct com_softc *sc = (struct com_softc*)self;
    199 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
    200 	struct cardbus_attach_args *ca = aux;
    201 
    202 	csc->cc_ct = ca->ca_ct;
    203 	csc->cc_tag = Cardbus_make_tag(csc->cc_ct);
    204 
    205 	if(gofigure(ca, csc) != 0)
    206 		return;
    207 
    208 	if(Cardbus_mapreg_map(ca->ca_ct,
    209 			      csc->cc_reg,
    210 			      csc->cc_type,
    211 			      0,
    212 			      &sc->sc_iot,
    213 			      &sc->sc_ioh,
    214 			      &csc->cc_addr,
    215 			      &csc->cc_size) != 0) {
    216 		printf("failed to map memory");
    217 		return;
    218 	}
    219 
    220 	csc->cc_base = csc->cc_addr;
    221 	csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
    222 	if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) {
    223 		csc->cc_base |= CARDBUS_MAPREG_TYPE_IO;
    224 		csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE;
    225 		csc->cc_cben = CARDBUS_IO_ENABLE;
    226 	} else {
    227 		csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
    228 		csc->cc_cben = CARDBUS_MEM_ENABLE;
    229 	}
    230 	sc->sc_iobase = csc->cc_addr;
    231 
    232 	sc->sc_frequency = COM_FREQ;
    233 
    234 	sc->enable = com_cardbus_enable;
    235 	sc->disable = com_cardbus_disable;
    236 	sc->enabled = 0;
    237 
    238 	if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
    239 		printf(": %s %s\n", ca->ca_cis.cis1_info[0],
    240 		    ca->ca_cis.cis1_info[1]);
    241 		printf("%s", DEVNAME(csc));
    242 	}
    243 
    244 	com_cardbus_setup(csc);
    245 
    246 	com_attach_subr(sc);
    247 }
    248 
    249 static void
    250 com_cardbus_setup(struct com_cardbus_softc *csc)
    251 {
    252         cardbus_devfunc_t ct = csc->cc_ct;
    253         cardbus_chipset_tag_t cc = ct->ct_cc;
    254         cardbus_function_tag_t cf = ct->ct_cf;
    255 	cardbusreg_t reg;
    256 
    257 	Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
    258 
    259 	/* enable accesses on cardbus bridge */
    260 	(*cf->cardbus_ctrl)(cc, csc->cc_cben);
    261 	(*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
    262 
    263 	/* and the card itself */
    264 	reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
    265 	reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
    266 	reg |= csc->cc_csr;
    267 	Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg);
    268 
    269         /*
    270          * Make sure the latency timer is set to some reasonable
    271          * value.
    272          */
    273         reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
    274         if (CARDBUS_LATTIMER(reg) < 0x20) {
    275                 reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
    276                 reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
    277                 cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg);
    278         }
    279 }
    280 
    281 static int
    282 com_cardbus_enable(struct com_softc *sc)
    283 {
    284 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
    285 	struct cardbus_softc *psc =
    286 		(struct cardbus_softc *)sc->sc_dev.dv_parent;
    287 	cardbus_chipset_tag_t cc = psc->sc_cc;
    288 	cardbus_function_tag_t cf = psc->sc_cf;
    289 
    290 	Cardbus_function_enable(csc->cc_ct);
    291 
    292 	com_cardbus_setup(csc);
    293 
    294 	/* establish the interrupt. */
    295 	csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
    296 					    IPL_SERIAL, comintr, sc);
    297 	if (csc->cc_ih == NULL) {
    298 		printf("%s: couldn't establish interrupt\n",
    299 		       DEVNAME(csc));
    300 		return 1;
    301 	}
    302 
    303 	printf("%s: interrupting at irq %d\n",
    304 	       DEVNAME(csc), psc->sc_intrline);
    305 
    306 	return 0;
    307 }
    308 
    309 static void
    310 com_cardbus_disable(struct com_softc *sc)
    311 {
    312 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
    313 	struct cardbus_softc *psc =
    314 		(struct cardbus_softc *)sc->sc_dev.dv_parent;
    315 	cardbus_chipset_tag_t cc = psc->sc_cc;
    316 	cardbus_function_tag_t cf = psc->sc_cf;
    317 
    318 	cardbus_intr_disestablish(cc, cf, csc->cc_ih);
    319 	Cardbus_function_disable(csc->cc_ct);
    320 }
    321 
    322 static int
    323 com_cardbus_detach(struct device *self, int flags)
    324 {
    325 	struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
    326 	struct com_softc *sc = (struct com_softc *) self;
    327 	struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
    328 	int error;
    329 
    330 	if ((error = com_detach(self, flags)) != 0)
    331 		return error;
    332 
    333 	cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
    334 
    335 	Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
    336 			     csc->cc_size);
    337 
    338 	return 0;
    339 }
    340