diff options
author | Aaron Campbell <aaron@cvs.openbsd.org> | 2002-06-08 00:10:55 +0000 |
---|---|---|
committer | Aaron Campbell <aaron@cvs.openbsd.org> | 2002-06-08 00:10:55 +0000 |
commit | 3583e589644febd5d23eb69a7befcbd541f8c412 (patch) | |
tree | 2b3e90d941f54093ba5a452e50b8a5df1fced977 /sys | |
parent | 50be9dfa5c5e8f5b64e0722ce6742b09997f83b9 (diff) |
Add support for RealTek 8129/8139-based CardBus cards; mostly from NetBSD.
deraadt@, jasoni@ ok. Thanks to niklas@ for donating a card for testing.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/cardbus/files.cardbus | 8 | ||||
-rw-r--r-- | sys/dev/cardbus/if_rl_cardbus.c | 353 | ||||
-rw-r--r-- | sys/dev/ic/rtl81x9.c | 81 | ||||
-rw-r--r-- | sys/dev/ic/rtl81x9reg.h | 16 |
4 files changed, 431 insertions, 27 deletions
diff --git a/sys/dev/cardbus/files.cardbus b/sys/dev/cardbus/files.cardbus index 2af27946058..52f6b7e0468 100644 --- a/sys/dev/cardbus/files.cardbus +++ b/sys/dev/cardbus/files.cardbus @@ -1,4 +1,4 @@ -# $OpenBSD: files.cardbus,v 1.4 2000/10/26 20:50:44 aaron Exp $ +# $OpenBSD: files.cardbus,v 1.5 2002/06/08 00:10:54 aaron Exp $ # $NetBSD: files.cardbus,v 1.8 2000/01/26 06:37:24 thorpej Exp $ # # files.cardbus @@ -38,6 +38,12 @@ attach fxp at cardbus with fxp_cardbus file dev/cardbus/if_fxp_cardbus.c fxp_cardbus # +# RealTek 8139 based CardBus cards +# +attach rl at cardbus with rl_cardbus +file dev/cardbus/if_rl_cardbus.c rl_cardbus + +# # #attach com at cardbus with com_cardbus #file dev/cardbus/com_cardbus.c com_cardbus diff --git a/sys/dev/cardbus/if_rl_cardbus.c b/sys/dev/cardbus/if_rl_cardbus.c new file mode 100644 index 00000000000..0c34ab5a617 --- /dev/null +++ b/sys/dev/cardbus/if_rl_cardbus.c @@ -0,0 +1,353 @@ +/* $OpenBSD: if_rl_cardbus.c,v 1.1 2002/06/08 00:10:54 aaron Exp $ */ +/* $NetBSD: if_rl_cardbus.c,v 1.3.8.3 2001/11/14 19:14:02 nathanw Exp $ */ + +/* + * Copyright (c) 2000 Masanori Kanaoka + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * if_rl_cardbus.c: + * Cardbus specific routines for RealTek 8139 ethernet adapter. + * Tested for + * - elecom-Laneed LD-10/100CBA (Accton MPX5030) + * - MELCO LPC3-TX-CB (RealTek 8139) + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_media.h> + +#include <machine/endian.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/mii/miivar.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#include <dev/cardbus/cardbusvar.h> +#include <dev/cardbus/cardbusdevs.h> + +/* + * Default to using PIO access for this driver. On SMP systems, + * there appear to be problems with memory mapped mode: it looks like + * doing too many memory mapped access back to back in rapid succession + * can hang the bus. I'm inclined to blame this on crummy design/construction + * on the part of RealTek. Memory mapped mode does appear to work on + * uniprocessor systems though. + */ +#define RL_USEIOSPACE + +#include <dev/ic/rtl81x9reg.h> + +/* + * Various supported device vendors/types and their names. + */ +static const struct rl_type rl_cardbus_devs[] = { + { CARDBUS_VENDOR_ACCTON, CARDBUS_PRODUCT_ACCTON_MPX5030 }, + { CARDBUS_VENDOR_REALTEK, CARDBUS_PRODUCT_REALTEK_RT8138 }, + { CARDBUS_VENDOR_REALTEK, CARDBUS_PRODUCT_REALTEK_RT8139 }, + { CARDBUS_VENDOR_COREGA, CARDBUS_PRODUCT_COREGA_CB_TXD }, + { 0, 0 } +}; + +struct rl_cardbus_softc { + struct rl_softc sc_rl; /* real rtk softc */ + + /* CardBus-specific goo. */ + void *sc_ih; + cardbus_devfunc_t sc_ct; + cardbustag_t sc_tag; + int sc_csr; + int sc_cben; + int sc_bar_reg; + pcireg_t sc_bar_val; + bus_size_t sc_mapsize; + int sc_intrline; +}; + +static int rl_cardbus_match __P((struct device *, void *, void *)); +static void rl_cardbus_attach __P((struct device *, struct device *, void *)); +static int rl_cardbus_detach __P((struct device *, int)); +void rl_cardbus_setup __P((struct rl_cardbus_softc *)); + +struct cfattach rl_cardbus_ca = { + sizeof(struct rl_cardbus_softc), rl_cardbus_match, rl_cardbus_attach, + rl_cardbus_detach +}; + +const struct rl_type *rl_cardbus_lookup + __P((const struct cardbus_attach_args *)); + +const struct rl_type * +rl_cardbus_lookup(ca) + const struct cardbus_attach_args *ca; +{ + const struct rl_type *t; + + for (t = rl_cardbus_devs; t->rl_vid != 0; t++){ + if (CARDBUS_VENDOR(ca->ca_id) == t->rl_vid && + CARDBUS_PRODUCT(ca->ca_id) == t->rl_did) { + return (t); + } + } + return (NULL); +} + +int +rl_cardbus_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cardbus_attach_args *ca = aux; + + if (rl_cardbus_lookup(ca) != NULL) + return (1); + + return (0); +} + + +void +rl_cardbus_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct rl_cardbus_softc *csc = (struct rl_cardbus_softc *)self; + struct rl_softc *sc = &csc->sc_rl; + struct cardbus_attach_args *ca = aux; + 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_devfunc_t ct = ca->ca_ct; + const struct rl_type *t; + bus_addr_t adr; + + sc->sc_dmat = ca->ca_dmat; + csc->sc_ct = ct; + csc->sc_tag = ca->ca_tag; + csc->sc_intrline = ca->ca_intrline; + + t = rl_cardbus_lookup(ca); + if (t == NULL) { + printf("\n"); + panic("rl_cardbus_attach: impossible"); + } + + /* + * Map control/status registers. + */ + csc->sc_csr = CARDBUS_COMMAND_MASTER_ENABLE; +#ifdef RL_USEIOSPACE + if (Cardbus_mapreg_map(ct, RL_PCI_LOIO, CARDBUS_MAPREG_TYPE_IO, 0, + &sc->rl_btag, &sc->rl_bhandle, &adr, &csc->sc_mapsize) == 0) { +#if rbus +#else + (*ct->ct_cf->cardbus_io_open)(cc, 0, adr, adr+csc->sc_mapsize); +#endif + csc->sc_cben = CARDBUS_IO_ENABLE; + csc->sc_csr |= CARDBUS_COMMAND_IO_ENABLE; + csc->sc_bar_reg = RL_PCI_LOIO; + csc->sc_bar_val = adr | CARDBUS_MAPREG_TYPE_IO; + } +#else + if (Cardbus_mapreg_map(ct, RL_PCI_LOMEM, CARDBUS_MAPREG_TYPE_MEM, 0, + &sc->rl_btag, &sc->rl_bhandle, &adr, &csc->sc_mapsize) == 0) { +#if rbus +#else + (*ct->ct_cf->cardbus_mem_open)(cc, 0, adr, adr+csc->sc_mapsize); +#endif + csc->sc_cben = CARDBUS_MEM_ENABLE; + csc->sc_csr |= CARDBUS_COMMAND_MEM_ENABLE; + csc->sc_bar_reg = RL_PCI_LOMEM; + csc->sc_bar_val = adr | CARDBUS_MAPREG_TYPE_MEM; + } +#endif + else { + printf("%s: unable to map deviceregisters\n", + sc->sc_dev.dv_xname); + return; + } + + Cardbus_function_enable(ct); + + rl_cardbus_setup(csc); + + /* + * Map and establish the interrupt. + */ + csc->sc_ih = cardbus_intr_establish(cc, cf, csc->sc_intrline, IPL_NET, + rl_intr, sc); + if (csc->sc_ih == NULL) { + printf(": couldn't establish interrupt\n"); + Cardbus_function_disable(csc->sc_ct); + return; + } + printf(": irq %d", csc->sc_intrline); + + /* XXX - hardcode this, for now */ + sc->rl_type = RL_8139; + + rl_attach(sc); +} + +int +rl_cardbus_detach(self, flags) + struct device *self; + int flags; +{ + struct rl_cardbus_softc *csc = (void *) self; + struct rl_softc *sc = &csc->sc_rl; + struct cardbus_devfunc *ct = csc->sc_ct; + int rv; + +#ifdef DIAGNOSTIC + if (ct == NULL) + panic("%s: data structure lacks\n", sc->sc_dev.dv_xname); +#endif + rv = rl_detach(sc); + if (rv) + return (rv); + /* + * Unhook the interrut handler. + */ + if (csc->sc_ih != NULL) + cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, csc->sc_ih); + + /* + * Release bus space and close window. + */ + if (csc->sc_bar_reg != 0) + Cardbus_mapreg_unmap(ct, csc->sc_bar_reg, + sc->rl_btag, sc->rl_bhandle, csc->sc_mapsize); + + return (0); +} + +void +rl_cardbus_setup(csc) + struct rl_cardbus_softc *csc; +{ + struct rl_softc *sc = &csc->sc_rl; + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + pcireg_t reg,command; + int pmreg; + + /* + * Handle power management nonsense. + */ + if (cardbus_get_capability(cc, cf, csc->sc_tag, + PCI_CAP_PWRMGMT, &pmreg, 0)) { + command = cardbus_conf_read(cc, cf, csc->sc_tag, pmreg + 4); + if (command & RL_PSTATE_MASK) { + pcireg_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = cardbus_conf_read(cc, cf, csc->sc_tag, + RL_PCI_LOIO); + membase = cardbus_conf_read(cc, cf,csc->sc_tag, + RL_PCI_LOMEM); + irq = cardbus_conf_read(cc, cf,csc->sc_tag, + PCI_PRODUCT_DELTA_8139); + + /* Reset the power state. */ + printf("%s: chip is is in D%d power mode " + "-- setting to D0\n", sc->sc_dev.dv_xname, + command & RL_PSTATE_MASK); + command &= 0xFFFFFFFC; + cardbus_conf_write(cc, cf, csc->sc_tag, + pmreg + 4, command); + + /* Restore PCI config data. */ + cardbus_conf_write(cc, cf, csc->sc_tag, + RL_PCI_LOIO, iobase); + cardbus_conf_write(cc, cf, csc->sc_tag, + RL_PCI_LOMEM, membase); + cardbus_conf_write(cc, cf, csc->sc_tag, + PCI_PRODUCT_DELTA_8139, irq); + } + } + + /* Make sure the right access type is on the CardBus bridge. */ + (*ct->ct_cf->cardbus_ctrl)(cc, csc->sc_cben); + (*ct->ct_cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE); + + /* Program the BAR */ + cardbus_conf_write(cc, cf, csc->sc_tag, + csc->sc_bar_reg, csc->sc_bar_val); + + /* Enable the appropriate bits in the CARDBUS CSR. */ + reg = cardbus_conf_read(cc, cf, csc->sc_tag, + CARDBUS_COMMAND_STATUS_REG); + reg &= ~(CARDBUS_COMMAND_IO_ENABLE|CARDBUS_COMMAND_MEM_ENABLE); + reg |= csc->sc_csr; + cardbus_conf_write(cc, cf, csc->sc_tag, + CARDBUS_COMMAND_STATUS_REG, reg); + + /* + * Make sure the latency timer is set to some reasonable + * value. + */ + reg = cardbus_conf_read(cc, cf, csc->sc_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->sc_tag, CARDBUS_BHLC_REG, reg); + } +} + diff --git a/sys/dev/ic/rtl81x9.c b/sys/dev/ic/rtl81x9.c index 764bc2ff5c2..fbca8235fdb 100644 --- a/sys/dev/ic/rtl81x9.c +++ b/sys/dev/ic/rtl81x9.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtl81x9.c,v 1.15 2002/06/07 18:32:51 art Exp $ */ +/* $OpenBSD: rtl81x9.c,v 1.16 2002/06/08 00:10:54 aaron Exp $ */ /* * Copyright (c) 1997, 1998 @@ -146,9 +146,9 @@ void rl_watchdog(struct ifnet *); int rl_ifmedia_upd(struct ifnet *); void rl_ifmedia_sts(struct ifnet *, struct ifmediareq *); -void rl_eeprom_putbyte(struct rl_softc *, int); -void rl_eeprom_getword(struct rl_softc *, int, u_int16_t *); -void rl_read_eeprom(struct rl_softc *, caddr_t, int, int, int); +void rl_eeprom_getword(struct rl_softc *, int, int, u_int16_t *); +void rl_eeprom_putbyte(struct rl_softc *, int, int); +void rl_read_eeprom(struct rl_softc *, caddr_t, int, int, int, int); void rl_mii_sync(struct rl_softc *); void rl_mii_send(struct rl_softc *, u_int32_t, int); @@ -175,19 +175,19 @@ int rl_list_tx_init(struct rl_softc *); /* * Send a read command and address to the EEPROM, check for ACK. */ -void rl_eeprom_putbyte(sc, addr) +void rl_eeprom_putbyte(sc, addr, addr_len) struct rl_softc *sc; - int addr; + int addr, addr_len; { register int d, i; - d = addr | RL_EECMD_READ; + d = (RL_EECMD_READ << addr_len) | addr; /* * Feed in each bit and strobe the clock. */ - for (i = 0x400; i; i >>= 1) { - if (d & i) + for (i = RL_EECMD_LEN + addr_len; i; i--) { + if (d & (1 << (i - 1))) EE_SET(RL_EE_DATAIN); else EE_CLR(RL_EE_DATAIN); @@ -203,9 +203,9 @@ void rl_eeprom_putbyte(sc, addr) /* * Read a word of data stored in the EEPROM at address 'addr.' */ -void rl_eeprom_getword(sc, addr, dest) +void rl_eeprom_getword(sc, addr, addr_len, dest) struct rl_softc *sc; - int addr; + int addr, addr_len; u_int16_t *dest; { register int i; @@ -217,18 +217,18 @@ void rl_eeprom_getword(sc, addr, dest) /* * Send address of word we want to read. */ - rl_eeprom_putbyte(sc, addr); + rl_eeprom_putbyte(sc, addr, addr_len); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); /* * Start reading bits from EEPROM. */ - for (i = 0x8000; i; i >>= 1) { + for (i = 16; i > 0; i--) { EE_SET(RL_EE_CLK); DELAY(100); if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) - word |= i; + word |= 1 << (i - 1); EE_CLR(RL_EE_CLK); DELAY(100); } @@ -242,10 +242,11 @@ void rl_eeprom_getword(sc, addr, dest) /* * Read a sequence of words from the EEPROM. */ -void rl_read_eeprom(sc, dest, off, cnt, swap) +void rl_read_eeprom(sc, dest, off, addr_len, cnt, swap) struct rl_softc *sc; caddr_t dest; int off; + int addr_len; int cnt; int swap; { @@ -253,7 +254,7 @@ void rl_read_eeprom(sc, dest, off, cnt, swap) u_int16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { - rl_eeprom_getword(sc, off + i, &word); + rl_eeprom_getword(sc, off + i, addr_len, &word); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); @@ -262,7 +263,6 @@ void rl_read_eeprom(sc, dest, off, cnt, swap) } } - /* * MII access routines are provided for the 8129, which * doesn't have a built-in PHY. For the 8139, we fake things @@ -1192,15 +1192,30 @@ rl_attach(sc) bus_dma_segment_t seg; bus_dmamap_t dmamap; int rseg; - u_int16_t rl_did; + u_int16_t rl_id, rl_did; caddr_t kva; + int addr_len; rl_reset(sc); - rl_read_eeprom(sc, (caddr_t)sc->arpcom.ac_enaddr, RL_EE_EADDR, 3, 0); + /* + * Check EEPROM type 9346 or 9356. + */ + rl_read_eeprom(sc, (caddr_t)&rl_id, RL_EE_ID, RL_EEADDR_LEN1, 1, 0); + if (rl_id == 0x8129) + addr_len = RL_EEADDR_LEN1; + else + addr_len = RL_EEADDR_LEN0; + + /* + * Get station address. + */ + rl_read_eeprom(sc, (caddr_t)sc->arpcom.ac_enaddr, RL_EE_EADDR, + addr_len, 3, 0); + printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); - rl_read_eeprom(sc, (caddr_t)&rl_did, RL_EE_PCI_DID, 1, 0); + rl_read_eeprom(sc, (caddr_t)&rl_did, RL_EE_PCI_DID, addr_len, 1, 0); if (rl_did == RT_DEVICEID_8139 || rl_did == ACCTON_DEVICEID_5030 || rl_did == DELTA_DEVICEID_8139 || rl_did == ADDTRON_DEVICEID_8139 @@ -1286,7 +1301,31 @@ rl_attach(sc) if_attach(ifp); ether_ifattach(ifp); - shutdownhook_establish(rl_shutdown, sc); + sc->sc_sdhook = shutdownhook_establish(rl_shutdown, sc); + + return (0); +} + +int +rl_detach(sc) + struct rl_softc *sc; +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + /* Unhook our tick handler. */ + timeout_del(&sc->sc_tick_tmo); + + /* Detach any PHYs we might have. */ + if (LIST_FIRST(&sc->sc_mii.mii_phys) != NULL) + mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); + + /* Delete any remaining media. */ + ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); + + ether_ifdetach(ifp); + if_detach(ifp); + + shutdownhook_disestablish(sc->sc_sdhook); return (0); } diff --git a/sys/dev/ic/rtl81x9reg.h b/sys/dev/ic/rtl81x9reg.h index f980a1022c3..d6907cb8cad 100644 --- a/sys/dev/ic/rtl81x9reg.h +++ b/sys/dev/ic/rtl81x9reg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rtl81x9reg.h,v 1.4 2002/03/14 01:26:55 millert Exp $ */ +/* $OpenBSD: rtl81x9reg.h,v 1.5 2002/06/08 00:10:54 aaron Exp $ */ /* * Copyright (c) 1997, 1998 @@ -238,10 +238,14 @@ #define RL_EEMODE_PROGRAM 0x80 #define RL_EEMODE_WRITECFG (0x80|0x40) -/* 9346 EEPROM commands */ -#define RL_EECMD_WRITE 0x140 -#define RL_EECMD_READ 0x180 -#define RL_EECMD_ERASE 0x1c0 +/* 9346/9356 EEPROM commands */ +#define RL_EECMD_WRITE 0x5 /* 0101b */ +#define RL_EECMD_READ 0x6 /* 0110b */ +#define RL_EECMD_ERASE 0x7 /* 0111b */ +#define RL_EECMD_LEN 4 + +#define RL_EEADDR_LEN0 6 /* 9346 */ +#define RL_EEADDR_LEN1 8 /* 9356 */ #define RL_EE_ID 0x00 #define RL_EE_PCI_VID 0x01 @@ -364,6 +368,7 @@ struct rl_softc { struct arpcom arpcom; /* interface info */ struct mii_data sc_mii; /* MII information */ u_int8_t rl_type; + void *sc_sdhook; /* shutdownhook */ int rl_txthresh; struct rl_chain_data rl_cdata; struct timeout sc_tick_tmo; @@ -479,4 +484,5 @@ struct rl_softc { #endif extern int rl_attach(struct rl_softc *); +extern int rl_detach(struct rl_softc *); extern int rl_intr(void *); |