diff options
author | Renato Botelho <renato@netgate.com> | 2015-12-28 07:46:42 -0200 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2015-12-28 07:46:42 -0200 |
commit | 0319542cc712df59c57f014b4f925f516bcb5b4b (patch) | |
tree | d471c26ca5a9ed2a730a1bbb6781f4e08c79a939 /sys/dev | |
parent | 3f9b44b7cebbdee8202dd56d8c57ac81b13114e1 (diff) | |
parent | 70f106975f9e06a5c50c4dfa06fb3b3d41d1780f (diff) | |
download | FreeBSD-src-0319542cc712df59c57f014b4f925f516bcb5b4b.zip FreeBSD-src-0319542cc712df59c57f014b4f925f516bcb5b4b.tar.gz |
Merge remote-tracking branch 'origin/stable/10' into devel
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_channel_mgmt.c | 2 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndis_pci.c | 3 | ||||
-rw-r--r-- | sys/dev/isp/isp.c | 69 | ||||
-rw-r--r-- | sys/dev/isp/isp_freebsd.c | 95 | ||||
-rw-r--r-- | sys/dev/isp/isp_freebsd.h | 15 | ||||
-rw-r--r-- | sys/dev/isp/isp_library.c | 13 | ||||
-rw-r--r-- | sys/dev/isp/isp_pci.c | 294 | ||||
-rw-r--r-- | sys/dev/isp/isp_sbus.c | 16 | ||||
-rw-r--r-- | sys/dev/isp/ispmbox.h | 17 | ||||
-rw-r--r-- | sys/dev/isp/ispvar.h | 12 | ||||
-rw-r--r-- | sys/dev/md/md.c | 40 | ||||
-rw-r--r-- | sys/dev/mii/rgephy.c | 2 | ||||
-rw-r--r-- | sys/dev/mii/rlphy.c | 2 | ||||
-rw-r--r-- | sys/dev/mii/rlswitch.c | 2 | ||||
-rw-r--r-- | sys/dev/mpt/mpt.c | 11 | ||||
-rw-r--r-- | sys/dev/mpt/mpt.h | 12 | ||||
-rw-r--r-- | sys/dev/qlxgbe/ql_hw.c | 3 | ||||
-rw-r--r-- | sys/dev/qlxgbe/ql_os.c | 2 | ||||
-rw-r--r-- | sys/dev/qlxgbe/ql_ver.h | 2 | ||||
-rw-r--r-- | sys/dev/re/if_re.c | 60 | ||||
-rw-r--r-- | sys/dev/rl/if_rl.c | 2126 | ||||
-rw-r--r-- | sys/dev/rl/if_rlreg.h | 1160 | ||||
-rw-r--r-- | sys/dev/usb/net/if_urndis.c | 178 | ||||
-rw-r--r-- | sys/dev/vt/colors/vt_termcolors.c | 11 |
24 files changed, 3808 insertions, 339 deletions
diff --git a/sys/dev/hyperv/vmbus/hv_channel_mgmt.c b/sys/dev/hyperv/vmbus/hv_channel_mgmt.c index d13ece5..d17d696 100644 --- a/sys/dev/hyperv/vmbus/hv_channel_mgmt.c +++ b/sys/dev/hyperv/vmbus/hv_channel_mgmt.c @@ -53,8 +53,6 @@ static void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr); static void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr); static void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr); static void vmbus_channel_process_offer(void *context); -struct hv_vmbus_channel* - vmbus_select_outgoing_channel(struct hv_vmbus_channel *promary); /** * Channel message dispatch table diff --git a/sys/dev/if_ndis/if_ndis_pci.c b/sys/dev/if_ndis/if_ndis_pci.c index 8ed6c5a..326e3de 100644 --- a/sys/dev/if_ndis/if_ndis_pci.c +++ b/sys/dev/if_ndis/if_ndis_pci.c @@ -295,8 +295,7 @@ ndis_attach_pci(dev) BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ - BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ - NDIS_NSEG_NEW, /* nsegments */ + DFLTPHYS, NDIS_NSEG_NEW,/* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ diff --git a/sys/dev/isp/isp.c b/sys/dev/isp/isp.c index 18c41e9..d394011 100644 --- a/sys/dev/isp/isp.c +++ b/sys/dev/isp/isp.c @@ -277,6 +277,9 @@ isp_reset(ispsoftc_t *isp, int do_load_defaults) case ISP_HA_FC_2500: btype = "2532"; break; + case ISP_HA_FC_2600: + btype = "2031"; + break; default: break; } @@ -655,8 +658,10 @@ isp_reset(ispsoftc_t *isp, int do_load_defaults) ISP_WRITE(isp, isp->isp_respinrp, 0); ISP_WRITE(isp, isp->isp_respoutrp, 0); if (IS_24XX(isp)) { - ISP_WRITE(isp, BIU2400_PRI_REQINP, 0); - ISP_WRITE(isp, BIU2400_PRI_REQOUTP, 0); + if (!IS_26XX(isp)) { + ISP_WRITE(isp, BIU2400_PRI_REQINP, 0); + ISP_WRITE(isp, BIU2400_PRI_REQOUTP, 0); + } ISP_WRITE(isp, BIU2400_ATIO_RSPINP, 0); ISP_WRITE(isp, BIU2400_ATIO_RSPOUTP, 0); } @@ -761,6 +766,7 @@ isp_reset(ispsoftc_t *isp, int do_load_defaults) code_org = ISP_CODE_ORG; } + isp->isp_loaded_fw = 0; if (dodnld && IS_24XX(isp)) { const uint32_t *ptr = isp->isp_mdvec->dv_ispfw; int wordload; @@ -956,8 +962,17 @@ isp_reset(ispsoftc_t *isp, int do_load_defaults) ISP_RESET0(isp); return; } + } else if (IS_26XX(isp)) { + MBSINIT(&mbs, MBOX_LOAD_FLASH_FIRMWARE, MBLOGALL, 5000000); + mbs.ibitm = 0x01; + mbs.obitm = 0x07; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, "Flash F/W load failed"); + ISP_RESET0(isp); + return; + } } else { - isp->isp_loaded_fw = 0; isp_prt(isp, ISP_LOGDEBUG2, "skipping f/w download"); } @@ -966,7 +981,6 @@ isp_reset(ispsoftc_t *isp, int do_load_defaults) */ if (isp->isp_loaded_fw) { MBSINIT(&mbs, MBOX_VERIFY_CHECKSUM, MBLOGNONE, 0); - mbs.param[0] = MBOX_VERIFY_CHECKSUM; if (IS_24XX(isp)) { mbs.param[1] = code_org >> 16; mbs.param[2] = code_org; @@ -998,9 +1012,6 @@ isp_reset(ispsoftc_t *isp, int do_load_defaults) } else { mbs.param[3] = 1; } - if (IS_25XX(isp)) { - mbs.ibits |= 0x10; - } } else if (IS_2322(isp)) { mbs.param[1] = code_org; if (isp->isp_loaded_fw) { @@ -1861,16 +1872,16 @@ isp_fibre_init(ispsoftc_t *isp) icbp->icb_idelaytimer = 10; } icbp->icb_zfwoptions = fcp->isp_zfwoptions; - if (isp->isp_confopts & ISP_CFG_ONEGB) { + if (isp->isp_confopts & ISP_CFG_1GB) { icbp->icb_zfwoptions &= ~ICBZOPT_RATE_MASK; - icbp->icb_zfwoptions |= ICBZOPT_RATE_ONEGB; - } else if (isp->isp_confopts & ISP_CFG_TWOGB) { + icbp->icb_zfwoptions |= ICBZOPT_RATE_1GB; + } else if (isp->isp_confopts & ISP_CFG_2GB) { icbp->icb_zfwoptions &= ~ICBZOPT_RATE_MASK; - icbp->icb_zfwoptions |= ICBZOPT_RATE_TWOGB; + icbp->icb_zfwoptions |= ICBZOPT_RATE_2GB; } else { switch (icbp->icb_zfwoptions & ICBZOPT_RATE_MASK) { - case ICBZOPT_RATE_ONEGB: - case ICBZOPT_RATE_TWOGB: + case ICBZOPT_RATE_1GB: + case ICBZOPT_RATE_2GB: case ICBZOPT_RATE_AUTO: break; default: @@ -2122,18 +2133,26 @@ isp_fibre_init_2400(ispsoftc_t *isp) break; } + if (IS_26XX(isp)) { + /* We don't support MSI-X yet, so set this unconditionally. */ + icbp->icb_fwoptions2 |= ICB2400_OPT2_ENA_IHR; + icbp->icb_fwoptions2 |= ICB2400_OPT2_ENA_IHA; + } + if ((icbp->icb_fwoptions3 & ICB2400_OPT3_RSPSZ_MASK) == 0) { icbp->icb_fwoptions3 |= ICB2400_OPT3_RSPSZ_24; } icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_AUTO; - if (isp->isp_confopts & ISP_CFG_ONEGB) { - icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_ONEGB; - } else if (isp->isp_confopts & ISP_CFG_TWOGB) { - icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_TWOGB; - } else if (isp->isp_confopts & ISP_CFG_FOURGB) { - icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_FOURGB; - } else if (IS_25XX(isp) && (isp->isp_confopts & ISP_CFG_EIGHTGB)) { - icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_EIGHTGB; + if (isp->isp_confopts & ISP_CFG_1GB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_1GB; + } else if (isp->isp_confopts & ISP_CFG_2GB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_2GB; + } else if (isp->isp_confopts & ISP_CFG_4GB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_4GB; + } else if (isp->isp_confopts & ISP_CFG_8GB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_8GB; + } else if (isp->isp_confopts & ISP_CFG_16GB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_16GB; } else { icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_AUTO; } @@ -6828,7 +6847,7 @@ static const char *scsi_mbcmd_names[] = { static const uint32_t mbpfc[] = { ISP_FC_OPMAP(0x01, 0x01), /* 0x00: MBOX_NO_OP */ ISP_FC_OPMAP(0x1f, 0x01), /* 0x01: MBOX_LOAD_RAM */ - ISP_FC_OPMAP(0x0f, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */ + ISP_FC_OPMAP_HALF(0x07, 0xff, 0x00, 0x03), /* 0x02: MBOX_EXEC_FIRMWARE */ ISP_FC_OPMAP(0xdf, 0x01), /* 0x03: MBOX_DUMP_RAM */ ISP_FC_OPMAP(0x07, 0x07), /* 0x04: MBOX_WRITE_RAM_WORD */ ISP_FC_OPMAP(0x03, 0x07), /* 0x05: MBOX_READ_RAM_WORD */ @@ -7602,6 +7621,8 @@ isp_setdfltfcparm(ispsoftc_t *isp, int chan) fcp->isp_wwnn_nvram = DEFAULT_NODEWWN(isp, chan); fcp->isp_wwpn_nvram = DEFAULT_PORTWWN(isp, chan); fcp->isp_fwoptions = 0; + fcp->isp_xfwoptions = 0; + fcp->isp_zfwoptions = 0; fcp->isp_lasthdl = NIL_HANDLE; if (IS_24XX(isp)) { @@ -7899,7 +7920,9 @@ isp_rd_2400_nvram(ispsoftc_t *isp, uint32_t addr, uint32_t *rp) uint32_t base = 0x7ffe0000; uint32_t tmp = 0; - if (IS_25XX(isp)) { + if (IS_26XX(isp)) { + base = 0x7fe7c000; /* XXX: Observation, may be wrong. */ + } else if (IS_25XX(isp)) { base = 0x7ff00000 | 0x48000; } ISP_WRITE(isp, BIU2400_FLASH_ADDR, base | addr); diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c index 73741eb..c533d69 100644 --- a/sys/dev/isp/isp_freebsd.c +++ b/sys/dev/isp/isp_freebsd.c @@ -4550,91 +4550,52 @@ isp_uninit(ispsoftc_t *isp) ISP_DISABLE_INTS(isp); } -/* - * When we want to get the 'default' WWNs (when lacking NVRAM), we pick them - * up from our platform default (defww{p|n}n) and morph them based upon - * channel. - * - * When we want to get the 'active' WWNs, we get NVRAM WWNs and then morph them - * based upon channel. - */ - uint64_t isp_default_wwn(ispsoftc_t * isp, int chan, int isactive, int iswwnn) { uint64_t seed; struct isp_fc *fc = ISP_FC_PC(isp, chan); - /* - * If we're asking for a active WWN, the default overrides get - * returned, otherwise the NVRAM value is picked. - * - * If we're asking for a default WWN, we just pick the default override. - */ + /* First try to use explicitly configured WWNs. */ + seed = iswwnn ? fc->def_wwnn : fc->def_wwpn; + if (seed) + return (seed); + + /* Otherwise try to use WWNs from NVRAM. */ if (isactive) { - seed = iswwnn ? fc->def_wwnn : fc->def_wwpn; - if (seed) { - return (seed); - } - seed = iswwnn ? FCPARAM(isp, chan)->isp_wwnn_nvram : FCPARAM(isp, chan)->isp_wwpn_nvram; - if (seed) { + seed = iswwnn ? FCPARAM(isp, chan)->isp_wwnn_nvram : + FCPARAM(isp, chan)->isp_wwpn_nvram; + if (seed) return (seed); - } - return (0x400000007F000009ull); } - seed = iswwnn ? fc->def_wwnn : fc->def_wwpn; - - /* - * For channel zero just return what we have. For either ACTIVE or - * DEFAULT cases, we depend on default override of NVRAM values for - * channel zero. - */ - if (chan == 0) { - return (seed); + /* If still no WWNs, try to steal them from the first channel. */ + if (chan > 0) { + seed = iswwnn ? ISP_FC_PC(isp, 0)->def_wwnn : + ISP_FC_PC(isp, 0)->def_wwpn; + if (seed == 0) { + seed = iswwnn ? FCPARAM(isp, 0)->isp_wwnn_nvram : + FCPARAM(isp, 0)->isp_wwpn_nvram; + } } - /* - * For other channels, we are doing one of three things: - * - * 1. If what we have now is non-zero, return it. Otherwise we morph - * values from channel 0. 2. If we're here for a WWPN we synthesize - * it if Channel 0's wwpn has a type 2 NAA. 3. If we're here for a - * WWNN we synthesize it if Channel 0's wwnn has a type 2 NAA. - */ - - if (seed) { - return (seed); + /* If still nothing -- improvise. */ + if (seed == 0) { + seed = 0x400000007F000000ull + device_get_unit(isp->isp_dev); + if (!iswwnn) + seed ^= 0x0100000000000000ULL; } - seed = iswwnn ? ISP_FC_PC(isp, 0)->def_wwnn : ISP_FC_PC(isp, 0)->def_wwpn; - if (seed == 0) - seed = iswwnn ? FCPARAM(isp, 0)->isp_wwnn_nvram : FCPARAM(isp, 0)->isp_wwpn_nvram; - if (((seed >> 60) & 0xf) == 2) { + /* For additional channels we have to improvise even more. */ + if (!iswwnn && chan > 0) { /* - * The type 2 NAA fields for QLogic cards appear be laid out - * thusly: - * - * bits 63..60 NAA == 2 bits 59..57 unused/zero bit 56 - * port (1) or node (0) WWN distinguishor bit 48 - * physical port on dual-port chips (23XX/24XX) - * - * This is somewhat nutty, particularly since bit 48 is - * irrelevant as they assign separate serial numbers to - * different physical ports anyway. - * * We'll stick our channel number plus one first into bits * 57..59 and thence into bits 52..55 which allows for 8 bits - * of channel which is comfortably more than our maximum - * (126) now. + * of channel which is enough for our maximum of 255 channels. */ - seed &= ~0x0FF0000000000000ULL; - if (iswwnn == 0) { - seed |= ((uint64_t) (chan + 1) & 0xf) << 56; - seed |= ((uint64_t) ((chan + 1) >> 4) & 0xf) << 52; - } - } else { - seed = 0; + seed ^= 0x0100000000000000ULL; + seed ^= ((uint64_t) (chan + 1) & 0xf) << 56; + seed ^= ((uint64_t) ((chan + 1) >> 4) & 0xf) << 52; } return (seed); } diff --git a/sys/dev/isp/isp_freebsd.h b/sys/dev/isp/isp_freebsd.h index ff9a5a3..9a9093a 100644 --- a/sys/dev/isp/isp_freebsd.h +++ b/sys/dev/isp/isp_freebsd.h @@ -38,6 +38,7 @@ #include <sys/malloc.h> #include <sys/mutex.h> #include <sys/condvar.h> +#include <sys/rman.h> #include <sys/sysctl.h> #include <sys/proc.h> @@ -286,9 +287,9 @@ struct isposinfo { /* * DMA related sdtuff */ - bus_space_tag_t bus_tag; + struct resource * regs; + struct resource * regs2; bus_dma_tag_t dmat; - bus_space_handle_t bus_handle; bus_dma_tag_t cdmat; bus_dmamap_t cdmap; @@ -361,8 +362,8 @@ struct isposinfo { #define FCP_NEXT_CRN isp_fcp_next_crn #define isp_lock isp_osinfo.lock -#define isp_bus_tag isp_osinfo.bus_tag -#define isp_bus_handle isp_osinfo.bus_handle +#define isp_regs isp_osinfo.regs +#define isp_regs2 isp_osinfo.regs2 /* * Locking macros... @@ -430,8 +431,7 @@ case SYNC_RESULT: \ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); \ break; \ case SYNC_REG: \ - bus_space_barrier(isp->isp_osinfo.bus_tag, \ - isp->isp_osinfo.bus_handle, offset, size, \ + bus_barrier(isp->isp_osinfo.regs, offset, size, \ BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); \ break; \ default: \ @@ -463,8 +463,7 @@ case SYNC_RESULT: \ isp->isp_osinfo.cdmap, BUS_DMASYNC_POSTWRITE); \ break; \ case SYNC_REG: \ - bus_space_barrier(isp->isp_osinfo.bus_tag, \ - isp->isp_osinfo.bus_handle, offset, size, \ + bus_barrier(isp->isp_osinfo.regs, offset, size, \ BUS_SPACE_BARRIER_WRITE); \ break; \ default: \ diff --git a/sys/dev/isp/isp_library.c b/sys/dev/isp/isp_library.c index 9e8f3ab..ec99244 100644 --- a/sys/dev/isp/isp_library.c +++ b/sys/dev/isp/isp_library.c @@ -1402,7 +1402,9 @@ isp_put_icb_2400(ispsoftc_t *isp, isp_icb_2400_t *src, isp_icb_2400_t *dst) for (i = 0; i < 4; i++) { ISP_IOXPUT_16(isp, src->icb_priaddr[i], &dst->icb_priaddr[i]); } - for (i = 0; i < 4; i++) { + ISP_IOXPUT_16(isp, src->icb_msixresp, &dst->icb_msixresp); + ISP_IOXPUT_16(isp, src->icb_msixatio, &dst->icb_msixatio); + for (i = 0; i < 2; i++) { ISP_IOXPUT_16(isp, src->icb_reserved1[i], &dst->icb_reserved1[i]); } ISP_IOXPUT_16(isp, src->icb_atio_in, &dst->icb_atio_in); @@ -1415,9 +1417,14 @@ isp_put_icb_2400(ispsoftc_t *isp, isp_icb_2400_t *src, isp_icb_2400_t *dst) ISP_IOXPUT_32(isp, src->icb_fwoptions1, &dst->icb_fwoptions1); ISP_IOXPUT_32(isp, src->icb_fwoptions2, &dst->icb_fwoptions2); ISP_IOXPUT_32(isp, src->icb_fwoptions3, &dst->icb_fwoptions3); - for (i = 0; i < 12; i++) { + ISP_IOXPUT_16(isp, src->icb_qos, &dst->icb_qos); + for (i = 0; i < 3; i++) ISP_IOXPUT_16(isp, src->icb_reserved2[i], &dst->icb_reserved2[i]); - } + for (i = 0; i < 3; i++) + ISP_IOXPUT_16(isp, src->icb_enodemac[i], &dst->icb_enodemac[i]); + ISP_IOXPUT_16(isp, src->icb_disctime, &dst->icb_disctime); + for (i = 0; i < 4; i++) + ISP_IOXPUT_16(isp, src->icb_reserved3[i], &dst->icb_reserved3[i]); } void diff --git a/sys/dev/isp/isp_pci.c b/sys/dev/isp/isp_pci.c index 55cb034..e5ea359 100644 --- a/sys/dev/isp/isp_pci.c +++ b/sys/dev/isp/isp_pci.c @@ -59,6 +59,8 @@ static uint32_t isp_pci_rd_reg_1080(ispsoftc_t *, int); static void isp_pci_wr_reg_1080(ispsoftc_t *, int, uint32_t); static uint32_t isp_pci_rd_reg_2400(ispsoftc_t *, int); static void isp_pci_wr_reg_2400(ispsoftc_t *, int, uint32_t); +static uint32_t isp_pci_rd_reg_2600(ispsoftc_t *, int); +static void isp_pci_wr_reg_2600(ispsoftc_t *, int, uint32_t); static int isp_pci_rd_isr(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *); static int isp_pci_rd_isr_2300(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *); static int isp_pci_rd_isr_2400(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *); @@ -172,6 +174,18 @@ static struct ispmdvec mdvec_2500 = { NULL }; +static struct ispmdvec mdvec_2600 = { + isp_pci_rd_isr_2400, + isp_pci_rd_reg_2600, + isp_pci_wr_reg_2600, + isp_pci_mbxdma, + isp_pci_dmasetup, + isp_common_dmateardown, + isp_pci_reset0, + isp_pci_reset1, + NULL +}; + #ifndef PCIM_CMD_INVEN #define PCIM_CMD_INVEN 0x10 #endif @@ -276,6 +290,14 @@ static struct ispmdvec mdvec_2500 = { #define PCI_PRODUCT_QLOGIC_ISP5432 0x5432 #endif +#ifndef PCI_PRODUCT_QLOGIC_ISP2031 +#define PCI_PRODUCT_QLOGIC_ISP2031 0x2031 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP8031 +#define PCI_PRODUCT_QLOGIC_ISP8031 0x8031 +#endif + #define PCI_QLOGIC_ISP5432 \ ((PCI_PRODUCT_QLOGIC_ISP5432 << 16) | PCI_VENDOR_QLOGIC) @@ -327,14 +349,17 @@ static struct ispmdvec mdvec_2500 = { #define PCI_QLOGIC_ISP6322 \ ((PCI_PRODUCT_QLOGIC_ISP6322 << 16) | PCI_VENDOR_QLOGIC) +#define PCI_QLOGIC_ISP2031 \ + ((PCI_PRODUCT_QLOGIC_ISP2031 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP8031 \ + ((PCI_PRODUCT_QLOGIC_ISP8031 << 16) | PCI_VENDOR_QLOGIC) + /* * Odd case for some AMI raid cards... We need to *not* attach to this. */ #define AMI_RAID_SUBVENDOR_ID 0x101e -#define IO_MAP_REG 0x10 -#define MEM_MAP_REG 0x14 - #define PCI_DFLT_LTNCY 0x40 #define PCI_DFLT_LNSZ 0x10 @@ -348,10 +373,16 @@ struct isp_pcisoftc { ispsoftc_t pci_isp; device_t pci_dev; struct resource * regs; + struct resource * regs1; + struct resource * regs2; void * irq; int iqd; int rtp; int rgd; + int rtp1; + int rgd1; + int rtp2; + int rgd2; void * ih; int16_t pci_poff[_NREG_BLKS]; bus_dma_tag_t dmat; @@ -434,6 +465,12 @@ isp_pci_probe(device_t dev) case PCI_QLOGIC_ISP6322: device_set_desc(dev, "Qlogic ISP 6322 PCI FC-AL Adapter"); break; + case PCI_QLOGIC_ISP2031: + device_set_desc(dev, "Qlogic ISP 2031 PCI FC-AL Adapter"); + break; + case PCI_QLOGIC_ISP8031: + device_set_desc(dev, "Qlogic ISP 8031 PCI FCoE Adapter"); + break; default: return (ENXIO); } @@ -485,31 +522,6 @@ isp_get_generic_options(device_t dev, ispsoftc_t *isp) } static void -isp_get_pci_options(device_t dev, int *m1, int *m2) -{ - int tval; - /* - * Which we should try first - memory mapping or i/o mapping? - * - * We used to try memory first followed by i/o on alpha, otherwise - * the reverse, but we should just try memory first all the time now. - */ - *m1 = PCIM_CMD_MEMEN; - *m2 = PCIM_CMD_PORTEN; - - tval = 0; - if (resource_int_value(device_get_name(dev), device_get_unit(dev), "prefer_iomap", &tval) == 0 && tval != 0) { - *m1 = PCIM_CMD_PORTEN; - *m2 = PCIM_CMD_MEMEN; - } - tval = 0; - if (resource_int_value(device_get_name(dev), device_get_unit(dev), "prefer_memmap", &tval) == 0 && tval != 0) { - *m1 = PCIM_CMD_MEMEN; - *m2 = PCIM_CMD_PORTEN; - } -} - -static void isp_get_specific_options(device_t dev, int chan, ispsoftc_t *isp) { const char *sptr; @@ -662,7 +674,7 @@ isp_get_specific_options(device_t dev, int chan, ispsoftc_t *isp) static int isp_pci_attach(device_t dev) { - int i, m1, m2, locksetup = 0; + int i, locksetup = 0; uint32_t data, cmd, linesz, did; struct isp_pcisoftc *pcs; ispsoftc_t *isp; @@ -689,33 +701,10 @@ isp_pci_attach(device_t dev) isp_nvports = 0; isp_get_generic_options(dev, isp); - /* - * Get PCI options- which in this case are just mapping preferences. - */ - isp_get_pci_options(dev, &m1, &m2); - linesz = PCI_DFLT_LNSZ; - pcs->irq = pcs->regs = NULL; + pcs->irq = pcs->regs = pcs->regs2 = NULL; pcs->rgd = pcs->rtp = pcs->iqd = 0; - pcs->rtp = (m1 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT; - pcs->rgd = (m1 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG; - pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd, RF_ACTIVE); - if (pcs->regs == NULL) { - pcs->rtp = (m2 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT; - pcs->rgd = (m2 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG; - pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd, RF_ACTIVE); - } - if (pcs->regs == NULL) { - device_printf(dev, "unable to map any ports\n"); - goto bad; - } - if (bootverbose) { - device_printf(dev, "using %s space register mapping\n", (pcs->rgd == IO_MAP_REG)? "I/O" : "Memory"); - } - isp->isp_bus_tag = rman_get_bustag(pcs->regs); - isp->isp_bus_handle = rman_get_bushandle(pcs->regs); - pcs->pci_dev = dev; pcs->pci_poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF; pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS_OFF; @@ -823,6 +812,14 @@ isp_pci_attach(device_t dev) isp->isp_type = ISP_HA_FC_2500; pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS2400_OFF; break; + case PCI_QLOGIC_ISP2031: + case PCI_QLOGIC_ISP8031: + did = 0x2600; + isp->isp_nchan += isp_nvports; + isp->isp_mdvec = &mdvec_2600; + isp->isp_type = ISP_HA_FC_2600; + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS2400_OFF; + break; default: device_printf(dev, "unknown device type\n"); goto bad; @@ -830,6 +827,42 @@ isp_pci_attach(device_t dev) } isp->isp_revision = pci_get_revid(dev); + if (IS_26XX(isp)) { + pcs->rtp = SYS_RES_MEMORY; + pcs->rgd = PCIR_BAR(0); + pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd, + RF_ACTIVE); + pcs->rtp1 = SYS_RES_MEMORY; + pcs->rgd1 = PCIR_BAR(2); + pcs->regs1 = bus_alloc_resource_any(dev, pcs->rtp1, &pcs->rgd1, + RF_ACTIVE); + pcs->rtp2 = SYS_RES_MEMORY; + pcs->rgd2 = PCIR_BAR(4); + pcs->regs2 = bus_alloc_resource_any(dev, pcs->rtp2, &pcs->rgd2, + RF_ACTIVE); + } else { + pcs->rtp = SYS_RES_MEMORY; + pcs->rgd = PCIR_BAR(1); + pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd, + RF_ACTIVE); + if (pcs->regs == NULL) { + pcs->rtp = SYS_RES_IOPORT; + pcs->rgd = PCIR_BAR(0); + pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, + &pcs->rgd, RF_ACTIVE); + } + } + if (pcs->regs == NULL) { + device_printf(dev, "Unable to map any ports\n"); + goto bad; + } + if (bootverbose) { + device_printf(dev, "Using %s space register mapping\n", + (pcs->rtp == SYS_RES_IOPORT)? "I/O" : "Memory"); + } + isp->isp_regs = pcs->regs; + isp->isp_regs2 = pcs->regs2; + if (IS_FC(isp)) { psize = sizeof (fcparam); xsize = sizeof (struct isp_fc); @@ -910,20 +943,28 @@ isp_pci_attach(device_t dev) data &= ~1; pci_write_config(dev, PCIR_ROMADDR, data, 4); - /* - * Do MSI - * - * NB: MSI-X needs to be disabled for the 2432 (PCI-Express) - */ - if (IS_24XX(isp) || IS_2322(isp)) { - pcs->msicount = pci_msi_count(dev); - if (pcs->msicount > 1) { - pcs->msicount = 1; + if (IS_26XX(isp)) { + /* 26XX chips support only MSI-X, so start from them. */ + pcs->msicount = imin(pci_msix_count(dev), 1); + if (pcs->msicount > 0 && + (i = pci_alloc_msix(dev, &pcs->msicount)) == 0) { + pcs->iqd = 1; + } else { + pcs->msicount = 0; } - if (pci_alloc_msi(dev, &pcs->msicount) == 0) { + } + if (pcs->msicount == 0 && (IS_24XX(isp) || IS_2322(isp))) { + /* + * Older chips support both MSI and MSI-X, but I have + * feeling that older firmware may not support MSI-X, + * but we have no way to check the firmware flag here. + */ + pcs->msicount = imin(pci_msi_count(dev), 1); + if (pcs->msicount > 0 && + pci_alloc_msi(dev, &pcs->msicount) == 0) { pcs->iqd = 1; } else { - pcs->iqd = 0; + pcs->msicount = 0; } } pcs->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &pcs->iqd, RF_ACTIVE | RF_SHAREABLE); @@ -978,9 +1019,12 @@ bad: if (pcs->msicount) { pci_release_msi(dev); } - if (pcs->regs) { + if (pcs->regs) (void) bus_release_resource(dev, pcs->rtp, pcs->rgd, pcs->regs); - } + if (pcs->regs1) + (void) bus_release_resource(dev, pcs->rtp1, pcs->rgd1, pcs->regs1); + if (pcs->regs2) + (void) bus_release_resource(dev, pcs->rtp2, pcs->rgd2, pcs->regs2); if (pcs->pci_isp.isp_param) { free(pcs->pci_isp.isp_param, M_DEVBUF); pcs->pci_isp.isp_param = NULL; @@ -1019,6 +1063,10 @@ isp_pci_detach(device_t dev) pci_release_msi(dev); } (void) bus_release_resource(dev, pcs->rtp, pcs->rgd, pcs->regs); + if (pcs->regs1) + (void) bus_release_resource(dev, pcs->rtp1, pcs->rgd1, pcs->regs1); + if (pcs->regs2) + (void) bus_release_resource(dev, pcs->rtp2, pcs->rgd2, pcs->regs2); /* * XXX: THERE IS A LOT OF LEAKAGE HERE */ @@ -1037,15 +1085,12 @@ isp_pci_detach(device_t dev) (((struct isp_pcisoftc *)a)->pci_poff[((x) & _BLK_REG_MASK) >> \ _BLK_REG_SHFT] + ((x) & 0xfff)) -#define BXR2(isp, off) \ - bus_space_read_2(isp->isp_bus_tag, isp->isp_bus_handle, off) -#define BXW2(isp, off, v) \ - bus_space_write_2(isp->isp_bus_tag, isp->isp_bus_handle, off, v) -#define BXR4(isp, off) \ - bus_space_read_4(isp->isp_bus_tag, isp->isp_bus_handle, off) -#define BXW4(isp, off, v) \ - bus_space_write_4(isp->isp_bus_tag, isp->isp_bus_handle, off, v) - +#define BXR2(isp, off) bus_read_2((isp)->isp_regs, (off)) +#define BXW2(isp, off, v) bus_write_2((isp)->isp_regs, (off), (v)) +#define BXR4(isp, off) bus_read_4((isp)->isp_regs, (off)) +#define BXW4(isp, off, v) bus_write_4((isp)->isp_regs, (off), (v)) +#define B2R4(isp, off) bus_read_4((isp)->isp_regs2, (off)) +#define B2W4(isp, off, v) bus_write_4((isp)->isp_regs2, (off), (v)) static ISP_INLINE int isp_pci_rd_debounced(ispsoftc_t *isp, int off, uint16_t *rp) @@ -1308,20 +1353,19 @@ isp_pci_rd_reg_2400(ispsoftc_t *isp, int regoff) case MBOX_BLOCK: return (BXR2(isp, IspVirt2Off(isp, regoff))); case SXP_BLOCK: - isp_prt(isp, ISP_LOGWARN, "SXP_BLOCK read at 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "SXP_BLOCK read at 0x%x", regoff); return (0xffffffff); case RISC_BLOCK: - isp_prt(isp, ISP_LOGWARN, "RISC_BLOCK read at 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "RISC_BLOCK read at 0x%x", regoff); return (0xffffffff); case DMA_BLOCK: - isp_prt(isp, ISP_LOGWARN, "DMA_BLOCK read at 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "DMA_BLOCK read at 0x%x", regoff); return (0xffffffff); default: - isp_prt(isp, ISP_LOGWARN, "unknown block read at 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "unknown block read at 0x%x", regoff); return (0xffffffff); } - switch (regoff) { case BIU2400_FLASH_ADDR: case BIU2400_FLASH_DATA: @@ -1349,8 +1393,8 @@ isp_pci_rd_reg_2400(ispsoftc_t *isp, int regoff) rv = BXR4(isp, IspVirt2Off(isp, regoff)) >> 16; break; default: - isp_prt(isp, ISP_LOGERR, - "isp_pci_rd_reg_2400: unknown offset %x", regoff); + isp_prt(isp, ISP_LOGERR, "unknown register read at 0x%x", + regoff); rv = 0xffffffff; break; } @@ -1370,17 +1414,16 @@ isp_pci_wr_reg_2400(ispsoftc_t *isp, int regoff, uint32_t val) MEMORYBARRIER(isp, SYNC_REG, IspVirt2Off(isp, regoff), 2, -1); return; case SXP_BLOCK: - isp_prt(isp, ISP_LOGWARN, "SXP_BLOCK write at 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "SXP_BLOCK write at 0x%x", regoff); return; case RISC_BLOCK: - isp_prt(isp, ISP_LOGWARN, "RISC_BLOCK write at 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "RISC_BLOCK write at 0x%x", regoff); return; case DMA_BLOCK: - isp_prt(isp, ISP_LOGWARN, "DMA_BLOCK write at 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "DMA_BLOCK write at 0x%x", regoff); return; default: - isp_prt(isp, ISP_LOGWARN, "unknown block write at 0x%x", - regoff); + isp_prt(isp, ISP_LOGERR, "unknown block write at 0x%x", regoff); break; } @@ -1415,10 +1458,83 @@ isp_pci_wr_reg_2400(ispsoftc_t *isp, int regoff, uint32_t val) MEMORYBARRIER(isp, SYNC_REG, IspVirt2Off(isp, regoff), 4, -1); break; default: - isp_prt(isp, ISP_LOGERR, - "isp_pci_wr_reg_2400: bad offset 0x%x", regoff); + isp_prt(isp, ISP_LOGERR, "unknown register write at 0x%x", + regoff); + break; + } +} + +static uint32_t +isp_pci_rd_reg_2600(ispsoftc_t *isp, int regoff) +{ + uint32_t rv; + + switch (regoff) { + case BIU2400_PRI_REQINP: + case BIU2400_PRI_REQOUTP: + isp_prt(isp, ISP_LOGERR, "unknown register read at 0x%x", + regoff); + rv = 0xffffffff; + break; + case BIU2400_REQINP: + rv = B2R4(isp, 0x00); + break; + case BIU2400_REQOUTP: + rv = B2R4(isp, 0x04); break; + case BIU2400_RSPINP: + rv = B2R4(isp, 0x08); + break; + case BIU2400_RSPOUTP: + rv = B2R4(isp, 0x0c); + break; + case BIU2400_ATIO_RSPINP: + rv = B2R4(isp, 0x10); + break; + case BIU2400_ATIO_RSPOUTP: + rv = B2R4(isp, 0x14); + break; + default: + rv = isp_pci_rd_reg_2400(isp, regoff); + break; + } + return (rv); +} + +static void +isp_pci_wr_reg_2600(ispsoftc_t *isp, int regoff, uint32_t val) +{ + int off; + + switch (regoff) { + case BIU2400_PRI_REQINP: + case BIU2400_PRI_REQOUTP: + isp_prt(isp, ISP_LOGERR, "unknown register write at 0x%x", + regoff); + return; + case BIU2400_REQINP: + off = 0x00; + break; + case BIU2400_REQOUTP: + off = 0x04; + break; + case BIU2400_RSPINP: + off = 0x08; + break; + case BIU2400_RSPOUTP: + off = 0x0c; + break; + case BIU2400_ATIO_RSPINP: + off = 0x10; + break; + case BIU2400_ATIO_RSPOUTP: + off = 0x14; + break; + default: + isp_pci_wr_reg_2400(isp, regoff, val); + return; } + B2W4(isp, off, val); } diff --git a/sys/dev/isp/isp_sbus.c b/sys/dev/isp/isp_sbus.c index cb8f169..2abfc64 100644 --- a/sys/dev/isp/isp_sbus.c +++ b/sys/dev/isp/isp_sbus.c @@ -180,8 +180,7 @@ isp_sbus_attach(device_t dev) sbs->sbus_poff[RISC_BLOCK >> _BLK_REG_SHFT] = SBUS_RISC_REGS_OFF; sbs->sbus_poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF; isp = &sbs->sbus_isp; - isp->isp_bus_tag = rman_get_bustag(sbs->regs); - isp->isp_bus_handle = rman_get_bushandle(sbs->regs); + isp->isp_regs = sbs->regs; isp->isp_mdvec = &sbs->sbus_mdvec; isp->isp_bustype = ISP_BT_SBUS; isp->isp_type = ISP_HA_SCSI_UNKNOWN; @@ -367,16 +366,15 @@ isp_sbus_detach(device_t dev) (((struct isp_sbussoftc *)a)->sbus_poff[((x) & _BLK_REG_MASK) >> \ _BLK_REG_SHFT] + ((x) & 0xff)) -#define BXR2(sbc, off) \ - bus_space_read_2(isp->isp_bus_tag, isp->isp_bus_handle, off) +#define BXR2(isp, off) bus_read_2((isp)->isp_regs, (off)) static int isp_sbus_rd_isr(ispsoftc_t *isp, uint16_t *isrp, uint16_t *semap, uint16_t *info) { uint16_t isr, sema; - isr = BXR2(sbc, IspVirt2Off(isp, BIU_ISR)); - sema = BXR2(sbc, IspVirt2Off(isp, BIU_SEMA)); + isr = BXR2(isp, IspVirt2Off(isp, BIU_ISR)); + sema = BXR2(isp, IspVirt2Off(isp, BIU_SEMA)); isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema); isr &= INT_PENDING_MASK(isp); sema &= BIU_SEMA_LOCK; @@ -385,7 +383,7 @@ isp_sbus_rd_isr(ispsoftc_t *isp, uint16_t *isrp, uint16_t *semap, uint16_t *info } *isrp = isr; if ((*semap = sema) != 0) - *info = BXR2(sbc, IspVirt2Off(isp, OUTMAILBOX0)); + *info = BXR2(isp, IspVirt2Off(isp, OUTMAILBOX0)); return (1); } @@ -396,7 +394,7 @@ isp_sbus_rd_reg(ispsoftc_t *isp, int regoff) struct isp_sbussoftc *sbs = (struct isp_sbussoftc *) isp; int offset = sbs->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT]; offset += (regoff & 0xff); - rval = bus_space_read_2(isp->isp_bus_tag, isp->isp_bus_handle, offset); + rval = BXR2(isp, offset); isp_prt(isp, ISP_LOGDEBUG3, "isp_sbus_rd_reg(off %x) = %x", regoff, rval); return (rval); @@ -410,7 +408,7 @@ isp_sbus_wr_reg(ispsoftc_t *isp, int regoff, uint32_t val) offset += (regoff & 0xff); isp_prt(isp, ISP_LOGDEBUG3, "isp_sbus_wr_reg(off %x) = %x", regoff, val); - bus_space_write_2(isp->isp_bus_tag, isp->isp_bus_handle, offset, val); + bus_write_2(isp->isp_regs, offset, val); MEMORYBARRIER(isp, SYNC_REG, offset, 2, -1); } diff --git a/sys/dev/isp/ispmbox.h b/sys/dev/isp/ispmbox.h index d724679..a89228b 100644 --- a/sys/dev/isp/ispmbox.h +++ b/sys/dev/isp/ispmbox.h @@ -122,6 +122,7 @@ #define MBOX_GET_TARGET_STATUS 0x0056 /* These are for the ISP2X00 FC cards */ +#define MBOX_LOAD_FLASH_FIRMWARE 0x0003 #define MBOX_WRITE_FC_SERDES_REG 0x0003 /* FC only */ #define MBOX_READ_FC_SERDES_REG 0x0004 /* FC only */ #define MBOX_GET_IO_STATUS 0x0012 @@ -1007,9 +1008,9 @@ typedef struct { #define ICBXOPT_TIMER_MASK 0x7 #define ICBZOPT_RATE_MASK 0xC000 -#define ICBZOPT_RATE_ONEGB 0x0000 +#define ICBZOPT_RATE_1GB 0x0000 #define ICBZOPT_RATE_AUTO 0x8000 -#define ICBZOPT_RATE_TWOGB 0x4000 +#define ICBZOPT_RATE_2GB 0x4000 #define ICBZOPT_50_OHM 0x2000 #define ICBZOPT_NO_LOCAL_PLOGI 0x0080 #define ICBZOPT_ENA_OOF 0x0040 /* out of order frame handling */ @@ -1058,14 +1059,14 @@ typedef struct { #define ICB2400_OPT3_ENA_ETH_RESP 0x08000000 #define ICB2400_OPT3_ENA_ETH_ATIO 0x04000000 #define ICB2400_OPT3_ENA_MFCF 0x00020000 -#define ICB2400_OPT3_SKIP_FOURGB 0x00010000 +#define ICB2400_OPT3_SKIP_4GB 0x00010000 #define ICB2400_OPT3_RATE_MASK 0x0000E000 -#define ICB2400_OPT3_RATE_ONEGB 0x00000000 -#define ICB2400_OPT3_RATE_TWOGB 0x00002000 +#define ICB2400_OPT3_RATE_1GB 0x00000000 +#define ICB2400_OPT3_RATE_2GB 0x00002000 #define ICB2400_OPT3_RATE_AUTO 0x00004000 -#define ICB2400_OPT3_RATE_FOURGB 0x00006000 -#define ICB2400_OPT3_RATE_EIGHTGB 0x00008000 -#define ICB2400_OPT3_RATE_SIXTEENGB 0x0000A000 +#define ICB2400_OPT3_RATE_4GB 0x00006000 +#define ICB2400_OPT3_RATE_8GB 0x00008000 +#define ICB2400_OPT3_RATE_16GB 0x0000A000 #define ICB2400_OPT3_ENA_OOF_XFRDY 0x00000200 #define ICB2400_OPT3_NO_N2N_LOGI 0x00000100 #define ICB2400_OPT3_NO_LOCAL_PLOGI 0x00000080 diff --git a/sys/dev/isp/ispvar.h b/sys/dev/isp/ispvar.h index 10060e1..0c42642 100644 --- a/sys/dev/isp/ispvar.h +++ b/sys/dev/isp/ispvar.h @@ -653,8 +653,8 @@ struct ispsoftc { #define ISP_CFG_NPORT 0x04 /* prefer {N/F}-Port connection */ #define ISP_CFG_NPORT_ONLY 0x08 /* insist on {N/F}-Port connection */ #define ISP_CFG_LPORT_ONLY 0x0c /* insist on {N/F}L-Port connection */ -#define ISP_CFG_ONEGB 0x10 /* force 1GB connection (23XX only) */ -#define ISP_CFG_TWOGB 0x20 /* force 2GB connection (23XX only) */ +#define ISP_CFG_1GB 0x10 /* force 1GB connection (23XX only) */ +#define ISP_CFG_2GB 0x20 /* force 2GB connection (23XX only) */ #define ISP_CFG_NORELOAD 0x80 /* don't download f/w */ #define ISP_CFG_NONVRAM 0x40 /* ignore NVRAM */ #define ISP_CFG_NOFCTAPE 0x100 /* disable FC-Tape */ @@ -662,9 +662,9 @@ struct ispsoftc { #define ISP_CFG_OWNFSZ 0x400 /* override NVRAM frame size */ #define ISP_CFG_OWNLOOPID 0x800 /* override NVRAM loopid */ #define ISP_CFG_OWNEXCTHROTTLE 0x1000 /* override NVRAM execution throttle */ -#define ISP_CFG_FOURGB 0x2000 /* force 4GB connection (24XX only) */ -#define ISP_CFG_EIGHTGB 0x4000 /* force 8GB connection (25XX only) */ -#define ISP_CFG_SIXTEENGB 0x8000 /* force 16GB connection (82XX only) */ +#define ISP_CFG_4GB 0x2000 /* force 4GB connection (24XX only) */ +#define ISP_CFG_8GB 0x4000 /* force 8GB connection (25XX only) */ +#define ISP_CFG_16GB 0x8000 /* force 16GB connection (82XX only) */ /* * For each channel, the outer layers should know what role that channel @@ -764,6 +764,7 @@ struct ispsoftc { #define ISP_HA_FC_2322 0x50 #define ISP_HA_FC_2400 0x60 #define ISP_HA_FC_2500 0x70 +#define ISP_HA_FC_2600 0x80 #define IS_SCSI(isp) (isp->isp_type & ISP_HA_SCSI) #define IS_1020(isp) (isp->isp_type < ISP_HA_SCSI_1240) @@ -789,6 +790,7 @@ struct ispsoftc { #define IS_2322(isp) ((isp)->isp_type == ISP_HA_FC_2322) #define IS_24XX(isp) ((isp)->isp_type >= ISP_HA_FC_2400) #define IS_25XX(isp) ((isp)->isp_type >= ISP_HA_FC_2500) +#define IS_26XX(isp) ((isp)->isp_type >= ISP_HA_FC_2600) /* * DMA related macros diff --git a/sys/dev/md/md.c b/sys/dev/md/md.c index 27ef8b3..693f000 100644 --- a/sys/dev/md/md.c +++ b/sys/dev/md/md.c @@ -825,8 +825,8 @@ mdstart_vnode(struct md_s *sc, struct bio *bp) struct buf *pb; bus_dma_segment_t *vlist; struct thread *td; - off_t len, zerosize; - int ma_offs; + off_t iolen, len, zerosize; + int ma_offs, npages; switch (bp->bio_cmd) { case BIO_READ: @@ -847,6 +847,7 @@ mdstart_vnode(struct md_s *sc, struct bio *bp) pb = NULL; piov = NULL; ma_offs = bp->bio_ma_offset; + len = bp->bio_length; /* * VNODE I/O @@ -879,7 +880,6 @@ mdstart_vnode(struct md_s *sc, struct bio *bp) auio.uio_iovcnt = howmany(bp->bio_length, zerosize); piov = malloc(sizeof(*piov) * auio.uio_iovcnt, M_MD, M_WAITOK); auio.uio_iov = piov; - len = bp->bio_length; while (len > 0) { piov->iov_base = __DECONST(void *, zero_region); piov->iov_len = len; @@ -893,7 +893,6 @@ mdstart_vnode(struct md_s *sc, struct bio *bp) piov = malloc(sizeof(*piov) * bp->bio_ma_n, M_MD, M_WAITOK); auio.uio_iov = piov; vlist = (bus_dma_segment_t *)bp->bio_data; - len = bp->bio_length; while (len > 0) { piov->iov_base = (void *)(uintptr_t)(vlist->ds_addr + ma_offs); @@ -909,11 +908,20 @@ mdstart_vnode(struct md_s *sc, struct bio *bp) piov = auio.uio_iov; } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pb = getpbuf(&md_vnode_pbuf_freecnt); - pmap_qenter((vm_offset_t)pb->b_data, bp->bio_ma, bp->bio_ma_n); - aiov.iov_base = (void *)((vm_offset_t)pb->b_data + ma_offs); - aiov.iov_len = bp->bio_length; + bp->bio_resid = len; +unmapped_step: + npages = atop(min(MAXPHYS, round_page(len + (ma_offs & + PAGE_MASK)))); + iolen = min(ptoa(npages) - (ma_offs & PAGE_MASK), len); + KASSERT(iolen > 0, ("zero iolen")); + pmap_qenter((vm_offset_t)pb->b_data, + &bp->bio_ma[atop(ma_offs)], npages); + aiov.iov_base = (void *)((vm_offset_t)pb->b_data + + (ma_offs & PAGE_MASK)); + aiov.iov_len = iolen; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; + auio.uio_resid = iolen; } else { aiov.iov_base = bp->bio_data; aiov.iov_len = bp->bio_length; @@ -937,15 +945,21 @@ mdstart_vnode(struct md_s *sc, struct bio *bp) vn_finished_write(mp); } - if (pb) { - pmap_qremove((vm_offset_t)pb->b_data, bp->bio_ma_n); + if (pb != NULL) { + pmap_qremove((vm_offset_t)pb->b_data, npages); + if (error == 0) { + len -= iolen; + bp->bio_resid -= iolen; + ma_offs += iolen; + if (len > 0) + goto unmapped_step; + } relpbuf(pb, &md_vnode_pbuf_freecnt); } - if (piov != NULL) - free(piov, M_MD); - - bp->bio_resid = auio.uio_resid; + free(piov, M_MD); + if (pb == NULL) + bp->bio_resid = auio.uio_resid; return (error); } diff --git a/sys/dev/mii/rgephy.c b/sys/dev/mii/rgephy.c index 2c28068..ff7c470 100644 --- a/sys/dev/mii/rgephy.c +++ b/sys/dev/mii/rgephy.c @@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$"); #include "miibus_if.h" #include <machine/bus.h> -#include <pci/if_rlreg.h> +#include <dev/rl/if_rlreg.h> static int rgephy_probe(device_t); static int rgephy_attach(device_t); diff --git a/sys/dev/mii/rlphy.c b/sys/dev/mii/rlphy.c index ddf8d73..308ee22 100644 --- a/sys/dev/mii/rlphy.c +++ b/sys/dev/mii/rlphy.c @@ -53,7 +53,7 @@ __FBSDID("$FreeBSD$"); #include "miidevs.h" #include <machine/bus.h> -#include <pci/if_rlreg.h> +#include <dev/rl/if_rlreg.h> #include "miibus_if.h" diff --git a/sys/dev/mii/rlswitch.c b/sys/dev/mii/rlswitch.c index b158fd4..2e9188e 100644 --- a/sys/dev/mii/rlswitch.c +++ b/sys/dev/mii/rlswitch.c @@ -54,7 +54,7 @@ __FBSDID("$FreeBSD$"); #include "miidevs.h" #include <machine/bus.h> -#include <pci/if_rlreg.h> +#include <dev/rl/if_rlreg.h> #include "miibus_if.h" diff --git a/sys/dev/mpt/mpt.c b/sys/dev/mpt/mpt.c index d5b2a18..9317965 100644 --- a/sys/dev/mpt/mpt.c +++ b/sys/dev/mpt/mpt.c @@ -1423,7 +1423,7 @@ mpt_send_handshake_cmd(struct mpt_softc *mpt, size_t len, void *cmd) /* Send the command */ for (i = 0; i < len; i++) { - mpt_write(mpt, MPT_OFFSET_DOORBELL, htole32(*data32++)); + mpt_write_stream(mpt, MPT_OFFSET_DOORBELL, *data32++); if (mpt_wait_db_ack(mpt) != MPT_OK) { mpt_prt(mpt, "mpt_send_handshake_cmd: timeout @ index %d\n", i); @@ -1457,7 +1457,7 @@ mpt_recv_handshake_reply(struct mpt_softc *mpt, size_t reply_len, void *reply) *data16++ = le16toh(data & MPT_DB_DATA_MASK); mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); - /* Get Second Word */ + /* Get second word */ if (mpt_wait_db_int(mpt) != MPT_OK) { mpt_prt(mpt, "mpt_recv_handshake_cmd timeout2\n"); return ETIMEDOUT; @@ -1481,18 +1481,13 @@ mpt_recv_handshake_reply(struct mpt_softc *mpt, size_t reply_len, void *reply) left = (hdr->MsgLength << 1) - 2; reply_left = reply_len - 2; while (left--) { - u_int16_t datum; - if (mpt_wait_db_int(mpt) != MPT_OK) { mpt_prt(mpt, "mpt_recv_handshake_cmd timeout3\n"); return ETIMEDOUT; } data = mpt_read(mpt, MPT_OFFSET_DOORBELL); - datum = le16toh(data & MPT_DB_DATA_MASK); - if (reply_left-- > 0) - *data16++ = datum; - + *data16++ = le16toh(data & MPT_DB_DATA_MASK); mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); } diff --git a/sys/dev/mpt/mpt.h b/sys/dev/mpt/mpt.h index 1dcd76f..57dbe7b 100644 --- a/sys/dev/mpt/mpt.h +++ b/sys/dev/mpt/mpt.h @@ -329,7 +329,6 @@ typedef struct mpt_config_params { } cfgparms_t; /**************************** MPI Target State Info ***************************/ - typedef struct { uint32_t reply_desc; /* current reply descriptor */ uint32_t resid; /* current data residual */ @@ -784,6 +783,7 @@ mpt_assign_serno(struct mpt_softc *mpt, request_t *req) /******************************* Register Access ******************************/ static __inline void mpt_write(struct mpt_softc *, size_t, uint32_t); +static __inline void mpt_write_stream(struct mpt_softc *, size_t, uint32_t); static __inline uint32_t mpt_read(struct mpt_softc *, int); static __inline void mpt_pio_write(struct mpt_softc *, size_t, uint32_t); static __inline uint32_t mpt_pio_read(struct mpt_softc *, int); @@ -794,6 +794,12 @@ mpt_write(struct mpt_softc *mpt, size_t offset, uint32_t val) bus_space_write_4(mpt->pci_st, mpt->pci_sh, offset, val); } +static __inline void +mpt_write_stream(struct mpt_softc *mpt, size_t offset, uint32_t val) +{ + bus_space_write_stream_4(mpt->pci_st, mpt->pci_sh, offset, val); +} + static __inline uint32_t mpt_read(struct mpt_softc *mpt, int offset) { @@ -818,6 +824,7 @@ mpt_pio_read(struct mpt_softc *mpt, int offset) KASSERT(mpt->pci_pio_reg != NULL, ("no PIO resource")); return (bus_space_read_4(mpt->pci_pio_st, mpt->pci_pio_sh, offset)); } + /*********************** Reply Frame/Request Management ***********************/ /* Max MPT Reply we are willing to accept (must be power of 2) */ #define MPT_REPLY_SIZE 256 @@ -958,6 +965,7 @@ mpt_cdblen(uint8_t cdb0, int maxlen) return (16); } } + #ifdef INVARIANTS static __inline request_t * mpt_tag_2_req(struct mpt_softc *, uint32_t); static __inline request_t * @@ -1136,6 +1144,7 @@ mpt_write_cur_cfg_page(struct mpt_softc *mpt, uint32_t PageAddress, PageAddress, hdr, len, sleep_ok, timeout_ms)); } + /* mpt_debug.c functions */ void mpt_print_reply(void *vmsg); void mpt_print_db(uint32_t mb); @@ -1145,4 +1154,5 @@ void mpt_req_state(mpt_req_state_t state); void mpt_print_config_request(void *vmsg); void mpt_print_request(void *vmsg); void mpt_dump_sgl(SGE_IO_UNION *se, int offset); + #endif /* _MPT_H_ */ diff --git a/sys/dev/qlxgbe/ql_hw.c b/sys/dev/qlxgbe/ql_hw.c index 0e389d6..3efca30 100644 --- a/sys/dev/qlxgbe/ql_hw.c +++ b/sys/dev/qlxgbe/ql_hw.c @@ -387,6 +387,7 @@ ql_hw_add_sysctls(qla_host_t *ha) "Minidump Utility can start minidump process"); #ifdef QL_DBG + ha->err_inject = 0; SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "err_inject", @@ -3057,7 +3058,7 @@ ql_hw_check_health(qla_host_t *ha) val = READ_REG32(ha, Q8_FIRMWARE_HEARTBEAT); if ((val != ha->hw.hbeat_value) && - (!(QL_ERR_INJECT(ha, INJCT_TEMPERATURE_FAILURE)))) { + (!(QL_ERR_INJECT(ha, INJCT_HEARTBEAT_FAILURE)))) { ha->hw.hbeat_value = val; return 0; } diff --git a/sys/dev/qlxgbe/ql_os.c b/sys/dev/qlxgbe/ql_os.c index ecc6cce..5190ff5 100644 --- a/sys/dev/qlxgbe/ql_os.c +++ b/sys/dev/qlxgbe/ql_os.c @@ -289,8 +289,6 @@ qla_pci_attach(device_t dev) int i; uint32_t num_rcvq = 0; - QL_DPRINT2(ha, (dev, "%s: enter\n", __func__)); - if ((ha = device_get_softc(dev)) == NULL) { device_printf(dev, "cannot get softc\n"); return (ENOMEM); diff --git a/sys/dev/qlxgbe/ql_ver.h b/sys/dev/qlxgbe/ql_ver.h index 061e368..0d86db9 100644 --- a/sys/dev/qlxgbe/ql_ver.h +++ b/sys/dev/qlxgbe/ql_ver.h @@ -36,6 +36,6 @@ #define QLA_VERSION_MAJOR 3 #define QLA_VERSION_MINOR 10 -#define QLA_VERSION_BUILD 24 +#define QLA_VERSION_BUILD 25 #endif /* #ifndef _QL_VER_H_ */ diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c index 20d47e2..3812962 100644 --- a/sys/dev/re/if_re.c +++ b/sys/dev/re/if_re.c @@ -147,7 +147,7 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> -#include <pci/if_rlreg.h> +#include <dev/rl/if_rlreg.h> MODULE_DEPEND(re, pci, 1, 1, 1); MODULE_DEPEND(re, ether, 1, 1, 1); @@ -237,6 +237,7 @@ static const struct rl_hwrev re_hwrevs[] = { { RL_HWREV_8168F, RL_8169, "8168F/8111F", RL_JUMBO_MTU_9K}, { RL_HWREV_8168G, RL_8169, "8168G/8111G", RL_JUMBO_MTU_9K}, { RL_HWREV_8168GU, RL_8169, "8168GU/8111GU", RL_JUMBO_MTU_9K}, + { RL_HWREV_8168H, RL_8169, "8168H/8111H", RL_JUMBO_MTU_9K}, { RL_HWREV_8411, RL_8169, "8411", RL_JUMBO_MTU_9K}, { RL_HWREV_8411B, RL_8169, "8411B", RL_JUMBO_MTU_9K}, { 0, 0, NULL, 0 } @@ -633,9 +634,8 @@ re_miibus_statchg(device_t dev) } } /* - * RealTek controllers does not provide any interface to - * Tx/Rx MACs for resolved speed, duplex and flow-control - * parameters. + * RealTek controllers do not provide any interface to the RX/TX + * MACs for resolved speed, duplex and flow-control parameters. */ } @@ -657,7 +657,7 @@ re_set_rxmode(struct rl_softc *sc) rxfilt = RL_RXCFG_CONFIG | RL_RXCFG_RX_INDIV | RL_RXCFG_RX_BROAD; if ((sc->rl_flags & RL_FLAG_EARLYOFF) != 0) rxfilt |= RL_RXCFG_EARLYOFF; - else if ((sc->rl_flags & RL_FLAG_EARLYOFFV2) != 0) + else if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) rxfilt |= RL_RXCFG_EARLYOFFV2; if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { @@ -1204,11 +1204,10 @@ re_attach(device_t dev) struct rl_softc *sc; struct ifnet *ifp; const struct rl_hwrev *hw_rev; + int capmask, error = 0, hwrev, i, msic, msixc, + phy, reg, rid; u_int32_t cap, ctl; - int hwrev; u_int16_t devid, re_did = 0; - int error = 0, i, phy, rid; - int msic, msixc, reg; uint8_t cfg; sc = device_get_softc(dev); @@ -1488,11 +1487,12 @@ re_attach(device_t dev) RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 | RL_FLAG_CMDSTOP_WAIT_TXQ | RL_FLAG_WOL_MANLINK | - RL_FLAG_EARLYOFFV2 | RL_FLAG_RXDV_GATED; + RL_FLAG_8168G_PLUS; break; case RL_HWREV_8168GU: + case RL_HWREV_8168H: if (pci_get_device(dev) == RT_DEVICEID_8101E) { - /* RTL8106EUS */ + /* RTL8106E(US), RTL8107E */ sc->rl_flags |= RL_FLAG_FASTETHER; } else sc->rl_flags |= RL_FLAG_JUMBOV2 | RL_FLAG_WOL_MANLINK; @@ -1500,7 +1500,7 @@ re_attach(device_t dev) sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_CMDSTOP_WAIT_TXQ | - RL_FLAG_EARLYOFFV2 | RL_FLAG_RXDV_GATED; + RL_FLAG_8168G_PLUS; break; case RL_HWREV_8169_8110SB: case RL_HWREV_8169_8110SBL: @@ -1650,8 +1650,11 @@ re_attach(device_t dev) phy = RE_PHYAD_INTERNAL; if (sc->rl_type == RL_8169) phy = 1; + capmask = BMSR_DEFCAPMASK; + if ((sc->rl_flags & RL_FLAG_FASTETHER) != 0) + capmask &= ~BMSR_EXTSTAT; error = mii_attach(dev, &sc->rl_miibus, ifp, re_ifmedia_upd, - re_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, MIIF_DOPAUSE); + re_ifmedia_sts, capmask, phy, MII_OFFSET_ANY, MIIF_DOPAUSE); if (error != 0) { device_printf(dev, "attaching PHYs failed\n"); goto fail; @@ -1691,13 +1694,13 @@ re_attach(device_t dev) #ifdef DEV_NETMAP re_netmap_attach(sc); #endif /* DEV_NETMAP */ + #ifdef RE_DIAG /* * Perform hardware diagnostic on the original RTL8169. * Some 32-bit cards were incorrectly wired and would * malfunction if plugged into a 64-bit slot. */ - if (hwrev == RL_HWREV_8169) { error = re_diag(sc); if (error) { @@ -1729,7 +1732,6 @@ re_attach(device_t dev) } fail: - if (error) re_detach(dev); @@ -2935,6 +2937,7 @@ re_start_locked(struct ifnet *ifp) return; } #endif /* DEV_NETMAP */ + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) return; @@ -3190,9 +3193,18 @@ re_init_locked(struct rl_softc *sc) CSR_WRITE_4(sc, RL_TXLIST_ADDR_LO, RL_ADDR_LO(sc->rl_ldata.rl_tx_list_addr)); - if ((sc->rl_flags & RL_FLAG_RXDV_GATED) != 0) + if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) { + /* Disable RXDV gate. */ CSR_WRITE_4(sc, RL_MISC, CSR_READ_4(sc, RL_MISC) & ~0x00080000); + } + + /* + * Enable transmit and receive for pre-RTL8168G controllers. + * RX/TX MACs should be enabled before RX/TX configuration. + */ + if ((sc->rl_flags & RL_FLAG_8168G_PLUS) == 0) + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB | RL_CMD_RX_ENB); /* * Set the initial TX configuration. @@ -3221,9 +3233,11 @@ re_init_locked(struct rl_softc *sc) } /* - * Enable transmit and receive. + * Enable transmit and receive for RTL8168G and later controllers. + * RX/TX MACs should be enabled after RX/TX configuration. */ - CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB | RL_CMD_RX_ENB); + if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB | RL_CMD_RX_ENB); #ifdef DEVICE_POLLING /* @@ -3579,6 +3593,12 @@ re_stop(struct rl_softc *sc) ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_INDIV | RL_RXCFG_RX_MULTI | RL_RXCFG_RX_BROAD)); + if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) { + /* Enable RXDV gate. */ + CSR_WRITE_4(sc, RL_MISC, CSR_READ_4(sc, RL_MISC) | + 0x00080000); + } + if ((sc->rl_flags & RL_FLAG_WAIT_TXPOLL) != 0) { for (i = RL_TIMEOUT; i > 0; i--) { if ((CSR_READ_1(sc, sc->rl_txstart) & @@ -3830,6 +3850,11 @@ re_setwol(struct rl_softc *sc) CSR_READ_1(sc, RL_GPIO) & ~0x01); } if ((ifp->if_capenable & IFCAP_WOL) != 0) { + if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) { + /* Disable RXDV gate. */ + CSR_WRITE_4(sc, RL_MISC, CSR_READ_4(sc, RL_MISC) & + ~0x00080000); + } re_set_rxmode(sc); if ((sc->rl_flags & RL_FLAG_WOL_MANLINK) != 0) re_set_linkspeed(sc); @@ -3942,7 +3967,6 @@ re_add_sysctls(struct rl_softc *sc) sc->rl_int_rx_mod = RL_TIMER_DEFAULT; } } - } static int diff --git a/sys/dev/rl/if_rl.c b/sys/dev/rl/if_rl.c new file mode 100644 index 0000000..2ff1310 --- /dev/null +++ b/sys/dev/rl/if_rl.c @@ -0,0 +1,2126 @@ +/*- + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * RealTek 8129/8139 PCI NIC driver + * + * Supports several extremely cheap PCI 10/100 adapters based on + * the RealTek chipset. Datasheets can be obtained from + * www.realtek.com.tw. + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ +/* + * The RealTek 8139 PCI NIC redefines the meaning of 'low end.' This is + * probably the worst PCI ethernet controller ever made, with the possible + * exception of the FEAST chip made by SMC. The 8139 supports bus-master + * DMA, but it has a terrible interface that nullifies any performance + * gains that bus-master DMA usually offers. + * + * For transmission, the chip offers a series of four TX descriptor + * registers. Each transmit frame must be in a contiguous buffer, aligned + * on a longword (32-bit) boundary. This means we almost always have to + * do mbuf copies in order to transmit a frame, except in the unlikely + * case where a) the packet fits into a single mbuf, and b) the packet + * is 32-bit aligned within the mbuf's data area. The presence of only + * four descriptor registers means that we can never have more than four + * packets queued for transmission at any one time. + * + * Reception is not much better. The driver has to allocate a single large + * buffer area (up to 64K in size) into which the chip will DMA received + * frames. Because we don't know where within this region received packets + * will begin or end, we have no choice but to copy data from the buffer + * area into mbufs in order to pass the packets up to the higher protocol + * levels. + * + * It's impossible given this rotten design to really achieve decent + * performance at 100Mbps, unless you happen to have a 400Mhz PII or + * some equally overmuscled CPU to drive it. + * + * On the bright side, the 8139 does have a built-in PHY, although + * rather than using an MDIO serial interface like most other NICs, the + * PHY registers are directly accessible through the 8139's register + * space. The 8139 supports autonegotiation, as well as a 64-bit multicast + * filter. + * + * The 8129 chip is an older version of the 8139 that uses an external PHY + * chip. The 8129 has a serial MDIO interface for accessing the MII where + * the 8139 lets you directly access the on-board PHY registers. We need + * to select which interface to use depending on the chip type. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <net/bpf.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/mii_bitbang.h> +#include <dev/mii/miivar.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +MODULE_DEPEND(rl, pci, 1, 1, 1); +MODULE_DEPEND(rl, ether, 1, 1, 1); +MODULE_DEPEND(rl, miibus, 1, 1, 1); + +/* "device miibus" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#include <dev/rl/if_rlreg.h> + +/* + * Various supported device vendors/types and their names. + */ +static const struct rl_type rl_devs[] = { + { RT_VENDORID, RT_DEVICEID_8129, RL_8129, + "RealTek 8129 10/100BaseTX" }, + { RT_VENDORID, RT_DEVICEID_8139, RL_8139, + "RealTek 8139 10/100BaseTX" }, + { RT_VENDORID, RT_DEVICEID_8139D, RL_8139, + "RealTek 8139 10/100BaseTX" }, + { RT_VENDORID, RT_DEVICEID_8138, RL_8139, + "RealTek 8139 10/100BaseTX CardBus" }, + { RT_VENDORID, RT_DEVICEID_8100, RL_8139, + "RealTek 8100 10/100BaseTX" }, + { ACCTON_VENDORID, ACCTON_DEVICEID_5030, RL_8139, + "Accton MPX 5030/5038 10/100BaseTX" }, + { DELTA_VENDORID, DELTA_DEVICEID_8139, RL_8139, + "Delta Electronics 8139 10/100BaseTX" }, + { ADDTRON_VENDORID, ADDTRON_DEVICEID_8139, RL_8139, + "Addtron Technology 8139 10/100BaseTX" }, + { DLINK_VENDORID, DLINK_DEVICEID_520TX_REVC1, RL_8139, + "D-Link DFE-520TX (rev. C1) 10/100BaseTX" }, + { DLINK_VENDORID, DLINK_DEVICEID_530TXPLUS, RL_8139, + "D-Link DFE-530TX+ 10/100BaseTX" }, + { DLINK_VENDORID, DLINK_DEVICEID_690TXD, RL_8139, + "D-Link DFE-690TXD 10/100BaseTX" }, + { NORTEL_VENDORID, ACCTON_DEVICEID_5030, RL_8139, + "Nortel Networks 10/100BaseTX" }, + { COREGA_VENDORID, COREGA_DEVICEID_FETHERCBTXD, RL_8139, + "Corega FEther CB-TXD" }, + { COREGA_VENDORID, COREGA_DEVICEID_FETHERIICBTXD, RL_8139, + "Corega FEtherII CB-TXD" }, + { PEPPERCON_VENDORID, PEPPERCON_DEVICEID_ROLF, RL_8139, + "Peppercon AG ROL-F" }, + { PLANEX_VENDORID, PLANEX_DEVICEID_FNW3603TX, RL_8139, + "Planex FNW-3603-TX" }, + { PLANEX_VENDORID, PLANEX_DEVICEID_FNW3800TX, RL_8139, + "Planex FNW-3800-TX" }, + { CP_VENDORID, RT_DEVICEID_8139, RL_8139, + "Compaq HNE-300" }, + { LEVEL1_VENDORID, LEVEL1_DEVICEID_FPC0106TX, RL_8139, + "LevelOne FPC-0106TX" }, + { EDIMAX_VENDORID, EDIMAX_DEVICEID_EP4103DL, RL_8139, + "Edimax EP-4103DL CardBus" } +}; + +static int rl_attach(device_t); +static int rl_detach(device_t); +static void rl_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static int rl_dma_alloc(struct rl_softc *); +static void rl_dma_free(struct rl_softc *); +static void rl_eeprom_putbyte(struct rl_softc *, int); +static void rl_eeprom_getword(struct rl_softc *, int, uint16_t *); +static int rl_encap(struct rl_softc *, struct mbuf **); +static int rl_list_tx_init(struct rl_softc *); +static int rl_list_rx_init(struct rl_softc *); +static int rl_ifmedia_upd(struct ifnet *); +static void rl_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int rl_ioctl(struct ifnet *, u_long, caddr_t); +static void rl_intr(void *); +static void rl_init(void *); +static void rl_init_locked(struct rl_softc *sc); +static int rl_miibus_readreg(device_t, int, int); +static void rl_miibus_statchg(device_t); +static int rl_miibus_writereg(device_t, int, int, int); +#ifdef DEVICE_POLLING +static int rl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count); +static int rl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count); +#endif +static int rl_probe(device_t); +static void rl_read_eeprom(struct rl_softc *, uint8_t *, int, int, int); +static void rl_reset(struct rl_softc *); +static int rl_resume(device_t); +static int rl_rxeof(struct rl_softc *); +static void rl_rxfilter(struct rl_softc *); +static int rl_shutdown(device_t); +static void rl_start(struct ifnet *); +static void rl_start_locked(struct ifnet *); +static void rl_stop(struct rl_softc *); +static int rl_suspend(device_t); +static void rl_tick(void *); +static void rl_txeof(struct rl_softc *); +static void rl_watchdog(struct rl_softc *); +static void rl_setwol(struct rl_softc *); +static void rl_clrwol(struct rl_softc *); + +/* + * MII bit-bang glue + */ +static uint32_t rl_mii_bitbang_read(device_t); +static void rl_mii_bitbang_write(device_t, uint32_t); + +static const struct mii_bitbang_ops rl_mii_bitbang_ops = { + rl_mii_bitbang_read, + rl_mii_bitbang_write, + { + RL_MII_DATAOUT, /* MII_BIT_MDO */ + RL_MII_DATAIN, /* MII_BIT_MDI */ + RL_MII_CLK, /* MII_BIT_MDC */ + RL_MII_DIR, /* MII_BIT_DIR_HOST_PHY */ + 0, /* MII_BIT_DIR_PHY_HOST */ + } +}; + +static device_method_t rl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rl_probe), + DEVMETHOD(device_attach, rl_attach), + DEVMETHOD(device_detach, rl_detach), + DEVMETHOD(device_suspend, rl_suspend), + DEVMETHOD(device_resume, rl_resume), + DEVMETHOD(device_shutdown, rl_shutdown), + + /* MII interface */ + DEVMETHOD(miibus_readreg, rl_miibus_readreg), + DEVMETHOD(miibus_writereg, rl_miibus_writereg), + DEVMETHOD(miibus_statchg, rl_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t rl_driver = { + "rl", + rl_methods, + sizeof(struct rl_softc) +}; + +static devclass_t rl_devclass; + +DRIVER_MODULE(rl, pci, rl_driver, rl_devclass, 0, 0); +DRIVER_MODULE(rl, cardbus, rl_driver, rl_devclass, 0, 0); +DRIVER_MODULE(miibus, rl, miibus_driver, miibus_devclass, 0, 0); + +#define EE_SET(x) \ + CSR_WRITE_1(sc, RL_EECMD, \ + CSR_READ_1(sc, RL_EECMD) | x) + +#define EE_CLR(x) \ + CSR_WRITE_1(sc, RL_EECMD, \ + CSR_READ_1(sc, RL_EECMD) & ~x) + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void +rl_eeprom_putbyte(struct rl_softc *sc, int addr) +{ + register int d, i; + + d = addr | sc->rl_eecmd_read; + + /* + * Feed in each bit and strobe the clock. + */ + for (i = 0x400; i; i >>= 1) { + if (d & i) { + EE_SET(RL_EE_DATAIN); + } else { + EE_CLR(RL_EE_DATAIN); + } + DELAY(100); + EE_SET(RL_EE_CLK); + DELAY(150); + EE_CLR(RL_EE_CLK); + DELAY(100); + } +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void +rl_eeprom_getword(struct rl_softc *sc, int addr, uint16_t *dest) +{ + register int i; + uint16_t word = 0; + + /* Enter EEPROM access mode. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); + + /* + * Send address of word we want to read. + */ + rl_eeprom_putbyte(sc, addr); + + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + EE_SET(RL_EE_CLK); + DELAY(100); + if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) + word |= i; + EE_CLR(RL_EE_CLK); + DELAY(100); + } + + /* Turn off EEPROM access mode. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); + + *dest = word; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void +rl_read_eeprom(struct rl_softc *sc, uint8_t *dest, int off, int cnt, int swap) +{ + int i; + uint16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + rl_eeprom_getword(sc, off + i, &word); + ptr = (uint16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } +} + +/* + * Read the MII serial port for the MII bit-bang module. + */ +static uint32_t +rl_mii_bitbang_read(device_t dev) +{ + struct rl_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + val = CSR_READ_1(sc, RL_MII); + CSR_BARRIER(sc, RL_MII, 1, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + + return (val); +} + +/* + * Write the MII serial port for the MII bit-bang module. + */ +static void +rl_mii_bitbang_write(device_t dev, uint32_t val) +{ + struct rl_softc *sc; + + sc = device_get_softc(dev); + + CSR_WRITE_1(sc, RL_MII, val); + CSR_BARRIER(sc, RL_MII, 1, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); +} + +static int +rl_miibus_readreg(device_t dev, int phy, int reg) +{ + struct rl_softc *sc; + uint16_t rl8139_reg; + + sc = device_get_softc(dev); + + if (sc->rl_type == RL_8139) { + switch (reg) { + case MII_BMCR: + rl8139_reg = RL_BMCR; + break; + case MII_BMSR: + rl8139_reg = RL_BMSR; + break; + case MII_ANAR: + rl8139_reg = RL_ANAR; + break; + case MII_ANER: + rl8139_reg = RL_ANER; + break; + case MII_ANLPAR: + rl8139_reg = RL_LPAR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + return (0); + /* + * Allow the rlphy driver to read the media status + * register. If we have a link partner which does not + * support NWAY, this is the register which will tell + * us the results of parallel detection. + */ + case RL_MEDIASTAT: + return (CSR_READ_1(sc, RL_MEDIASTAT)); + default: + device_printf(sc->rl_dev, "bad phy register\n"); + return (0); + } + return (CSR_READ_2(sc, rl8139_reg)); + } + + return (mii_bitbang_readreg(dev, &rl_mii_bitbang_ops, phy, reg)); +} + +static int +rl_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct rl_softc *sc; + uint16_t rl8139_reg; + + sc = device_get_softc(dev); + + if (sc->rl_type == RL_8139) { + switch (reg) { + case MII_BMCR: + rl8139_reg = RL_BMCR; + break; + case MII_BMSR: + rl8139_reg = RL_BMSR; + break; + case MII_ANAR: + rl8139_reg = RL_ANAR; + break; + case MII_ANER: + rl8139_reg = RL_ANER; + break; + case MII_ANLPAR: + rl8139_reg = RL_LPAR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + return (0); + break; + default: + device_printf(sc->rl_dev, "bad phy register\n"); + return (0); + } + CSR_WRITE_2(sc, rl8139_reg, data); + return (0); + } + + mii_bitbang_writereg(dev, &rl_mii_bitbang_ops, phy, reg, data); + + return (0); +} + +static void +rl_miibus_statchg(device_t dev) +{ + struct rl_softc *sc; + struct ifnet *ifp; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->rl_miibus); + ifp = sc->rl_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + sc->rl_flags &= ~RL_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->rl_flags |= RL_FLAG_LINK; + break; + default: + break; + } + } + /* + * RealTek controllers do not provide any interface to + * Tx/Rx MACs for resolved speed, duplex and flow-control + * parameters. + */ +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void +rl_rxfilter(struct rl_softc *sc) +{ + struct ifnet *ifp = sc->rl_ifp; + int h = 0; + uint32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + uint32_t rxfilt; + + RL_LOCK_ASSERT(sc); + + rxfilt = CSR_READ_4(sc, RL_RXCFG); + rxfilt &= ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_BROAD | + RL_RXCFG_RX_MULTI); + /* Always accept frames destined for this host. */ + rxfilt |= RL_RXCFG_RX_INDIV; + /* Set capture broadcast bit to capture broadcast frames. */ + if (ifp->if_flags & IFF_BROADCAST) + rxfilt |= RL_RXCFG_RX_BROAD; + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxfilt |= RL_RXCFG_RX_MULTI; + if (ifp->if_flags & IFF_PROMISC) + rxfilt |= RL_RXCFG_RX_ALLPHYS; + hashes[0] = 0xFFFFFFFF; + hashes[1] = 0xFFFFFFFF; + } else { + /* Now program new ones. */ + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + if_maddr_runlock(ifp); + if (hashes[0] != 0 || hashes[1] != 0) + rxfilt |= RL_RXCFG_RX_MULTI; + } + + CSR_WRITE_4(sc, RL_MAR0, hashes[0]); + CSR_WRITE_4(sc, RL_MAR4, hashes[1]); + CSR_WRITE_4(sc, RL_RXCFG, rxfilt); +} + +static void +rl_reset(struct rl_softc *sc) +{ + register int i; + + RL_LOCK_ASSERT(sc); + + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); + + for (i = 0; i < RL_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) + break; + } + if (i == RL_TIMEOUT) + device_printf(sc->rl_dev, "reset never completed!\n"); +} + +/* + * Probe for a RealTek 8129/8139 chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int +rl_probe(device_t dev) +{ + const struct rl_type *t; + uint16_t devid, revid, vendor; + int i; + + vendor = pci_get_vendor(dev); + devid = pci_get_device(dev); + revid = pci_get_revid(dev); + + if (vendor == RT_VENDORID && devid == RT_DEVICEID_8139) { + if (revid == 0x20) { + /* 8139C+, let re(4) take care of this device. */ + return (ENXIO); + } + } + t = rl_devs; + for (i = 0; i < sizeof(rl_devs) / sizeof(rl_devs[0]); i++, t++) { + if (vendor == t->rl_vid && devid == t->rl_did) { + device_set_desc(dev, t->rl_name); + return (BUS_PROBE_DEFAULT); + } + } + + return (ENXIO); +} + +struct rl_dmamap_arg { + bus_addr_t rl_busaddr; +}; + +static void +rl_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct rl_dmamap_arg *ctx; + + if (error != 0) + return; + + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + ctx = (struct rl_dmamap_arg *)arg; + ctx->rl_busaddr = segs[0].ds_addr; +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +rl_attach(device_t dev) +{ + uint8_t eaddr[ETHER_ADDR_LEN]; + uint16_t as[3]; + struct ifnet *ifp; + struct rl_softc *sc; + const struct rl_type *t; + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *children; + int error = 0, hwrev, i, phy, pmc, rid; + int prefer_iomap, unit; + uint16_t rl_did = 0; + char tn[32]; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + sc->rl_dev = dev; + + sc->rl_twister_enable = 0; + snprintf(tn, sizeof(tn), "dev.rl.%d.twister_enable", unit); + TUNABLE_INT_FETCH(tn, &sc->rl_twister_enable); + ctx = device_get_sysctl_ctx(sc->rl_dev); + children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev)); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, "twister_enable", CTLFLAG_RD, + &sc->rl_twister_enable, 0, ""); + + mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->rl_stat_callout, &sc->rl_mtx, 0); + + pci_enable_busmaster(dev); + + + /* + * Map control/status registers. + * 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. + */ + prefer_iomap = 1; + snprintf(tn, sizeof(tn), "dev.rl.%d.prefer_iomap", unit); + TUNABLE_INT_FETCH(tn, &prefer_iomap); + if (prefer_iomap) { + sc->rl_res_id = PCIR_BAR(0); + sc->rl_res_type = SYS_RES_IOPORT; + sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type, + &sc->rl_res_id, RF_ACTIVE); + } + if (prefer_iomap == 0 || sc->rl_res == NULL) { + sc->rl_res_id = PCIR_BAR(1); + sc->rl_res_type = SYS_RES_MEMORY; + sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type, + &sc->rl_res_id, RF_ACTIVE); + } + if (sc->rl_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + error = ENXIO; + goto fail; + } + +#ifdef notdef + /* + * Detect the Realtek 8139B. For some reason, this chip is very + * unstable when left to autoselect the media + * The best workaround is to set the device to the required + * media type or to set it to the 10 Meg speed. + */ + if ((rman_get_end(sc->rl_res) - rman_get_start(sc->rl_res)) == 0xFF) + device_printf(dev, +"Realtek 8139B detected. Warning, this may be unstable in autoselect mode\n"); +#endif + + sc->rl_btag = rman_get_bustag(sc->rl_res); + sc->rl_bhandle = rman_get_bushandle(sc->rl_res); + + /* Allocate interrupt */ + rid = 0; + sc->rl_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->rl_irq[0] == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } + + sc->rl_cfg0 = RL_8139_CFG0; + sc->rl_cfg1 = RL_8139_CFG1; + sc->rl_cfg2 = 0; + sc->rl_cfg3 = RL_8139_CFG3; + sc->rl_cfg4 = RL_8139_CFG4; + sc->rl_cfg5 = RL_8139_CFG5; + + /* + * Reset the adapter. Only take the lock here as it's needed in + * order to call rl_reset(). + */ + RL_LOCK(sc); + rl_reset(sc); + RL_UNLOCK(sc); + + sc->rl_eecmd_read = RL_EECMD_READ_6BIT; + rl_read_eeprom(sc, (uint8_t *)&rl_did, 0, 1, 0); + if (rl_did != 0x8129) + sc->rl_eecmd_read = RL_EECMD_READ_8BIT; + + /* + * Get station address from the EEPROM. + */ + rl_read_eeprom(sc, (uint8_t *)as, RL_EE_EADDR, 3, 0); + for (i = 0; i < 3; i++) { + eaddr[(i * 2) + 0] = as[i] & 0xff; + eaddr[(i * 2) + 1] = as[i] >> 8; + } + + /* + * Now read the exact device type from the EEPROM to find + * out if it's an 8129 or 8139. + */ + rl_read_eeprom(sc, (uint8_t *)&rl_did, RL_EE_PCI_DID, 1, 0); + + t = rl_devs; + sc->rl_type = 0; + while(t->rl_name != NULL) { + if (rl_did == t->rl_did) { + sc->rl_type = t->rl_basetype; + break; + } + t++; + } + + if (sc->rl_type == 0) { + device_printf(dev, "unknown device ID: %x assuming 8139\n", + rl_did); + sc->rl_type = RL_8139; + /* + * Read RL_IDR register to get ethernet address as accessing + * EEPROM may not extract correct address. + */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + eaddr[i] = CSR_READ_1(sc, RL_IDR0 + i); + } + + if ((error = rl_dma_alloc(sc)) != 0) + goto fail; + + ifp = sc->rl_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not if_alloc()\n"); + error = ENOSPC; + goto fail; + } + +#define RL_PHYAD_INTERNAL 0 + + /* Do MII setup */ + phy = MII_PHY_ANY; + if (sc->rl_type == RL_8139) + phy = RL_PHYAD_INTERNAL; + error = mii_attach(dev, &sc->rl_miibus, ifp, rl_ifmedia_upd, + rl_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + if (error != 0) { + device_printf(dev, "attaching PHYs failed\n"); + goto fail; + } + + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = rl_ioctl; + ifp->if_start = rl_start; + ifp->if_init = rl_init; + ifp->if_capabilities = IFCAP_VLAN_MTU; + /* Check WOL for RTL8139B or newer controllers. */ + if (sc->rl_type == RL_8139 && + pci_find_cap(sc->rl_dev, PCIY_PMG, &pmc) == 0) { + hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV; + switch (hwrev) { + case RL_HWREV_8139B: + case RL_HWREV_8130: + case RL_HWREV_8139C: + case RL_HWREV_8139D: + case RL_HWREV_8101: + case RL_HWREV_8100: + ifp->if_capabilities |= IFCAP_WOL; + /* Disable WOL. */ + rl_clrwol(sc); + break; + default: + break; + } + } + ifp->if_capenable = ifp->if_capabilities; + ifp->if_capenable &= ~(IFCAP_WOL_UCAST | IFCAP_WOL_MCAST); +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, eaddr); + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE, + NULL, rl_intr, sc, &sc->rl_intrhand[0]); + if (error) { + device_printf(sc->rl_dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + } + +fail: + if (error) + rl_detach(dev); + + return (error); +} + +/* + * Shutdown hardware and free up resources. This can be called any + * time after the mutex has been initialized. It is called in both + * the error case in attach and the normal detach case so it needs + * to be careful about only freeing resources that have actually been + * allocated. + */ +static int +rl_detach(device_t dev) +{ + struct rl_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = sc->rl_ifp; + + KASSERT(mtx_initialized(&sc->rl_mtx), ("rl mutex not initialized")); + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(ifp); +#endif + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + RL_LOCK(sc); + rl_stop(sc); + RL_UNLOCK(sc); + callout_drain(&sc->rl_stat_callout); + ether_ifdetach(ifp); + } +#if 0 + sc->suspended = 1; +#endif + if (sc->rl_miibus) + device_delete_child(dev, sc->rl_miibus); + bus_generic_detach(dev); + + if (sc->rl_intrhand[0]) + bus_teardown_intr(dev, sc->rl_irq[0], sc->rl_intrhand[0]); + if (sc->rl_irq[0]) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq[0]); + if (sc->rl_res) + bus_release_resource(dev, sc->rl_res_type, sc->rl_res_id, + sc->rl_res); + + if (ifp) + if_free(ifp); + + rl_dma_free(sc); + + mtx_destroy(&sc->rl_mtx); + + return (0); +} + +static int +rl_dma_alloc(struct rl_softc *sc) +{ + struct rl_dmamap_arg ctx; + int error, i; + + /* + * Allocate the parent bus DMA tag appropriate for PCI. + */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->rl_dev), /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->rl_parent_tag); + if (error) { + device_printf(sc->rl_dev, + "failed to create parent DMA tag.\n"); + goto fail; + } + /* Create DMA tag for Rx memory block. */ + error = bus_dma_tag_create(sc->rl_parent_tag, /* parent */ + RL_RX_8139_BUF_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, 1, /* maxsize,nsegments */ + RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->rl_cdata.rl_rx_tag); + if (error) { + device_printf(sc->rl_dev, + "failed to create Rx memory block DMA tag.\n"); + goto fail; + } + /* Create DMA tag for Tx buffer. */ + error = bus_dma_tag_create(sc->rl_parent_tag, /* parent */ + RL_TX_8139_BUF_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, 1, /* maxsize, nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->rl_cdata.rl_tx_tag); + if (error) { + device_printf(sc->rl_dev, "failed to create Tx DMA tag.\n"); + goto fail; + } + + /* + * Allocate DMA'able memory and load DMA map for Rx memory block. + */ + error = bus_dmamem_alloc(sc->rl_cdata.rl_rx_tag, + (void **)&sc->rl_cdata.rl_rx_buf, BUS_DMA_WAITOK | + BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rl_cdata.rl_rx_dmamap); + if (error != 0) { + device_printf(sc->rl_dev, + "failed to allocate Rx DMA memory block.\n"); + goto fail; + } + ctx.rl_busaddr = 0; + error = bus_dmamap_load(sc->rl_cdata.rl_rx_tag, + sc->rl_cdata.rl_rx_dmamap, sc->rl_cdata.rl_rx_buf, + RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, rl_dmamap_cb, &ctx, + BUS_DMA_NOWAIT); + if (error != 0 || ctx.rl_busaddr == 0) { + device_printf(sc->rl_dev, + "could not load Rx DMA memory block.\n"); + goto fail; + } + sc->rl_cdata.rl_rx_buf_paddr = ctx.rl_busaddr; + + /* Create DMA maps for Tx buffers. */ + for (i = 0; i < RL_TX_LIST_CNT; i++) { + sc->rl_cdata.rl_tx_chain[i] = NULL; + sc->rl_cdata.rl_tx_dmamap[i] = NULL; + error = bus_dmamap_create(sc->rl_cdata.rl_tx_tag, 0, + &sc->rl_cdata.rl_tx_dmamap[i]); + if (error != 0) { + device_printf(sc->rl_dev, + "could not create Tx dmamap.\n"); + goto fail; + } + } + + /* Leave a few bytes before the start of the RX ring buffer. */ + sc->rl_cdata.rl_rx_buf_ptr = sc->rl_cdata.rl_rx_buf; + sc->rl_cdata.rl_rx_buf += RL_RX_8139_BUF_RESERVE; + +fail: + return (error); +} + +static void +rl_dma_free(struct rl_softc *sc) +{ + int i; + + /* Rx memory block. */ + if (sc->rl_cdata.rl_rx_tag != NULL) { + if (sc->rl_cdata.rl_rx_dmamap != NULL) + bus_dmamap_unload(sc->rl_cdata.rl_rx_tag, + sc->rl_cdata.rl_rx_dmamap); + if (sc->rl_cdata.rl_rx_dmamap != NULL && + sc->rl_cdata.rl_rx_buf_ptr != NULL) + bus_dmamem_free(sc->rl_cdata.rl_rx_tag, + sc->rl_cdata.rl_rx_buf_ptr, + sc->rl_cdata.rl_rx_dmamap); + sc->rl_cdata.rl_rx_buf_ptr = NULL; + sc->rl_cdata.rl_rx_buf = NULL; + sc->rl_cdata.rl_rx_dmamap = NULL; + bus_dma_tag_destroy(sc->rl_cdata.rl_rx_tag); + sc->rl_cdata.rl_tx_tag = NULL; + } + + /* Tx buffers. */ + if (sc->rl_cdata.rl_tx_tag != NULL) { + for (i = 0; i < RL_TX_LIST_CNT; i++) { + if (sc->rl_cdata.rl_tx_dmamap[i] != NULL) { + bus_dmamap_destroy( + sc->rl_cdata.rl_tx_tag, + sc->rl_cdata.rl_tx_dmamap[i]); + sc->rl_cdata.rl_tx_dmamap[i] = NULL; + } + } + bus_dma_tag_destroy(sc->rl_cdata.rl_tx_tag); + sc->rl_cdata.rl_tx_tag = NULL; + } + + if (sc->rl_parent_tag != NULL) { + bus_dma_tag_destroy(sc->rl_parent_tag); + sc->rl_parent_tag = NULL; + } +} + +/* + * Initialize the transmit descriptors. + */ +static int +rl_list_tx_init(struct rl_softc *sc) +{ + struct rl_chain_data *cd; + int i; + + RL_LOCK_ASSERT(sc); + + cd = &sc->rl_cdata; + for (i = 0; i < RL_TX_LIST_CNT; i++) { + cd->rl_tx_chain[i] = NULL; + CSR_WRITE_4(sc, + RL_TXADDR0 + (i * sizeof(uint32_t)), 0x0000000); + } + + sc->rl_cdata.cur_tx = 0; + sc->rl_cdata.last_tx = 0; + + return (0); +} + +static int +rl_list_rx_init(struct rl_softc *sc) +{ + + RL_LOCK_ASSERT(sc); + + bzero(sc->rl_cdata.rl_rx_buf_ptr, + RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ); + bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, sc->rl_cdata.rl_rx_dmamap, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + * + * You know there's something wrong with a PCI bus-master chip design + * when you have to use m_devget(). + * + * The receive operation is badly documented in the datasheet, so I'll + * attempt to document it here. The driver provides a buffer area and + * places its base address in the RX buffer start address register. + * The chip then begins copying frames into the RX buffer. Each frame + * is preceded by a 32-bit RX status word which specifies the length + * of the frame and certain other status bits. Each frame (starting with + * the status word) is also 32-bit aligned. The frame length is in the + * first 16 bits of the status word; the lower 15 bits correspond with + * the 'rx status register' mentioned in the datasheet. + * + * Note: to make the Alpha happy, the frame payload needs to be aligned + * on a 32-bit boundary. To achieve this, we pass RL_ETHER_ALIGN (2 bytes) + * as the offset argument to m_devget(). + */ +static int +rl_rxeof(struct rl_softc *sc) +{ + struct mbuf *m; + struct ifnet *ifp = sc->rl_ifp; + uint8_t *rxbufpos; + int total_len = 0; + int wrap = 0; + int rx_npkts = 0; + uint32_t rxstat; + uint16_t cur_rx; + uint16_t limit; + uint16_t max_bytes, rx_bytes = 0; + + RL_LOCK_ASSERT(sc); + + bus_dmamap_sync(sc->rl_cdata.rl_rx_tag, sc->rl_cdata.rl_rx_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + cur_rx = (CSR_READ_2(sc, RL_CURRXADDR) + 16) % RL_RXBUFLEN; + + /* Do not try to read past this point. */ + limit = CSR_READ_2(sc, RL_CURRXBUF) % RL_RXBUFLEN; + + if (limit < cur_rx) + max_bytes = (RL_RXBUFLEN - cur_rx) + limit; + else + max_bytes = limit - cur_rx; + + while((CSR_READ_1(sc, RL_COMMAND) & RL_CMD_EMPTY_RXBUF) == 0) { +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + if (sc->rxcycles <= 0) + break; + sc->rxcycles--; + } +#endif + rxbufpos = sc->rl_cdata.rl_rx_buf + cur_rx; + rxstat = le32toh(*(uint32_t *)rxbufpos); + + /* + * Here's a totally undocumented fact for you. When the + * RealTek chip is in the process of copying a packet into + * RAM for you, the length will be 0xfff0. If you spot a + * packet header with this value, you need to stop. The + * datasheet makes absolutely no mention of this and + * RealTek should be shot for this. + */ + total_len = rxstat >> 16; + if (total_len == RL_RXSTAT_UNFINISHED) + break; + + if (!(rxstat & RL_RXSTAT_RXOK) || + total_len < ETHER_MIN_LEN || + total_len > ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) { + ifp->if_ierrors++; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + rl_init_locked(sc); + return (rx_npkts); + } + + /* No errors; receive the packet. */ + rx_bytes += total_len + 4; + + /* + * XXX The RealTek chip includes the CRC with every + * received frame, and there's no way to turn this + * behavior off (at least, I can't find anything in + * the manual that explains how to do it) so we have + * to trim off the CRC manually. + */ + total_len -= ETHER_CRC_LEN; + + /* + * Avoid trying to read more bytes than we know + * the chip has prepared for us. + */ + if (rx_bytes > max_bytes) + break; + + rxbufpos = sc->rl_cdata.rl_rx_buf + + ((cur_rx + sizeof(uint32_t)) % RL_RXBUFLEN); + if (rxbufpos == (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN)) + rxbufpos = sc->rl_cdata.rl_rx_buf; + + wrap = (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN) - rxbufpos; + if (total_len > wrap) { + m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, + NULL); + if (m != NULL) + m_copyback(m, wrap, total_len - wrap, + sc->rl_cdata.rl_rx_buf); + cur_rx = (total_len - wrap + ETHER_CRC_LEN); + } else { + m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, + NULL); + cur_rx += total_len + 4 + ETHER_CRC_LEN; + } + + /* Round up to 32-bit boundary. */ + cur_rx = (cur_rx + 3) & ~3; + CSR_WRITE_2(sc, RL_CURRXADDR, cur_rx - 16); + + if (m == NULL) { + ifp->if_iqdrops++; + continue; + } + + ifp->if_ipackets++; + RL_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + RL_LOCK(sc); + rx_npkts++; + } + + /* No need to sync Rx memory block as we didn't modify it. */ + return (rx_npkts); +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +static void +rl_txeof(struct rl_softc *sc) +{ + struct ifnet *ifp = sc->rl_ifp; + uint32_t txstat; + + RL_LOCK_ASSERT(sc); + + /* + * Go through our tx list and free mbufs for those + * frames that have been uploaded. + */ + do { + if (RL_LAST_TXMBUF(sc) == NULL) + break; + txstat = CSR_READ_4(sc, RL_LAST_TXSTAT(sc)); + if (!(txstat & (RL_TXSTAT_TX_OK| + RL_TXSTAT_TX_UNDERRUN|RL_TXSTAT_TXABRT))) + break; + + ifp->if_collisions += (txstat & RL_TXSTAT_COLLCNT) >> 24; + + bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, RL_LAST_DMAMAP(sc), + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->rl_cdata.rl_tx_tag, RL_LAST_DMAMAP(sc)); + m_freem(RL_LAST_TXMBUF(sc)); + RL_LAST_TXMBUF(sc) = NULL; + /* + * If there was a transmit underrun, bump the TX threshold. + * Make sure not to overflow the 63 * 32byte we can address + * with the 6 available bit. + */ + if ((txstat & RL_TXSTAT_TX_UNDERRUN) && + (sc->rl_txthresh < 2016)) + sc->rl_txthresh += 32; + if (txstat & RL_TXSTAT_TX_OK) + ifp->if_opackets++; + else { + int oldthresh; + ifp->if_oerrors++; + if ((txstat & RL_TXSTAT_TXABRT) || + (txstat & RL_TXSTAT_OUTOFWIN)) + CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); + oldthresh = sc->rl_txthresh; + /* error recovery */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + rl_init_locked(sc); + /* restore original threshold */ + sc->rl_txthresh = oldthresh; + return; + } + RL_INC(sc->rl_cdata.last_tx); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + } while (sc->rl_cdata.last_tx != sc->rl_cdata.cur_tx); + + if (RL_LAST_TXMBUF(sc) == NULL) + sc->rl_watchdog_timer = 0; +} + +static void +rl_twister_update(struct rl_softc *sc) +{ + uint16_t linktest; + /* + * Table provided by RealTek (Kinston <shangh@realtek.com.tw>) for + * Linux driver. Values undocumented otherwise. + */ + static const uint32_t param[4][4] = { + {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} + }; + + /* + * Tune the so-called twister registers of the RTL8139. These + * are used to compensate for impedance mismatches. The + * method for tuning these registers is undocumented and the + * following procedure is collected from public sources. + */ + switch (sc->rl_twister) + { + case CHK_LINK: + /* + * If we have a sufficient link, then we can proceed in + * the state machine to the next stage. If not, then + * disable further tuning after writing sane defaults. + */ + if (CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_LINK_OK) { + CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_OFF_CMD); + sc->rl_twister = FIND_ROW; + } else { + CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_CMD); + CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST); + CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF); + CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF); + sc->rl_twister = DONE; + } + break; + case FIND_ROW: + /* + * Read how long it took to see the echo to find the tuning + * row to use. + */ + linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS; + if (linktest == RL_CSCFG_ROW3) + sc->rl_twist_row = 3; + else if (linktest == RL_CSCFG_ROW2) + sc->rl_twist_row = 2; + else if (linktest == RL_CSCFG_ROW1) + sc->rl_twist_row = 1; + else + sc->rl_twist_row = 0; + sc->rl_twist_col = 0; + sc->rl_twister = SET_PARAM; + break; + case SET_PARAM: + if (sc->rl_twist_col == 0) + CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET); + CSR_WRITE_4(sc, RL_PARA7C, + param[sc->rl_twist_row][sc->rl_twist_col]); + if (++sc->rl_twist_col == 4) { + if (sc->rl_twist_row == 3) + sc->rl_twister = RECHK_LONG; + else + sc->rl_twister = DONE; + } + break; + case RECHK_LONG: + /* + * For long cables, we have to double check to make sure we + * don't mistune. + */ + linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS; + if (linktest == RL_CSCFG_ROW3) + sc->rl_twister = DONE; + else { + CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_RETUNE); + sc->rl_twister = RETUNE; + } + break; + case RETUNE: + /* Retune for a shorter cable (try column 2) */ + CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST); + CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF); + CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF); + CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET); + sc->rl_twist_row--; + sc->rl_twist_col = 0; + sc->rl_twister = SET_PARAM; + break; + + case DONE: + break; + } + +} + +static void +rl_tick(void *xsc) +{ + struct rl_softc *sc = xsc; + struct mii_data *mii; + int ticks; + + RL_LOCK_ASSERT(sc); + /* + * If we're doing the twister cable calibration, then we need to defer + * watchdog timeouts. This is a no-op in normal operations, but + * can falsely trigger when the cable calibration takes a while and + * there was traffic ready to go when rl was started. + * + * We don't defer mii_tick since that updates the mii status, which + * helps the twister process, at least according to similar patches + * for the Linux driver I found online while doing the fixes. Worst + * case is a few extra mii reads during calibration. + */ + mii = device_get_softc(sc->rl_miibus); + mii_tick(mii); + if ((sc->rl_flags & RL_FLAG_LINK) == 0) + rl_miibus_statchg(sc->rl_dev); + if (sc->rl_twister_enable) { + if (sc->rl_twister == DONE) + rl_watchdog(sc); + else + rl_twister_update(sc); + if (sc->rl_twister == DONE) + ticks = hz; + else + ticks = hz / 10; + } else { + rl_watchdog(sc); + ticks = hz; + } + + callout_reset(&sc->rl_stat_callout, ticks, rl_tick, sc); +} + +#ifdef DEVICE_POLLING +static int +rl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct rl_softc *sc = ifp->if_softc; + int rx_npkts = 0; + + RL_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + rx_npkts = rl_poll_locked(ifp, cmd, count); + RL_UNLOCK(sc); + return (rx_npkts); +} + +static int +rl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct rl_softc *sc = ifp->if_softc; + int rx_npkts; + + RL_LOCK_ASSERT(sc); + + sc->rxcycles = count; + rx_npkts = rl_rxeof(sc); + rl_txeof(sc); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + rl_start_locked(ifp); + + if (cmd == POLL_AND_CHECK_STATUS) { + uint16_t status; + + /* We should also check the status register. */ + status = CSR_READ_2(sc, RL_ISR); + if (status == 0xffff) + return (rx_npkts); + if (status != 0) + CSR_WRITE_2(sc, RL_ISR, status); + + /* XXX We should check behaviour on receiver stalls. */ + + if (status & RL_ISR_SYSTEM_ERR) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + rl_init_locked(sc); + } + } + return (rx_npkts); +} +#endif /* DEVICE_POLLING */ + +static void +rl_intr(void *arg) +{ + struct rl_softc *sc = arg; + struct ifnet *ifp = sc->rl_ifp; + uint16_t status; + int count; + + RL_LOCK(sc); + + if (sc->suspended) + goto done_locked; + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + goto done_locked; +#endif + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done_locked2; + status = CSR_READ_2(sc, RL_ISR); + if (status == 0xffff || (status & RL_INTRS) == 0) + goto done_locked; + /* + * Ours, disable further interrupts. + */ + CSR_WRITE_2(sc, RL_IMR, 0); + for (count = 16; count > 0; count--) { + CSR_WRITE_2(sc, RL_ISR, status); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (status & (RL_ISR_RX_OK | RL_ISR_RX_ERR)) + rl_rxeof(sc); + if (status & (RL_ISR_TX_OK | RL_ISR_TX_ERR)) + rl_txeof(sc); + if (status & RL_ISR_SYSTEM_ERR) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + rl_init_locked(sc); + RL_UNLOCK(sc); + return; + } + } + status = CSR_READ_2(sc, RL_ISR); + /* If the card has gone away, the read returns 0xffff. */ + if (status == 0xffff || (status & RL_INTRS) == 0) + break; + } + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + rl_start_locked(ifp); + +done_locked2: + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + CSR_WRITE_2(sc, RL_IMR, RL_INTRS); +done_locked: + RL_UNLOCK(sc); +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +rl_encap(struct rl_softc *sc, struct mbuf **m_head) +{ + struct mbuf *m; + bus_dma_segment_t txsegs[1]; + int error, nsegs, padlen; + + RL_LOCK_ASSERT(sc); + + m = *m_head; + padlen = 0; + /* + * Hardware doesn't auto-pad, so we have to make sure + * pad short frames out to the minimum frame length. + */ + if (m->m_pkthdr.len < RL_MIN_FRAMELEN) + padlen = RL_MIN_FRAMELEN - m->m_pkthdr.len; + /* + * The RealTek is brain damaged and wants longword-aligned + * TX buffers, plus we can only have one fragment buffer + * per packet. We have to copy pretty much all the time. + */ + if (m->m_next != NULL || (mtod(m, uintptr_t) & 3) != 0 || + (padlen > 0 && M_TRAILINGSPACE(m) < padlen)) { + m = m_defrag(*m_head, M_NOWAIT); + if (m == NULL) { + m_freem(*m_head); + *m_head = NULL; + return (ENOMEM); + } + } + *m_head = m; + + if (padlen > 0) { + /* + * Make security-conscious people happy: zero out the + * bytes in the pad area, since we don't know what + * this mbuf cluster buffer's previous user might + * have left in it. + */ + bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); + m->m_pkthdr.len += padlen; + m->m_len = m->m_pkthdr.len; + } + + error = bus_dmamap_load_mbuf_sg(sc->rl_cdata.rl_tx_tag, + RL_CUR_DMAMAP(sc), m, txsegs, &nsegs, 0); + if (error != 0) + return (error); + if (nsegs == 0) { + m_freem(*m_head); + *m_head = NULL; + return (EIO); + } + + RL_CUR_TXMBUF(sc) = m; + bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, RL_CUR_DMAMAP(sc), + BUS_DMASYNC_PREWRITE); + CSR_WRITE_4(sc, RL_CUR_TXADDR(sc), RL_ADDR_LO(txsegs[0].ds_addr)); + + return (0); +} + +/* + * Main transmit routine. + */ +static void +rl_start(struct ifnet *ifp) +{ + struct rl_softc *sc = ifp->if_softc; + + RL_LOCK(sc); + rl_start_locked(ifp); + RL_UNLOCK(sc); +} + +static void +rl_start_locked(struct ifnet *ifp) +{ + struct rl_softc *sc = ifp->if_softc; + struct mbuf *m_head = NULL; + + RL_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) + return; + + while (RL_CUR_TXMBUF(sc) == NULL) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + + if (m_head == NULL) + break; + + if (rl_encap(sc, &m_head)) { + if (m_head == NULL) + break; + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + /* Pass a copy of this mbuf chain to the bpf subsystem. */ + BPF_MTAP(ifp, RL_CUR_TXMBUF(sc)); + + /* Transmit the frame. */ + CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc), + RL_TXTHRESH(sc->rl_txthresh) | + RL_CUR_TXMBUF(sc)->m_pkthdr.len); + + RL_INC(sc->rl_cdata.cur_tx); + + /* Set a timeout in case the chip goes out to lunch. */ + sc->rl_watchdog_timer = 5; + } + + /* + * We broke out of the loop because all our TX slots are + * full. Mark the NIC as busy until it drains some of the + * packets from the queue. + */ + if (RL_CUR_TXMBUF(sc) != NULL) + ifp->if_drv_flags |= IFF_DRV_OACTIVE; +} + +static void +rl_init(void *xsc) +{ + struct rl_softc *sc = xsc; + + RL_LOCK(sc); + rl_init_locked(sc); + RL_UNLOCK(sc); +} + +static void +rl_init_locked(struct rl_softc *sc) +{ + struct ifnet *ifp = sc->rl_ifp; + struct mii_data *mii; + uint32_t eaddr[2]; + + RL_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->rl_miibus); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + rl_stop(sc); + + rl_reset(sc); + if (sc->rl_twister_enable) { + /* + * Reset twister register tuning state. The twister + * registers and their tuning are undocumented, but + * are necessary to cope with bad links. rl_twister = + * DONE here will disable this entirely. + */ + sc->rl_twister = CHK_LINK; + } + + /* + * Init our MAC address. Even though the chipset + * documentation doesn't mention it, we need to enter "Config + * register write enable" mode to modify the ID registers. + */ + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG); + bzero(eaddr, sizeof(eaddr)); + bcopy(IF_LLADDR(sc->rl_ifp), eaddr, ETHER_ADDR_LEN); + CSR_WRITE_STREAM_4(sc, RL_IDR0, eaddr[0]); + CSR_WRITE_STREAM_4(sc, RL_IDR4, eaddr[1]); + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); + + /* Init the RX memory block pointer register. */ + CSR_WRITE_4(sc, RL_RXADDR, sc->rl_cdata.rl_rx_buf_paddr + + RL_RX_8139_BUF_RESERVE); + /* Init TX descriptors. */ + rl_list_tx_init(sc); + /* Init Rx memory block. */ + rl_list_rx_init(sc); + + /* + * Enable transmit and receive. + */ + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); + + /* + * Set the initial TX and RX configuration. + */ + CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); + CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG); + + /* Set RX filter. */ + rl_rxfilter(sc); + +#ifdef DEVICE_POLLING + /* Disable interrupts if we are polling. */ + if (ifp->if_capenable & IFCAP_POLLING) + CSR_WRITE_2(sc, RL_IMR, 0); + else +#endif + /* Enable interrupts. */ + CSR_WRITE_2(sc, RL_IMR, RL_INTRS); + + /* Set initial TX threshold */ + sc->rl_txthresh = RL_TX_THRESH_INIT; + + /* Start RX/TX process. */ + CSR_WRITE_4(sc, RL_MISSEDPKT, 0); + + /* Enable receiver and transmitter. */ + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); + + sc->rl_flags &= ~RL_FLAG_LINK; + mii_mediachg(mii); + + CSR_WRITE_1(sc, sc->rl_cfg1, RL_CFG1_DRVLOAD|RL_CFG1_FULLDUPLEX); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->rl_stat_callout, hz, rl_tick, sc); +} + +/* + * Set media options. + */ +static int +rl_ifmedia_upd(struct ifnet *ifp) +{ + struct rl_softc *sc = ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->rl_miibus); + + RL_LOCK(sc); + mii_mediachg(mii); + RL_UNLOCK(sc); + + return (0); +} + +/* + * Report current media status. + */ +static void +rl_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct rl_softc *sc = ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->rl_miibus); + + RL_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + RL_UNLOCK(sc); +} + +static int +rl_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct ifreq *ifr = (struct ifreq *)data; + struct mii_data *mii; + struct rl_softc *sc = ifp->if_softc; + int error = 0, mask; + + switch (command) { + case SIOCSIFFLAGS: + RL_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING && + ((ifp->if_flags ^ sc->rl_if_flags) & + (IFF_PROMISC | IFF_ALLMULTI))) + rl_rxfilter(sc); + else + rl_init_locked(sc); + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) + rl_stop(sc); + sc->rl_if_flags = ifp->if_flags; + RL_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + RL_LOCK(sc); + rl_rxfilter(sc); + RL_UNLOCK(sc); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->rl_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + case SIOCSIFCAP: + mask = ifr->ifr_reqcap ^ ifp->if_capenable; +#ifdef DEVICE_POLLING + if (ifr->ifr_reqcap & IFCAP_POLLING && + !(ifp->if_capenable & IFCAP_POLLING)) { + error = ether_poll_register(rl_poll, ifp); + if (error) + return(error); + RL_LOCK(sc); + /* Disable interrupts */ + CSR_WRITE_2(sc, RL_IMR, 0x0000); + ifp->if_capenable |= IFCAP_POLLING; + RL_UNLOCK(sc); + return (error); + + } + if (!(ifr->ifr_reqcap & IFCAP_POLLING) && + ifp->if_capenable & IFCAP_POLLING) { + error = ether_poll_deregister(ifp); + /* Enable interrupts. */ + RL_LOCK(sc); + CSR_WRITE_2(sc, RL_IMR, RL_INTRS); + ifp->if_capenable &= ~IFCAP_POLLING; + RL_UNLOCK(sc); + return (error); + } +#endif /* DEVICE_POLLING */ + if ((mask & IFCAP_WOL) != 0 && + (ifp->if_capabilities & IFCAP_WOL) != 0) { + if ((mask & IFCAP_WOL_UCAST) != 0) + ifp->if_capenable ^= IFCAP_WOL_UCAST; + if ((mask & IFCAP_WOL_MCAST) != 0) + ifp->if_capenable ^= IFCAP_WOL_MCAST; + if ((mask & IFCAP_WOL_MAGIC) != 0) + ifp->if_capenable ^= IFCAP_WOL_MAGIC; + } + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return (error); +} + +static void +rl_watchdog(struct rl_softc *sc) +{ + + RL_LOCK_ASSERT(sc); + + if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer >0) + return; + + device_printf(sc->rl_dev, "watchdog timeout\n"); + sc->rl_ifp->if_oerrors++; + + rl_txeof(sc); + rl_rxeof(sc); + sc->rl_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + rl_init_locked(sc); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +rl_stop(struct rl_softc *sc) +{ + register int i; + struct ifnet *ifp = sc->rl_ifp; + + RL_LOCK_ASSERT(sc); + + sc->rl_watchdog_timer = 0; + callout_stop(&sc->rl_stat_callout); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->rl_flags &= ~RL_FLAG_LINK; + + CSR_WRITE_1(sc, RL_COMMAND, 0x00); + CSR_WRITE_2(sc, RL_IMR, 0x0000); + for (i = 0; i < RL_TIMEOUT; i++) { + DELAY(10); + if ((CSR_READ_1(sc, RL_COMMAND) & + (RL_CMD_RX_ENB | RL_CMD_TX_ENB)) == 0) + break; + } + if (i == RL_TIMEOUT) + device_printf(sc->rl_dev, "Unable to stop Tx/Rx MAC\n"); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < RL_TX_LIST_CNT; i++) { + if (sc->rl_cdata.rl_tx_chain[i] != NULL) { + if (sc->rl_cdata.rl_tx_chain[i] != NULL) { + bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, + sc->rl_cdata.rl_tx_dmamap[i], + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->rl_cdata.rl_tx_tag, + sc->rl_cdata.rl_tx_dmamap[i]); + m_freem(sc->rl_cdata.rl_tx_chain[i]); + sc->rl_cdata.rl_tx_chain[i] = NULL; + } + CSR_WRITE_4(sc, RL_TXADDR0 + (i * sizeof(uint32_t)), + 0x0000000); + } + } +} + +/* + * Device suspend routine. Stop the interface and save some PCI + * settings in case the BIOS doesn't restore them properly on + * resume. + */ +static int +rl_suspend(device_t dev) +{ + struct rl_softc *sc; + + sc = device_get_softc(dev); + + RL_LOCK(sc); + rl_stop(sc); + rl_setwol(sc); + sc->suspended = 1; + RL_UNLOCK(sc); + + return (0); +} + +/* + * Device resume routine. Restore some PCI settings in case the BIOS + * doesn't, re-enable busmastering, and restart the interface if + * appropriate. + */ +static int +rl_resume(device_t dev) +{ + struct rl_softc *sc; + struct ifnet *ifp; + int pmc; + uint16_t pmstat; + + sc = device_get_softc(dev); + ifp = sc->rl_ifp; + + RL_LOCK(sc); + + if ((ifp->if_capabilities & IFCAP_WOL) != 0 && + pci_find_cap(sc->rl_dev, PCIY_PMG, &pmc) == 0) { + /* Disable PME and clear PME status. */ + pmstat = pci_read_config(sc->rl_dev, + pmc + PCIR_POWER_STATUS, 2); + if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) { + pmstat &= ~PCIM_PSTAT_PMEENABLE; + pci_write_config(sc->rl_dev, + pmc + PCIR_POWER_STATUS, pmstat, 2); + } + /* + * Clear WOL matching such that normal Rx filtering + * wouldn't interfere with WOL patterns. + */ + rl_clrwol(sc); + } + + /* reinitialize interface if necessary */ + if (ifp->if_flags & IFF_UP) + rl_init_locked(sc); + + sc->suspended = 0; + + RL_UNLOCK(sc); + + return (0); +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static int +rl_shutdown(device_t dev) +{ + struct rl_softc *sc; + + sc = device_get_softc(dev); + + RL_LOCK(sc); + rl_stop(sc); + /* + * Mark interface as down since otherwise we will panic if + * interrupt comes in later on, which can happen in some + * cases. + */ + sc->rl_ifp->if_flags &= ~IFF_UP; + rl_setwol(sc); + RL_UNLOCK(sc); + + return (0); +} + +static void +rl_setwol(struct rl_softc *sc) +{ + struct ifnet *ifp; + int pmc; + uint16_t pmstat; + uint8_t v; + + RL_LOCK_ASSERT(sc); + + ifp = sc->rl_ifp; + if ((ifp->if_capabilities & IFCAP_WOL) == 0) + return; + if (pci_find_cap(sc->rl_dev, PCIY_PMG, &pmc) != 0) + return; + + /* Enable config register write. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); + + /* Enable PME. */ + v = CSR_READ_1(sc, sc->rl_cfg1); + v &= ~RL_CFG1_PME; + if ((ifp->if_capenable & IFCAP_WOL) != 0) + v |= RL_CFG1_PME; + CSR_WRITE_1(sc, sc->rl_cfg1, v); + + v = CSR_READ_1(sc, sc->rl_cfg3); + v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC); + if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) + v |= RL_CFG3_WOL_MAGIC; + CSR_WRITE_1(sc, sc->rl_cfg3, v); + + v = CSR_READ_1(sc, sc->rl_cfg5); + v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST); + v &= ~RL_CFG5_WOL_LANWAKE; + if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0) + v |= RL_CFG5_WOL_UCAST; + if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0) + v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST; + if ((ifp->if_capenable & IFCAP_WOL) != 0) + v |= RL_CFG5_WOL_LANWAKE; + CSR_WRITE_1(sc, sc->rl_cfg5, v); + + /* Config register write done. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); + + /* Request PME if WOL is requested. */ + pmstat = pci_read_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, 2); + pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + if ((ifp->if_capenable & IFCAP_WOL) != 0) + pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + pci_write_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); +} + +static void +rl_clrwol(struct rl_softc *sc) +{ + struct ifnet *ifp; + uint8_t v; + + ifp = sc->rl_ifp; + if ((ifp->if_capabilities & IFCAP_WOL) == 0) + return; + + /* Enable config register write. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); + + v = CSR_READ_1(sc, sc->rl_cfg3); + v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC); + CSR_WRITE_1(sc, sc->rl_cfg3, v); + + /* Config register write done. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); + + v = CSR_READ_1(sc, sc->rl_cfg5); + v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST); + v &= ~RL_CFG5_WOL_LANWAKE; + CSR_WRITE_1(sc, sc->rl_cfg5, v); +} diff --git a/sys/dev/rl/if_rlreg.h b/sys/dev/rl/if_rlreg.h new file mode 100644 index 0000000..5a5c5f5 --- /dev/null +++ b/sys/dev/rl/if_rlreg.h @@ -0,0 +1,1160 @@ +/*- + * Copyright (c) 1997, 1998-2003 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * RealTek 8129/8139 register offsets + */ +#define RL_IDR0 0x0000 /* ID register 0 (station addr) */ +#define RL_IDR1 0x0001 /* Must use 32-bit accesses (?) */ +#define RL_IDR2 0x0002 +#define RL_IDR3 0x0003 +#define RL_IDR4 0x0004 +#define RL_IDR5 0x0005 + /* 0006-0007 reserved */ +#define RL_MAR0 0x0008 /* Multicast hash table */ +#define RL_MAR1 0x0009 +#define RL_MAR2 0x000A +#define RL_MAR3 0x000B +#define RL_MAR4 0x000C +#define RL_MAR5 0x000D +#define RL_MAR6 0x000E +#define RL_MAR7 0x000F + +#define RL_TXSTAT0 0x0010 /* status of TX descriptor 0 */ +#define RL_TXSTAT1 0x0014 /* status of TX descriptor 1 */ +#define RL_TXSTAT2 0x0018 /* status of TX descriptor 2 */ +#define RL_TXSTAT3 0x001C /* status of TX descriptor 3 */ + +#define RL_TXADDR0 0x0020 /* address of TX descriptor 0 */ +#define RL_TXADDR1 0x0024 /* address of TX descriptor 1 */ +#define RL_TXADDR2 0x0028 /* address of TX descriptor 2 */ +#define RL_TXADDR3 0x002C /* address of TX descriptor 3 */ + +#define RL_RXADDR 0x0030 /* RX ring start address */ +#define RL_RX_EARLY_BYTES 0x0034 /* RX early byte count */ +#define RL_RX_EARLY_STAT 0x0036 /* RX early status */ +#define RL_COMMAND 0x0037 /* command register */ +#define RL_CURRXADDR 0x0038 /* current address of packet read */ +#define RL_CURRXBUF 0x003A /* current RX buffer address */ +#define RL_IMR 0x003C /* interrupt mask register */ +#define RL_ISR 0x003E /* interrupt status register */ +#define RL_TXCFG 0x0040 /* transmit config */ +#define RL_RXCFG 0x0044 /* receive config */ +#define RL_TIMERCNT 0x0048 /* timer count register */ +#define RL_MISSEDPKT 0x004C /* missed packet counter */ +#define RL_EECMD 0x0050 /* EEPROM command register */ + +/* RTL8139/RTL8139C+ only */ +#define RL_8139_CFG0 0x0051 /* config register #0 */ +#define RL_8139_CFG1 0x0052 /* config register #1 */ +#define RL_8139_CFG3 0x0059 /* config register #3 */ +#define RL_8139_CFG4 0x005A /* config register #4 */ +#define RL_8139_CFG5 0x00D8 /* config register #5 */ + +#define RL_CFG0 0x0051 /* config register #0 */ +#define RL_CFG1 0x0052 /* config register #1 */ +#define RL_CFG2 0x0053 /* config register #2 */ +#define RL_CFG3 0x0054 /* config register #3 */ +#define RL_CFG4 0x0055 /* config register #4 */ +#define RL_CFG5 0x0056 /* config register #5 */ + /* 0057 reserved */ +#define RL_MEDIASTAT 0x0058 /* media status register (8139) */ + /* 0059-005A reserved */ +#define RL_MII 0x005A /* 8129 chip only */ +#define RL_HALTCLK 0x005B +#define RL_MULTIINTR 0x005C /* multiple interrupt */ +#define RL_PCIREV 0x005E /* PCI revision value */ + /* 005F reserved */ +#define RL_TXSTAT_ALL 0x0060 /* TX status of all descriptors */ + +/* Direct PHY access registers only available on 8139 */ +#define RL_BMCR 0x0062 /* PHY basic mode control */ +#define RL_BMSR 0x0064 /* PHY basic mode status */ +#define RL_ANAR 0x0066 /* PHY autoneg advert */ +#define RL_LPAR 0x0068 /* PHY link partner ability */ +#define RL_ANER 0x006A /* PHY autoneg expansion */ + +#define RL_DISCCNT 0x006C /* disconnect counter */ +#define RL_FALSECAR 0x006E /* false carrier counter */ +#define RL_NWAYTST 0x0070 /* NWAY test register */ +#define RL_RX_ER 0x0072 /* RX_ER counter */ +#define RL_CSCFG 0x0074 /* CS configuration register */ + +/* + * When operating in special C+ mode, some of the registers in an + * 8139C+ chip have different definitions. These are also used for + * the 8169 gigE chip. + */ +#define RL_DUMPSTATS_LO 0x0010 /* counter dump command register */ +#define RL_DUMPSTATS_HI 0x0014 /* counter dump command register */ +#define RL_TXLIST_ADDR_LO 0x0020 /* 64 bits, 256 byte alignment */ +#define RL_TXLIST_ADDR_HI 0x0024 /* 64 bits, 256 byte alignment */ +#define RL_TXLIST_ADDR_HPRIO_LO 0x0028 /* 64 bits, 256 byte alignment */ +#define RL_TXLIST_ADDR_HPRIO_HI 0x002C /* 64 bits, 256 byte alignment */ +#define RL_CFG2 0x0053 +#define RL_TIMERINT 0x0054 /* interrupt on timer expire */ +#define RL_TXSTART 0x00D9 /* 8 bits */ +#define RL_CPLUS_CMD 0x00E0 /* 16 bits */ +#define RL_RXLIST_ADDR_LO 0x00E4 /* 64 bits, 256 byte alignment */ +#define RL_RXLIST_ADDR_HI 0x00E8 /* 64 bits, 256 byte alignment */ +#define RL_EARLY_TX_THRESH 0x00EC /* 8 bits */ + +/* + * Registers specific to the 8169 gigE chip + */ +#define RL_GTXSTART 0x0038 /* 8 bits */ +#define RL_TIMERINT_8169 0x0058 /* different offset than 8139 */ +#define RL_PHYAR 0x0060 +#define RL_TBICSR 0x0064 +#define RL_TBI_ANAR 0x0068 +#define RL_TBI_LPAR 0x006A +#define RL_GMEDIASTAT 0x006C /* 8 bits */ +#define RL_MACDBG 0x006D /* 8 bits, 8168C SPIN2 only */ +#define RL_GPIO 0x006E /* 8 bits, 8168C SPIN2 only */ +#define RL_PMCH 0x006F /* 8 bits */ +#define RL_MAXRXPKTLEN 0x00DA /* 16 bits, chip multiplies by 8 */ +#define RL_INTRMOD 0x00E2 /* 16 bits */ +#define RL_MISC 0x00F0 + +/* + * TX config register bits + */ +#define RL_TXCFG_CLRABRT 0x00000001 /* retransmit aborted pkt */ +#define RL_TXCFG_MAXDMA 0x00000700 /* max DMA burst size */ +#define RL_TXCFG_QUEUE_EMPTY 0x00000800 /* 8168E-VL or higher */ +#define RL_TXCFG_CRCAPPEND 0x00010000 /* CRC append (0 = yes) */ +#define RL_TXCFG_LOOPBKTST 0x00060000 /* loopback test */ +#define RL_TXCFG_IFG2 0x00080000 /* 8169 only */ +#define RL_TXCFG_IFG 0x03000000 /* interframe gap */ +#define RL_TXCFG_HWREV 0x7CC00000 + +#define RL_LOOPTEST_OFF 0x00000000 +#define RL_LOOPTEST_ON 0x00020000 +#define RL_LOOPTEST_ON_CPLUS 0x00060000 + +/* Known revision codes. */ +#define RL_HWREV_8169 0x00000000 +#define RL_HWREV_8169S 0x00800000 +#define RL_HWREV_8110S 0x04000000 +#define RL_HWREV_8169_8110SB 0x10000000 +#define RL_HWREV_8169_8110SC 0x18000000 +#define RL_HWREV_8401E 0x24000000 +#define RL_HWREV_8102EL 0x24800000 +#define RL_HWREV_8102EL_SPIN1 0x24C00000 +#define RL_HWREV_8168D 0x28000000 +#define RL_HWREV_8168DP 0x28800000 +#define RL_HWREV_8168E 0x2C000000 +#define RL_HWREV_8168E_VL 0x2C800000 +#define RL_HWREV_8168B_SPIN1 0x30000000 +#define RL_HWREV_8100E 0x30800000 +#define RL_HWREV_8101E 0x34000000 +#define RL_HWREV_8102E 0x34800000 +#define RL_HWREV_8103E 0x34C00000 +#define RL_HWREV_8168B_SPIN2 0x38000000 +#define RL_HWREV_8168B_SPIN3 0x38400000 +#define RL_HWREV_8168C 0x3C000000 +#define RL_HWREV_8168C_SPIN2 0x3C400000 +#define RL_HWREV_8168CP 0x3C800000 +#define RL_HWREV_8105E 0x40800000 +#define RL_HWREV_8105E_SPIN1 0x40C00000 +#define RL_HWREV_8402 0x44000000 +#define RL_HWREV_8106E 0x44800000 +#define RL_HWREV_8168F 0x48000000 +#define RL_HWREV_8411 0x48800000 +#define RL_HWREV_8168G 0x4C000000 +#define RL_HWREV_8168EP 0x50000000 +#define RL_HWREV_8168GU 0x50800000 +#define RL_HWREV_8168H 0x54000000 +#define RL_HWREV_8411B 0x5C800000 +#define RL_HWREV_8139 0x60000000 +#define RL_HWREV_8139A 0x70000000 +#define RL_HWREV_8139AG 0x70800000 +#define RL_HWREV_8139B 0x78000000 +#define RL_HWREV_8130 0x7C000000 +#define RL_HWREV_8139C 0x74000000 +#define RL_HWREV_8139D 0x74400000 +#define RL_HWREV_8139CPLUS 0x74800000 +#define RL_HWREV_8101 0x74C00000 +#define RL_HWREV_8100 0x78800000 +#define RL_HWREV_8169_8110SBL 0x7CC00000 +#define RL_HWREV_8169_8110SCE 0x98000000 + +#define RL_TXDMA_16BYTES 0x00000000 +#define RL_TXDMA_32BYTES 0x00000100 +#define RL_TXDMA_64BYTES 0x00000200 +#define RL_TXDMA_128BYTES 0x00000300 +#define RL_TXDMA_256BYTES 0x00000400 +#define RL_TXDMA_512BYTES 0x00000500 +#define RL_TXDMA_1024BYTES 0x00000600 +#define RL_TXDMA_2048BYTES 0x00000700 + +/* + * Transmit descriptor status register bits. + */ +#define RL_TXSTAT_LENMASK 0x00001FFF +#define RL_TXSTAT_OWN 0x00002000 +#define RL_TXSTAT_TX_UNDERRUN 0x00004000 +#define RL_TXSTAT_TX_OK 0x00008000 +#define RL_TXSTAT_EARLY_THRESH 0x003F0000 +#define RL_TXSTAT_COLLCNT 0x0F000000 +#define RL_TXSTAT_CARR_HBEAT 0x10000000 +#define RL_TXSTAT_OUTOFWIN 0x20000000 +#define RL_TXSTAT_TXABRT 0x40000000 +#define RL_TXSTAT_CARRLOSS 0x80000000 + +/* + * Interrupt status register bits. + */ +#define RL_ISR_RX_OK 0x0001 +#define RL_ISR_RX_ERR 0x0002 +#define RL_ISR_TX_OK 0x0004 +#define RL_ISR_TX_ERR 0x0008 +#define RL_ISR_RX_OVERRUN 0x0010 +#define RL_ISR_PKT_UNDERRUN 0x0020 +#define RL_ISR_LINKCHG 0x0020 /* 8169 only */ +#define RL_ISR_FIFO_OFLOW 0x0040 /* 8139 only */ +#define RL_ISR_TX_DESC_UNAVAIL 0x0080 /* C+ only */ +#define RL_ISR_SWI 0x0100 /* C+ only */ +#define RL_ISR_CABLE_LEN_CHGD 0x2000 +#define RL_ISR_PCS_TIMEOUT 0x4000 /* 8129 only */ +#define RL_ISR_TIMEOUT_EXPIRED 0x4000 +#define RL_ISR_SYSTEM_ERR 0x8000 + +#define RL_INTRS \ + (RL_ISR_TX_OK|RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ + RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ + RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR) + +#ifdef RE_TX_MODERATION +#define RL_INTRS_CPLUS \ + (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ + RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ + RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED) +#else +#define RL_INTRS_CPLUS \ + (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR|RL_ISR_TX_OK| \ + RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ + RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED) +#endif + +/* + * Media status register. (8139 only) + */ +#define RL_MEDIASTAT_RXPAUSE 0x01 +#define RL_MEDIASTAT_TXPAUSE 0x02 +#define RL_MEDIASTAT_LINK 0x04 +#define RL_MEDIASTAT_SPEED10 0x08 +#define RL_MEDIASTAT_RXFLOWCTL 0x40 /* duplex mode */ +#define RL_MEDIASTAT_TXFLOWCTL 0x80 /* duplex mode */ + +/* + * Receive config register. + */ +#define RL_RXCFG_RX_ALLPHYS 0x00000001 /* accept all nodes */ +#define RL_RXCFG_RX_INDIV 0x00000002 /* match filter */ +#define RL_RXCFG_RX_MULTI 0x00000004 /* accept all multicast */ +#define RL_RXCFG_RX_BROAD 0x00000008 /* accept all broadcast */ +#define RL_RXCFG_RX_RUNT 0x00000010 +#define RL_RXCFG_RX_ERRPKT 0x00000020 +#define RL_RXCFG_WRAP 0x00000080 +#define RL_RXCFG_EARLYOFFV2 0x00000800 +#define RL_RXCFG_MAXDMA 0x00000700 +#define RL_RXCFG_BUFSZ 0x00001800 +#define RL_RXCFG_EARLYOFF 0x00003800 +#define RL_RXCFG_FIFOTHRESH 0x0000E000 +#define RL_RXCFG_EARLYTHRESH 0x07000000 + +#define RL_RXDMA_16BYTES 0x00000000 +#define RL_RXDMA_32BYTES 0x00000100 +#define RL_RXDMA_64BYTES 0x00000200 +#define RL_RXDMA_128BYTES 0x00000300 +#define RL_RXDMA_256BYTES 0x00000400 +#define RL_RXDMA_512BYTES 0x00000500 +#define RL_RXDMA_1024BYTES 0x00000600 +#define RL_RXDMA_UNLIMITED 0x00000700 + +#define RL_RXBUF_8 0x00000000 +#define RL_RXBUF_16 0x00000800 +#define RL_RXBUF_32 0x00001000 +#define RL_RXBUF_64 0x00001800 + +#define RL_RXFIFO_16BYTES 0x00000000 +#define RL_RXFIFO_32BYTES 0x00002000 +#define RL_RXFIFO_64BYTES 0x00004000 +#define RL_RXFIFO_128BYTES 0x00006000 +#define RL_RXFIFO_256BYTES 0x00008000 +#define RL_RXFIFO_512BYTES 0x0000A000 +#define RL_RXFIFO_1024BYTES 0x0000C000 +#define RL_RXFIFO_NOTHRESH 0x0000E000 + +/* + * Bits in RX status header (included with RX'ed packet + * in ring buffer). + */ +#define RL_RXSTAT_RXOK 0x00000001 +#define RL_RXSTAT_ALIGNERR 0x00000002 +#define RL_RXSTAT_CRCERR 0x00000004 +#define RL_RXSTAT_GIANT 0x00000008 +#define RL_RXSTAT_RUNT 0x00000010 +#define RL_RXSTAT_BADSYM 0x00000020 +#define RL_RXSTAT_BROAD 0x00002000 +#define RL_RXSTAT_INDIV 0x00004000 +#define RL_RXSTAT_MULTI 0x00008000 +#define RL_RXSTAT_LENMASK 0xFFFF0000 +#define RL_RXSTAT_UNFINISHED 0x0000FFF0 /* DMA still in progress */ + +/* + * Command register. + */ +#define RL_CMD_EMPTY_RXBUF 0x0001 +#define RL_CMD_TX_ENB 0x0004 +#define RL_CMD_RX_ENB 0x0008 +#define RL_CMD_RESET 0x0010 +#define RL_CMD_STOPREQ 0x0080 + +/* + * Twister register values. These are completely undocumented and derived + * from public sources. + */ +#define RL_CSCFG_LINK_OK 0x0400 +#define RL_CSCFG_CHANGE 0x0800 +#define RL_CSCFG_STATUS 0xf000 +#define RL_CSCFG_ROW3 0x7000 +#define RL_CSCFG_ROW2 0x3000 +#define RL_CSCFG_ROW1 0x1000 +#define RL_CSCFG_LINK_DOWN_OFF_CMD 0x03c0 +#define RL_CSCFG_LINK_DOWN_CMD 0xf3c0 + +#define RL_NWAYTST_RESET 0 +#define RL_NWAYTST_CBL_TEST 0x20 + +#define RL_PARA78 0x78 +#define RL_PARA78_DEF 0x78fa8388 +#define RL_PARA7C 0x7C +#define RL_PARA7C_DEF 0xcb38de43 +#define RL_PARA7C_RETUNE 0xfb38de03 + +/* + * EEPROM control register + */ +#define RL_EE_DATAOUT 0x01 /* Data out */ +#define RL_EE_DATAIN 0x02 /* Data in */ +#define RL_EE_CLK 0x04 /* clock */ +#define RL_EE_SEL 0x08 /* chip select */ +#define RL_EE_MODE (0x40|0x80) + +#define RL_EEMODE_OFF 0x00 +#define RL_EEMODE_AUTOLOAD 0x40 +#define RL_EEMODE_PROGRAM 0x80 +#define RL_EEMODE_WRITECFG (0x80|0x40) + +/* 9346 EEPROM commands */ +#define RL_9346_ADDR_LEN 6 /* 93C46 1K: 128x16 */ +#define RL_9356_ADDR_LEN 8 /* 93C56 2K: 256x16 */ + +#define RL_9346_WRITE 0x5 +#define RL_9346_READ 0x6 +#define RL_9346_ERASE 0x7 +#define RL_9346_EWEN 0x4 +#define RL_9346_EWEN_ADDR 0x30 +#define RL_9456_EWDS 0x4 +#define RL_9346_EWDS_ADDR 0x00 + +#define RL_EECMD_WRITE 0x140 +#define RL_EECMD_READ_6BIT 0x180 +#define RL_EECMD_READ_8BIT 0x600 +#define RL_EECMD_ERASE 0x1c0 + +#define RL_EE_ID 0x00 +#define RL_EE_PCI_VID 0x01 +#define RL_EE_PCI_DID 0x02 +/* Location of station address inside EEPROM */ +#define RL_EE_EADDR 0x07 + +/* + * MII register (8129 only) + */ +#define RL_MII_CLK 0x01 +#define RL_MII_DATAIN 0x02 +#define RL_MII_DATAOUT 0x04 +#define RL_MII_DIR 0x80 /* 0 == input, 1 == output */ + +/* + * Config 0 register + */ +#define RL_CFG0_ROM0 0x01 +#define RL_CFG0_ROM1 0x02 +#define RL_CFG0_ROM2 0x04 +#define RL_CFG0_PL0 0x08 +#define RL_CFG0_PL1 0x10 +#define RL_CFG0_10MBPS 0x20 /* 10 Mbps internal mode */ +#define RL_CFG0_PCS 0x40 +#define RL_CFG0_SCR 0x80 + +/* + * Config 1 register + */ +#define RL_CFG1_PWRDWN 0x01 +#define RL_CFG1_PME 0x01 +#define RL_CFG1_SLEEP 0x02 +#define RL_CFG1_VPDEN 0x02 +#define RL_CFG1_IOMAP 0x04 +#define RL_CFG1_MEMMAP 0x08 +#define RL_CFG1_RSVD 0x10 +#define RL_CFG1_LWACT 0x10 +#define RL_CFG1_DRVLOAD 0x20 +#define RL_CFG1_LED0 0x40 +#define RL_CFG1_FULLDUPLEX 0x40 /* 8129 only */ +#define RL_CFG1_LED1 0x80 + +/* + * Config 2 register + */ +#define RL_CFG2_PCI33MHZ 0x00 +#define RL_CFG2_PCI66MHZ 0x01 +#define RL_CFG2_PCI64BIT 0x08 +#define RL_CFG2_AUXPWR 0x10 +#define RL_CFG2_MSI 0x20 + +/* + * Config 3 register + */ +#define RL_CFG3_GRANTSEL 0x80 +#define RL_CFG3_WOL_MAGIC 0x20 +#define RL_CFG3_WOL_LINK 0x10 +#define RL_CFG3_JUMBO_EN0 0x04 /* RTL8168C or later. */ +#define RL_CFG3_FAST_B2B 0x01 + +/* + * Config 4 register + */ +#define RL_CFG4_LWPTN 0x04 +#define RL_CFG4_LWPME 0x10 +#define RL_CFG4_JUMBO_EN1 0x02 /* RTL8168C or later. */ + +/* + * Config 5 register + */ +#define RL_CFG5_WOL_BCAST 0x40 +#define RL_CFG5_WOL_MCAST 0x20 +#define RL_CFG5_WOL_UCAST 0x10 +#define RL_CFG5_WOL_LANWAKE 0x02 +#define RL_CFG5_PME_STS 0x01 + +/* + * 8139C+ register definitions + */ + +/* RL_DUMPSTATS_LO register */ +#define RL_DUMPSTATS_START 0x00000008 + +/* Transmit start register */ +#define RL_TXSTART_SWI 0x01 /* generate TX interrupt */ +#define RL_TXSTART_START 0x40 /* start normal queue transmit */ +#define RL_TXSTART_HPRIO_START 0x80 /* start hi prio queue transmit */ + +/* + * Config 2 register, 8139C+/8169/8169S/8110S only + */ +#define RL_CFG2_BUSFREQ 0x07 +#define RL_CFG2_BUSWIDTH 0x08 +#define RL_CFG2_AUXPWRSTS 0x10 + +#define RL_BUSFREQ_33MHZ 0x00 +#define RL_BUSFREQ_66MHZ 0x01 + +#define RL_BUSWIDTH_32BITS 0x00 +#define RL_BUSWIDTH_64BITS 0x08 + +/* C+ mode command register */ +#define RL_CPLUSCMD_TXENB 0x0001 /* enable C+ transmit mode */ +#define RL_CPLUSCMD_RXENB 0x0002 /* enable C+ receive mode */ +#define RL_CPLUSCMD_PCI_MRW 0x0008 /* enable PCI multi-read/write */ +#define RL_CPLUSCMD_PCI_DAC 0x0010 /* PCI dual-address cycle only */ +#define RL_CPLUSCMD_RXCSUM_ENB 0x0020 /* enable RX checksum offload */ +#define RL_CPLUSCMD_VLANSTRIP 0x0040 /* enable VLAN tag stripping */ +#define RL_CPLUSCMD_MACSTAT_DIS 0x0080 /* 8168B/C/CP */ +#define RL_CPLUSCMD_ASF 0x0100 /* 8168C/CP */ +#define RL_CPLUSCMD_DBG_SEL 0x0200 /* 8168C/CP */ +#define RL_CPLUSCMD_FORCE_TXFC 0x0400 /* 8168C/CP */ +#define RL_CPLUSCMD_FORCE_RXFC 0x0800 /* 8168C/CP */ +#define RL_CPLUSCMD_FORCE_HDPX 0x1000 /* 8168C/CP */ +#define RL_CPLUSCMD_NORMAL_MODE 0x2000 /* 8168C/CP */ +#define RL_CPLUSCMD_DBG_ENB 0x4000 /* 8168C/CP */ +#define RL_CPLUSCMD_BIST_ENB 0x8000 /* 8168C/CP */ + +/* C+ early transmit threshold */ +#define RL_EARLYTXTHRESH_CNT 0x003F /* byte count times 8 */ + +/* Timer interrupt register */ +#define RL_TIMERINT_8169_VAL 0x00001FFF +#define RL_TIMER_MIN 0 +#define RL_TIMER_MAX 65 /* 65.528us */ +#define RL_TIMER_DEFAULT RL_TIMER_MAX +#define RL_TIMER_PCIE_CLK 125 /* 125MHZ */ +#define RL_USECS(x) ((x) * RL_TIMER_PCIE_CLK) + +/* + * Gigabit PHY access register (8169 only) + */ +#define RL_PHYAR_PHYDATA 0x0000FFFF +#define RL_PHYAR_PHYREG 0x001F0000 +#define RL_PHYAR_BUSY 0x80000000 + +/* + * Gigabit media status (8169 only) + */ +#define RL_GMEDIASTAT_FDX 0x01 /* full duplex */ +#define RL_GMEDIASTAT_LINK 0x02 /* link up */ +#define RL_GMEDIASTAT_10MBPS 0x04 /* 10mps link */ +#define RL_GMEDIASTAT_100MBPS 0x08 /* 100mbps link */ +#define RL_GMEDIASTAT_1000MBPS 0x10 /* gigE link */ +#define RL_GMEDIASTAT_RXFLOW 0x20 /* RX flow control on */ +#define RL_GMEDIASTAT_TXFLOW 0x40 /* TX flow control on */ +#define RL_GMEDIASTAT_TBI 0x80 /* TBI enabled */ + +/* + * The RealTek doesn't use a fragment-based descriptor mechanism. + * Instead, there are only four register sets, each or which represents + * one 'descriptor.' Basically, each TX descriptor is just a contiguous + * packet buffer (32-bit aligned!) and we place the buffer addresses in + * the registers so the chip knows where they are. + * + * We can sort of kludge together the same kind of buffer management + * used in previous drivers, but we have to do buffer copies almost all + * the time, so it doesn't really buy us much. + * + * For reception, there's just one large buffer where the chip stores + * all received packets. + */ +#define RL_RX_BUF_SZ RL_RXBUF_64 +#define RL_RXBUFLEN (1 << ((RL_RX_BUF_SZ >> 11) + 13)) +#define RL_TX_LIST_CNT 4 +#define RL_MIN_FRAMELEN 60 +#define RL_TX_8139_BUF_ALIGN 4 +#define RL_RX_8139_BUF_ALIGN 8 +#define RL_RX_8139_BUF_RESERVE sizeof(int64_t) +#define RL_RX_8139_BUF_GUARD_SZ \ + (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN + RL_RX_8139_BUF_RESERVE) +#define RL_TXTHRESH(x) ((x) << 11) +#define RL_TX_THRESH_INIT 96 +#define RL_RX_FIFOTHRESH RL_RXFIFO_NOTHRESH +#define RL_RX_MAXDMA RL_RXDMA_UNLIMITED +#define RL_TX_MAXDMA RL_TXDMA_2048BYTES + +#define RL_RXCFG_CONFIG (RL_RX_FIFOTHRESH|RL_RX_MAXDMA|RL_RX_BUF_SZ) +#define RL_TXCFG_CONFIG (RL_TXCFG_IFG|RL_TX_MAXDMA) + +#define RL_ETHER_ALIGN 2 + +/* + * re(4) hardware ip4csum-tx could be mangled with 28 bytes or less IP packets. + */ +#define RL_IP4CSUMTX_MINLEN 28 +#define RL_IP4CSUMTX_PADLEN (ETHER_HDR_LEN + RL_IP4CSUMTX_MINLEN) + +struct rl_chain_data { + uint16_t cur_rx; + uint8_t *rl_rx_buf; + uint8_t *rl_rx_buf_ptr; + + struct mbuf *rl_tx_chain[RL_TX_LIST_CNT]; + bus_dmamap_t rl_tx_dmamap[RL_TX_LIST_CNT]; + bus_dma_tag_t rl_tx_tag; + bus_dma_tag_t rl_rx_tag; + bus_dmamap_t rl_rx_dmamap; + bus_addr_t rl_rx_buf_paddr; + uint8_t last_tx; + uint8_t cur_tx; +}; + +#define RL_INC(x) (x = (x + 1) % RL_TX_LIST_CNT) +#define RL_CUR_TXADDR(x) ((x->rl_cdata.cur_tx * 4) + RL_TXADDR0) +#define RL_CUR_TXSTAT(x) ((x->rl_cdata.cur_tx * 4) + RL_TXSTAT0) +#define RL_CUR_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.cur_tx]) +#define RL_CUR_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.cur_tx]) +#define RL_LAST_TXADDR(x) ((x->rl_cdata.last_tx * 4) + RL_TXADDR0) +#define RL_LAST_TXSTAT(x) ((x->rl_cdata.last_tx * 4) + RL_TXSTAT0) +#define RL_LAST_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.last_tx]) +#define RL_LAST_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.last_tx]) + +struct rl_type { + uint16_t rl_vid; + uint16_t rl_did; + int rl_basetype; + const char *rl_name; +}; + +struct rl_hwrev { + uint32_t rl_rev; + int rl_type; + const char *rl_desc; + int rl_max_mtu; +}; + +#define RL_8129 1 +#define RL_8139 2 +#define RL_8139CPLUS 3 +#define RL_8169 4 + +#define RL_ISCPLUS(x) ((x)->rl_type == RL_8139CPLUS || \ + (x)->rl_type == RL_8169) + +/* + * The 8139C+ and 8160 gigE chips support descriptor-based TX + * and RX. In fact, they even support TCP large send. Descriptors + * must be allocated in contiguous blocks that are aligned on a + * 256-byte boundary. The rings can hold a maximum of 64 descriptors. + */ + +/* + * RX/TX descriptor definition. When large send mode is enabled, the + * lower 11 bits of the TX rl_cmdstat word are used to hold the MSS, and + * the checksum offload bits are disabled. The structure layout is + * the same for RX and TX descriptors + */ +struct rl_desc { + uint32_t rl_cmdstat; + uint32_t rl_vlanctl; + uint32_t rl_bufaddr_lo; + uint32_t rl_bufaddr_hi; +}; + +#define RL_TDESC_CMD_FRAGLEN 0x0000FFFF +#define RL_TDESC_CMD_TCPCSUM 0x00010000 /* TCP checksum enable */ +#define RL_TDESC_CMD_UDPCSUM 0x00020000 /* UDP checksum enable */ +#define RL_TDESC_CMD_IPCSUM 0x00040000 /* IP header checksum enable */ +#define RL_TDESC_CMD_MSSVAL 0x07FF0000 /* Large send MSS value */ +#define RL_TDESC_CMD_MSSVAL_SHIFT 16 /* Large send MSS value shift */ +#define RL_TDESC_CMD_LGSEND 0x08000000 /* TCP large send enb */ +#define RL_TDESC_CMD_EOF 0x10000000 /* end of frame marker */ +#define RL_TDESC_CMD_SOF 0x20000000 /* start of frame marker */ +#define RL_TDESC_CMD_EOR 0x40000000 /* end of ring marker */ +#define RL_TDESC_CMD_OWN 0x80000000 /* chip owns descriptor */ + +#define RL_TDESC_VLANCTL_TAG 0x00020000 /* Insert VLAN tag */ +#define RL_TDESC_VLANCTL_DATA 0x0000FFFF /* TAG data */ +/* RTL8168C/RTL8168CP/RTL8111C/RTL8111CP */ +#define RL_TDESC_CMD_UDPCSUMV2 0x80000000 +#define RL_TDESC_CMD_TCPCSUMV2 0x40000000 +#define RL_TDESC_CMD_IPCSUMV2 0x20000000 +#define RL_TDESC_CMD_MSSVALV2 0x1FFC0000 +#define RL_TDESC_CMD_MSSVALV2_SHIFT 18 + +/* + * Error bits are valid only on the last descriptor of a frame + * (i.e. RL_TDESC_CMD_EOF == 1) + */ +#define RL_TDESC_STAT_COLCNT 0x000F0000 /* collision count */ +#define RL_TDESC_STAT_EXCESSCOL 0x00100000 /* excessive collisions */ +#define RL_TDESC_STAT_LINKFAIL 0x00200000 /* link faulure */ +#define RL_TDESC_STAT_OWINCOL 0x00400000 /* out-of-window collision */ +#define RL_TDESC_STAT_TXERRSUM 0x00800000 /* transmit error summary */ +#define RL_TDESC_STAT_UNDERRUN 0x02000000 /* TX underrun occured */ +#define RL_TDESC_STAT_OWN 0x80000000 + +/* + * RX descriptor cmd/vlan definitions + */ +#define RL_RDESC_CMD_EOR 0x40000000 +#define RL_RDESC_CMD_OWN 0x80000000 +#define RL_RDESC_CMD_BUFLEN 0x00001FFF + +#define RL_RDESC_STAT_OWN 0x80000000 +#define RL_RDESC_STAT_EOR 0x40000000 +#define RL_RDESC_STAT_SOF 0x20000000 +#define RL_RDESC_STAT_EOF 0x10000000 +#define RL_RDESC_STAT_FRALIGN 0x08000000 /* frame alignment error */ +#define RL_RDESC_STAT_MCAST 0x04000000 /* multicast pkt received */ +#define RL_RDESC_STAT_UCAST 0x02000000 /* unicast pkt received */ +#define RL_RDESC_STAT_BCAST 0x01000000 /* broadcast pkt received */ +#define RL_RDESC_STAT_BUFOFLOW 0x00800000 /* out of buffer space */ +#define RL_RDESC_STAT_FIFOOFLOW 0x00400000 /* FIFO overrun */ +#define RL_RDESC_STAT_GIANT 0x00200000 /* pkt > 4096 bytes */ +#define RL_RDESC_STAT_RXERRSUM 0x00100000 /* RX error summary */ +#define RL_RDESC_STAT_RUNT 0x00080000 /* runt packet received */ +#define RL_RDESC_STAT_CRCERR 0x00040000 /* CRC error */ +#define RL_RDESC_STAT_PROTOID 0x00030000 /* Protocol type */ +#define RL_RDESC_STAT_UDP 0x00020000 /* UDP, 8168C/CP, 8111C/CP */ +#define RL_RDESC_STAT_TCP 0x00010000 /* TCP, 8168C/CP, 8111C/CP */ +#define RL_RDESC_STAT_IPSUMBAD 0x00008000 /* IP header checksum bad */ +#define RL_RDESC_STAT_UDPSUMBAD 0x00004000 /* UDP checksum bad */ +#define RL_RDESC_STAT_TCPSUMBAD 0x00002000 /* TCP checksum bad */ +#define RL_RDESC_STAT_FRAGLEN 0x00001FFF /* RX'ed frame/frag len */ +#define RL_RDESC_STAT_GFRAGLEN 0x00003FFF /* RX'ed frame/frag len */ +#define RL_RDESC_STAT_ERRS (RL_RDESC_STAT_GIANT|RL_RDESC_STAT_RUNT| \ + RL_RDESC_STAT_CRCERR) + +#define RL_RDESC_VLANCTL_TAG 0x00010000 /* VLAN tag available + (rl_vlandata valid)*/ +#define RL_RDESC_VLANCTL_DATA 0x0000FFFF /* TAG data */ +/* RTL8168C/RTL8168CP/RTL8111C/RTL8111CP */ +#define RL_RDESC_IPV6 0x80000000 +#define RL_RDESC_IPV4 0x40000000 + +#define RL_PROTOID_NONIP 0x00000000 +#define RL_PROTOID_TCPIP 0x00010000 +#define RL_PROTOID_UDPIP 0x00020000 +#define RL_PROTOID_IP 0x00030000 +#define RL_TCPPKT(x) (((x) & RL_RDESC_STAT_PROTOID) == \ + RL_PROTOID_TCPIP) +#define RL_UDPPKT(x) (((x) & RL_RDESC_STAT_PROTOID) == \ + RL_PROTOID_UDPIP) + +/* + * Statistics counter structure (8139C+ and 8169 only) + */ +struct rl_stats { + uint64_t rl_tx_pkts; + uint64_t rl_rx_pkts; + uint64_t rl_tx_errs; + uint32_t rl_rx_errs; + uint16_t rl_missed_pkts; + uint16_t rl_rx_framealign_errs; + uint32_t rl_tx_onecoll; + uint32_t rl_tx_multicolls; + uint64_t rl_rx_ucasts; + uint64_t rl_rx_bcasts; + uint32_t rl_rx_mcasts; + uint16_t rl_tx_aborts; + uint16_t rl_rx_underruns; +}; + +/* + * Rx/Tx descriptor parameters (8139C+ and 8169 only) + * + * 8139C+ + * Number of descriptors supported : up to 64 + * Descriptor alignment : 256 bytes + * Tx buffer : At least 4 bytes in length. + * Rx buffer : At least 8 bytes in length and 8 bytes alignment required. + * + * 8169 + * Number of descriptors supported : up to 1024 + * Descriptor alignment : 256 bytes + * Tx buffer : At least 4 bytes in length. + * Rx buffer : At least 8 bytes in length and 8 bytes alignment required. + */ +#ifndef __NO_STRICT_ALIGNMENT +#define RE_FIXUP_RX 1 +#endif + +#define RL_8169_TX_DESC_CNT 256 +#define RL_8169_RX_DESC_CNT 256 +#define RL_8139_TX_DESC_CNT 64 +#define RL_8139_RX_DESC_CNT 64 +#define RL_TX_DESC_CNT RL_8169_TX_DESC_CNT +#define RL_RX_DESC_CNT RL_8169_RX_DESC_CNT +#define RL_RX_JUMBO_DESC_CNT RL_RX_DESC_CNT +#define RL_NTXSEGS 35 + +#define RL_RING_ALIGN 256 +#define RL_DUMP_ALIGN 64 +#define RL_IFQ_MAXLEN 512 +#define RL_TX_DESC_NXT(sc,x) ((x + 1) & ((sc)->rl_ldata.rl_tx_desc_cnt - 1)) +#define RL_TX_DESC_PRV(sc,x) ((x - 1) & ((sc)->rl_ldata.rl_tx_desc_cnt - 1)) +#define RL_RX_DESC_NXT(sc,x) ((x + 1) & ((sc)->rl_ldata.rl_rx_desc_cnt - 1)) +#define RL_OWN(x) (le32toh((x)->rl_cmdstat) & RL_RDESC_STAT_OWN) +#define RL_RXBYTES(x) (le32toh((x)->rl_cmdstat) & sc->rl_rxlenmask) +#define RL_PKTSZ(x) ((x)/* >> 3*/) +#ifdef RE_FIXUP_RX +#define RE_ETHER_ALIGN sizeof(uint64_t) +#define RE_RX_DESC_BUFLEN (MCLBYTES - RE_ETHER_ALIGN) +#else +#define RE_ETHER_ALIGN 0 +#define RE_RX_DESC_BUFLEN MCLBYTES +#endif + +#define RL_MSI_MESSAGES 1 + +#define RL_ADDR_LO(y) ((uint64_t) (y) & 0xFFFFFFFF) +#define RL_ADDR_HI(y) ((uint64_t) (y) >> 32) + +/* + * The number of bits reserved for MSS in RealTek controllers is + * 11bits. This limits the maximum interface MTU size in TSO case + * as upper stack should not generate TCP segments with MSS greater + * than the limit. + */ +#define RL_TSO_MTU (2047 - ETHER_HDR_LEN - ETHER_CRC_LEN) + +/* see comment in dev/re/if_re.c */ +#define RL_JUMBO_FRAMELEN 7440 +#define RL_JUMBO_MTU \ + (RL_JUMBO_FRAMELEN-ETHER_VLAN_ENCAP_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN) +#define RL_JUMBO_MTU_6K \ + ((6 * 1024) - ETHER_VLAN_ENCAP_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN) +#define RL_JUMBO_MTU_9K \ + ((9 * 1024) - ETHER_VLAN_ENCAP_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN) +#define RL_MTU \ + (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN) + +struct rl_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; +}; + +struct rl_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; + bus_size_t rx_size; +}; + +struct rl_list_data { + struct rl_txdesc rl_tx_desc[RL_TX_DESC_CNT]; + struct rl_rxdesc rl_rx_desc[RL_RX_DESC_CNT]; + struct rl_rxdesc rl_jrx_desc[RL_RX_JUMBO_DESC_CNT]; + int rl_tx_desc_cnt; + int rl_rx_desc_cnt; + int rl_tx_prodidx; + int rl_rx_prodidx; + int rl_tx_considx; + int rl_tx_free; + bus_dma_tag_t rl_tx_mtag; /* mbuf TX mapping tag */ + bus_dma_tag_t rl_rx_mtag; /* mbuf RX mapping tag */ + bus_dma_tag_t rl_jrx_mtag; /* mbuf RX mapping tag */ + bus_dmamap_t rl_rx_sparemap; + bus_dmamap_t rl_jrx_sparemap; + bus_dma_tag_t rl_stag; /* stats mapping tag */ + bus_dmamap_t rl_smap; /* stats map */ + struct rl_stats *rl_stats; + bus_addr_t rl_stats_addr; + bus_dma_tag_t rl_rx_list_tag; + bus_dmamap_t rl_rx_list_map; + struct rl_desc *rl_rx_list; + bus_addr_t rl_rx_list_addr; + bus_dma_tag_t rl_tx_list_tag; + bus_dmamap_t rl_tx_list_map; + struct rl_desc *rl_tx_list; + bus_addr_t rl_tx_list_addr; +}; + +enum rl_twist { DONE, CHK_LINK, FIND_ROW, SET_PARAM, RECHK_LONG, RETUNE }; + +struct rl_softc { + struct ifnet *rl_ifp; /* interface info */ + bus_space_handle_t rl_bhandle; /* bus space handle */ + bus_space_tag_t rl_btag; /* bus space tag */ + device_t rl_dev; + struct resource *rl_res; + int rl_res_id; + int rl_res_type; + struct resource *rl_res_pba; + struct resource *rl_irq[RL_MSI_MESSAGES]; + void *rl_intrhand[RL_MSI_MESSAGES]; + device_t rl_miibus; + bus_dma_tag_t rl_parent_tag; + uint8_t rl_type; + const struct rl_hwrev *rl_hwrev; + uint32_t rl_macrev; + int rl_eecmd_read; + int rl_eewidth; + int rl_expcap; + int rl_txthresh; + bus_size_t rl_cfg0; + bus_size_t rl_cfg1; + bus_size_t rl_cfg2; + bus_size_t rl_cfg3; + bus_size_t rl_cfg4; + bus_size_t rl_cfg5; + struct rl_chain_data rl_cdata; + struct rl_list_data rl_ldata; + struct callout rl_stat_callout; + int rl_watchdog_timer; + struct mtx rl_mtx; + struct mbuf *rl_head; + struct mbuf *rl_tail; + uint32_t rl_rxlenmask; + int rl_testmode; + int rl_if_flags; + int rl_twister_enable; + enum rl_twist rl_twister; + int rl_twist_row; + int rl_twist_col; + int suspended; /* 0 = normal 1 = suspended */ +#ifdef DEVICE_POLLING + int rxcycles; +#endif + + struct task rl_inttask; + + int rl_txstart; + int rl_int_rx_act; + int rl_int_rx_mod; + uint32_t rl_flags; +#define RL_FLAG_MSI 0x00000001 +#define RL_FLAG_AUTOPAD 0x00000002 +#define RL_FLAG_PHYWAKE_PM 0x00000004 +#define RL_FLAG_PHYWAKE 0x00000008 +#define RL_FLAG_JUMBOV2 0x00000010 +#define RL_FLAG_PAR 0x00000020 +#define RL_FLAG_DESCV2 0x00000040 +#define RL_FLAG_MACSTAT 0x00000080 +#define RL_FLAG_FASTETHER 0x00000100 +#define RL_FLAG_CMDSTOP 0x00000200 +#define RL_FLAG_MACRESET 0x00000400 +#define RL_FLAG_MSIX 0x00000800 +#define RL_FLAG_WOLRXENB 0x00001000 +#define RL_FLAG_MACSLEEP 0x00002000 +#define RL_FLAG_WAIT_TXPOLL 0x00004000 +#define RL_FLAG_CMDSTOP_WAIT_TXQ 0x00008000 +#define RL_FLAG_WOL_MANLINK 0x00010000 +#define RL_FLAG_EARLYOFF 0x00020000 +#define RL_FLAG_8168G_PLUS 0x00040000 +#define RL_FLAG_PCIE 0x40000000 +#define RL_FLAG_LINK 0x80000000 +}; + +#define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx) +#define RL_UNLOCK(_sc) mtx_unlock(&(_sc)->rl_mtx) +#define RL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->rl_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define CSR_WRITE_STREAM_4(sc, reg, val) \ + bus_space_write_stream_4(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->rl_btag, sc->rl_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->rl_btag, sc->rl_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->rl_btag, sc->rl_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->rl_btag, sc->rl_bhandle, reg) + +#define CSR_BARRIER(sc, reg, length, flags) \ + bus_space_barrier(sc->rl_btag, sc->rl_bhandle, reg, length, flags) + +#define CSR_SETBIT_1(sc, offset, val) \ + CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) | (val)) + +#define CSR_CLRBIT_1(sc, offset, val) \ + CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) & ~(val)) + +#define CSR_SETBIT_2(sc, offset, val) \ + CSR_WRITE_2(sc, offset, CSR_READ_2(sc, offset) | (val)) + +#define CSR_CLRBIT_2(sc, offset, val) \ + CSR_WRITE_2(sc, offset, CSR_READ_2(sc, offset) & ~(val)) + +#define CSR_SETBIT_4(sc, offset, val) \ + CSR_WRITE_4(sc, offset, CSR_READ_4(sc, offset) | (val)) + +#define CSR_CLRBIT_4(sc, offset, val) \ + CSR_WRITE_4(sc, offset, CSR_READ_4(sc, offset) & ~(val)) + +#define RL_TIMEOUT 1000 +#define RL_PHY_TIMEOUT 2000 + +/* + * General constants that are fun to know. + * + * RealTek PCI vendor ID + */ +#define RT_VENDORID 0x10EC + +/* + * RealTek chip device IDs. + */ +#define RT_DEVICEID_8139D 0x8039 +#define RT_DEVICEID_8129 0x8129 +#define RT_DEVICEID_8101E 0x8136 +#define RT_DEVICEID_8138 0x8138 +#define RT_DEVICEID_8139 0x8139 +#define RT_DEVICEID_8169SC 0x8167 +#define RT_DEVICEID_8168 0x8168 +#define RT_DEVICEID_8169 0x8169 +#define RT_DEVICEID_8100 0x8100 + +#define RT_REVID_8139CPLUS 0x20 + +/* + * Accton PCI vendor ID + */ +#define ACCTON_VENDORID 0x1113 + +/* + * Accton MPX 5030/5038 device ID. + */ +#define ACCTON_DEVICEID_5030 0x1211 + +/* + * Nortel PCI vendor ID + */ +#define NORTEL_VENDORID 0x126C + +/* + * Delta Electronics Vendor ID. + */ +#define DELTA_VENDORID 0x1500 + +/* + * Delta device IDs. + */ +#define DELTA_DEVICEID_8139 0x1360 + +/* + * Addtron vendor ID. + */ +#define ADDTRON_VENDORID 0x4033 + +/* + * Addtron device IDs. + */ +#define ADDTRON_DEVICEID_8139 0x1360 + +/* + * D-Link vendor ID. + */ +#define DLINK_VENDORID 0x1186 + +/* + * D-Link DFE-530TX+ device ID + */ +#define DLINK_DEVICEID_530TXPLUS 0x1300 + +/* + * D-Link DFE-520TX rev. C1 device ID + */ +#define DLINK_DEVICEID_520TX_REVC1 0x4200 + +/* + * D-Link DFE-5280T device ID + */ +#define DLINK_DEVICEID_528T 0x4300 +#define DLINK_DEVICEID_530T_REVC 0x4302 + +/* + * D-Link DFE-690TXD device ID + */ +#define DLINK_DEVICEID_690TXD 0x1340 + +/* + * Corega K.K vendor ID + */ +#define COREGA_VENDORID 0x1259 + +/* + * Corega FEther CB-TXD device ID + */ +#define COREGA_DEVICEID_FETHERCBTXD 0xa117 + +/* + * Corega FEtherII CB-TXD device ID + */ +#define COREGA_DEVICEID_FETHERIICBTXD 0xa11e + +/* + * Corega CG-LAPCIGT device ID + */ +#define COREGA_DEVICEID_CGLAPCIGT 0xc107 + +/* + * Linksys vendor ID + */ +#define LINKSYS_VENDORID 0x1737 + +/* + * Linksys EG1032 device ID + */ +#define LINKSYS_DEVICEID_EG1032 0x1032 + +/* + * Linksys EG1032 rev 3 sub-device ID + */ +#define LINKSYS_SUBDEVICE_EG1032_REV3 0x0024 + +/* + * Peppercon vendor ID + */ +#define PEPPERCON_VENDORID 0x1743 + +/* + * Peppercon ROL-F device ID + */ +#define PEPPERCON_DEVICEID_ROLF 0x8139 + +/* + * Planex Communications, Inc. vendor ID + */ +#define PLANEX_VENDORID 0x14ea + +/* + * Planex FNW-3603-TX device ID + */ +#define PLANEX_DEVICEID_FNW3603TX 0xab06 + +/* + * Planex FNW-3800-TX device ID + */ +#define PLANEX_DEVICEID_FNW3800TX 0xab07 + +/* + * LevelOne vendor ID + */ +#define LEVEL1_VENDORID 0x018A + +/* + * LevelOne FPC-0106TX devide ID + */ +#define LEVEL1_DEVICEID_FPC0106TX 0x0106 + +/* + * Compaq vendor ID + */ +#define CP_VENDORID 0x021B + +/* + * Edimax vendor ID + */ +#define EDIMAX_VENDORID 0x13D1 + +/* + * Edimax EP-4103DL cardbus device ID + */ +#define EDIMAX_DEVICEID_EP4103DL 0xAB06 + +/* US Robotics vendor ID */ + +#define USR_VENDORID 0x16EC + +/* US Robotics 997902 device ID */ + +#define USR_DEVICEID_997902 0x0116 diff --git a/sys/dev/usb/net/if_urndis.c b/sys/dev/usb/net/if_urndis.c index 6509e78..5d2a637 100644 --- a/sys/dev/usb/net/if_urndis.c +++ b/sys/dev/usb/net/if_urndis.c @@ -78,12 +78,20 @@ static uether_fn_t urndis_start; static uether_fn_t urndis_setmulti; static uether_fn_t urndis_setpromisc; -static uint32_t urndis_ctrl_query(struct urndis_softc *, uint32_t, const void **, uint16_t *); -static uint32_t urndis_ctrl_set(struct urndis_softc *, uint32_t, struct urndis_set_req *, uint16_t); -static uint32_t urndis_ctrl_handle_init(struct urndis_softc *, const struct urndis_comp_hdr *); -static uint32_t urndis_ctrl_handle_query(struct urndis_softc *, const struct urndis_comp_hdr *, const void **, uint16_t *); -static uint32_t urndis_ctrl_handle_reset(struct urndis_softc *, const struct urndis_comp_hdr *); -static uint32_t urndis_ctrl_init(struct urndis_softc *); +static uint32_t urndis_ctrl_query(struct urndis_softc *sc, uint32_t oid, + struct urndis_query_req *msg, uint16_t len, + const void **rbuf, uint16_t *rbufsz); +static uint32_t urndis_ctrl_set(struct urndis_softc *sc, uint32_t oid, + struct urndis_set_req *msg, uint16_t len); +static uint32_t urndis_ctrl_handle_init(struct urndis_softc *sc, + const struct urndis_comp_hdr *hdr); +static uint32_t urndis_ctrl_handle_query(struct urndis_softc *sc, + const struct urndis_comp_hdr *hdr, const void **buf, + uint16_t *bufsz); +static uint32_t urndis_ctrl_handle_reset(struct urndis_softc *sc, + const struct urndis_comp_hdr *hdr); +static uint32_t urndis_ctrl_init(struct urndis_softc *sc); +static uint32_t urndis_ctrl_halt(struct urndis_softc *sc); #ifdef USB_DEBUG static int urndis_debug = 0; @@ -93,7 +101,6 @@ SYSCTL_INT(_hw_usb_urndis, OID_AUTO, debug, CTLFLAG_RW, &urndis_debug, 0, #endif static const struct usb_config urndis_config[URNDIS_N_TRANSFER] = { - [URNDIS_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, @@ -154,7 +161,7 @@ static driver_t urndis_driver = { static devclass_t urndis_devclass; -DRIVER_MODULE(urndis, uhub, urndis_driver, urndis_devclass, NULL, 0); +DRIVER_MODULE(urndis, uhub, urndis_driver, urndis_devclass, NULL, NULL); MODULE_VERSION(urndis, 1); MODULE_DEPEND(urndis, uether, 1, 1, 1); MODULE_DEPEND(urndis, usb, 1, 1, 1); @@ -171,6 +178,9 @@ static const struct usb_ether_methods urndis_ue_methods = { static const STRUCT_USB_HOST_ID urndis_host_devs[] = { /* Generic RNDIS class match */ + {USB_IFACE_CLASS(UICLASS_CDC), + USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), + USB_IFACE_PROTOCOL(0xff)}, {USB_IFACE_CLASS(UICLASS_WIRELESS), USB_IFACE_SUBCLASS(UISUBCLASS_RF), USB_IFACE_PROTOCOL(UIPROTO_RNDIS)}, {USB_IFACE_CLASS(UICLASS_IAD), USB_IFACE_SUBCLASS(UISUBCLASS_SYNC), @@ -192,21 +202,27 @@ urndis_probe(device_t dev) static void urndis_attach_post(struct usb_ether *ue) { + /* no-op */ - return; } static int urndis_attach(device_t dev) { + static struct { + union { + struct urndis_query_req query; + struct urndis_set_req set; + } hdr; + union { + uint8_t eaddr[ETHER_ADDR_LEN]; + uint32_t filter; + } ibuf; + } msg; struct urndis_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_cdc_cm_descriptor *cmd; - struct { - struct urndis_set_req hdr; - uint32_t filter; - } msg_filter; const void *buf; uint16_t bufsz; uint8_t iface_index[2] = { uaa->info.bIfaceIndex + 1, uaa->info.bIfaceIndex }; @@ -228,9 +244,7 @@ urndis_attach(device_t dev) mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* scan the alternate settings looking for a valid one */ - for (i = 0; i != 32; i++) { - error = usbd_set_alt_interface_index(uaa->device, iface_index[0], i); @@ -244,16 +258,27 @@ urndis_attach(device_t dev) if (error == 0) break; } - if ((error != 0) || (i == 32)) { - device_printf(dev, "No valid alternate " - "setting found\n"); + device_printf(dev, "No valid alternate setting found\n"); goto detach; } + + /* Initialize device - must be done before even querying it */ URNDIS_LOCK(sc); - error = urndis_ctrl_query(sc, OID_802_3_PERMANENT_ADDRESS, &buf, &bufsz); + error = urndis_ctrl_init(sc); URNDIS_UNLOCK(sc); + if (error != (int)RNDIS_STATUS_SUCCESS) { + device_printf(dev, "Unable to initialize hardware\n"); + goto detach; + } + /* Determine MAC address */ + memset(msg.ibuf.eaddr, 0, sizeof(msg.ibuf.eaddr)); + URNDIS_LOCK(sc); + error = urndis_ctrl_query(sc, OID_802_3_PERMANENT_ADDRESS, + &msg.hdr.query, sizeof(msg.hdr.query) + sizeof(msg.ibuf.eaddr), + &buf, &bufsz); + URNDIS_UNLOCK(sc); if (error != (int)RNDIS_STATUS_SUCCESS) { device_printf(dev, "Unable to get hardware address\n"); goto detach; @@ -267,17 +292,16 @@ urndis_attach(device_t dev) /* Initialize packet filter */ sc->sc_filter = RNDIS_PACKET_TYPE_BROADCAST | RNDIS_PACKET_TYPE_ALL_MULTICAST; - msg_filter.filter = htole32(sc->sc_filter); - + msg.ibuf.filter = htole32(sc->sc_filter); URNDIS_LOCK(sc); error = urndis_ctrl_set(sc, OID_GEN_CURRENT_PACKET_FILTER, - &msg_filter.hdr, sizeof(msg_filter)); + &msg.hdr.set, sizeof(msg.hdr.set) + sizeof(msg.ibuf.filter)); URNDIS_UNLOCK(sc); - if (error != (int)RNDIS_STATUS_SUCCESS) { device_printf(dev, "Unable to set data filters\n"); goto detach; } + ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; @@ -298,7 +322,7 @@ urndis_attach(device_t dev) return (0); /* success */ detach: - urndis_detach(dev); + (void)urndis_detach(dev); return (ENXIO); /* failure */ } @@ -313,6 +337,10 @@ urndis_detach(device_t dev) uether_ifdetach(ue); + URNDIS_LOCK(sc); + (void)urndis_ctrl_halt(sc); + URNDIS_UNLOCK(sc); + mtx_destroy(&sc->sc_mtx); return (0); @@ -340,8 +368,6 @@ urndis_init(struct usb_ether *ue) ifp->if_drv_flags |= IFF_DRV_RUNNING; - urndis_ctrl_init(sc); - /* stall data write direction, which depends on USB mode */ usbd_xfer_set_stall(sc->sc_xfer[URNDIS_BULK_TX]); @@ -369,20 +395,21 @@ urndis_stop(struct usb_ether *ue) static void urndis_setmulti(struct usb_ether *ue) { + /* no-op */ - return; } static void urndis_setpromisc(struct usb_ether *ue) { + /* no-op */ - return; } static int urndis_suspend(device_t dev) { + device_printf(dev, "Suspending\n"); return (0); } @@ -390,6 +417,7 @@ urndis_suspend(device_t dev) static int urndis_resume(device_t dev) { + device_printf(dev, "Resuming\n"); return (0); } @@ -416,8 +444,8 @@ urndis_ctrl_send(struct urndis_softc *sc, void *buf, uint16_t len) { usb_error_t err; - err = urndis_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, UR_GET_STATUS, - sc->sc_ifaceno_ctl, 0, buf, len); + err = urndis_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, + UCDC_SEND_ENCAPSULATED_COMMAND, sc->sc_ifaceno_ctl, 0, buf, len); DPRINTF("%s\n", usbd_errstr(err)); @@ -430,8 +458,9 @@ urndis_ctrl_recv(struct urndis_softc *sc) struct urndis_comp_hdr *hdr; usb_error_t err; - err = urndis_ctrl_msg(sc, UT_READ_CLASS_INTERFACE, UR_CLEAR_FEATURE, - sc->sc_ifaceno_ctl, 0, sc->sc_response_buf, RNDIS_RESPONSE_LEN); + err = urndis_ctrl_msg(sc, UT_READ_CLASS_INTERFACE, + UCDC_GET_ENCAPSULATED_RESPONSE, sc->sc_ifaceno_ctl, 0, + sc->sc_response_buf, RNDIS_RESPONSE_LEN); if (err != USB_ERR_NORMAL_COMPLETION) return (NULL); @@ -480,7 +509,8 @@ urndis_ctrl_handle(struct urndis_softc *sc, struct urndis_comp_hdr *hdr, break; default: - DPRINTF("ctrl message error: unknown event 0x%x\n", + device_printf(sc->sc_ue.ue_dev, + "ctrl message error: unknown event 0x%x\n", le32toh(hdr->rm_type)); rval = RNDIS_STATUS_FAILURE; break; @@ -548,10 +578,8 @@ urndis_ctrl_handle_query(struct urndis_softc *sc, le32toh(msg->rm_infobuflen), le32toh(msg->rm_infobufoffset)); - if (buf != NULL && bufsz != NULL) { - *buf = NULL; - *bufsz = 0; - } + *buf = NULL; + *bufsz = 0; if (le32toh(msg->rm_status) != RNDIS_STATUS_SUCCESS) { DPRINTF("query failed 0x%x\n", le32toh(msg->rm_status)); return (le32toh(msg->rm_status)); @@ -571,10 +599,10 @@ urndis_ctrl_handle_query(struct urndis_softc *sc, le32toh(msg->rm_len)); return (RNDIS_STATUS_FAILURE); } - if (buf != NULL && bufsz != NULL) { - *buf = ((const uint8_t *)msg) + RNDIS_HEADER_OFFSET + le32toh(msg->rm_infobufoffset); - *bufsz = le32toh(msg->rm_infobuflen); - } + *buf = ((const uint8_t *)msg) + RNDIS_HEADER_OFFSET + + le32toh(msg->rm_infobufoffset); + *bufsz = le32toh(msg->rm_infobuflen); + return (le32toh(msg->rm_status)); } @@ -627,7 +655,7 @@ urndis_ctrl_init(struct urndis_softc *sc) msg.rm_type = htole32(REMOTE_NDIS_INITIALIZE_MSG); msg.rm_len = htole32(sizeof(msg)); - msg.rm_rid = htole32(0); + msg.rm_rid = 0; msg.rm_ver_major = htole32(1); msg.rm_ver_minor = htole32(1); msg.rm_max_xfersz = htole32(RNDIS_RX_MAXLEN); @@ -656,7 +684,6 @@ urndis_ctrl_init(struct urndis_softc *sc) return (rval); } -#if 0 static uint32_t urndis_ctrl_halt(struct urndis_softc *sc) { @@ -675,39 +702,48 @@ urndis_ctrl_halt(struct urndis_softc *sc) rval = urndis_ctrl_send(sc, &msg, sizeof(msg)); if (rval != RNDIS_STATUS_SUCCESS) - printf("halt failed\n"); + DPRINTF("halt failed\n"); return (rval); } -#endif - +/* + * NB: Querying a device has the requirment of using an input buffer the size + * of the expected reply or larger, except for variably sized replies. + */ static uint32_t -urndis_ctrl_query(struct urndis_softc *sc, uint32_t oid, const void **rbuf, uint16_t *rbufsz) +urndis_ctrl_query(struct urndis_softc *sc, uint32_t oid, + struct urndis_query_req *msg, uint16_t len, const void **rbuf, + uint16_t *rbufsz) { - struct urndis_query_req msg; - uint32_t rval; struct urndis_comp_hdr *hdr; + uint32_t datalen, rval; - msg.rm_type = htole32(REMOTE_NDIS_QUERY_MSG); - msg.rm_len = htole32(sizeof(msg)); - msg.rm_rid = 0; /* XXX */ - msg.rm_oid = htole32(oid); - msg.rm_infobuflen = htole32(0); - msg.rm_infobufoffset = 0; - msg.rm_devicevchdl = 0; + msg->rm_type = htole32(REMOTE_NDIS_QUERY_MSG); + msg->rm_len = htole32(len); + msg->rm_rid = 0; /* XXX */ + msg->rm_oid = htole32(oid); + datalen = len - sizeof(*msg); + msg->rm_infobuflen = htole32(datalen); + if (datalen != 0) { + msg->rm_infobufoffset = htole32(sizeof(*msg) - + RNDIS_HEADER_OFFSET); + } else { + msg->rm_infobufoffset = 0; + } + msg->rm_devicevchdl = 0; DPRINTF("type %u len %u rid %u oid 0x%x " "infobuflen %u infobufoffset %u devicevchdl %u\n", - le32toh(msg.rm_type), - le32toh(msg.rm_len), - le32toh(msg.rm_rid), - le32toh(msg.rm_oid), - le32toh(msg.rm_infobuflen), - le32toh(msg.rm_infobufoffset), - le32toh(msg.rm_devicevchdl)); + le32toh(msg->rm_type), + le32toh(msg->rm_len), + le32toh(msg->rm_rid), + le32toh(msg->rm_oid), + le32toh(msg->rm_infobuflen), + le32toh(msg->rm_infobufoffset), + le32toh(msg->rm_devicevchdl)); - rval = urndis_ctrl_send(sc, &msg, sizeof(msg)); + rval = urndis_ctrl_send(sc, msg, len); if (rval != RNDIS_STATUS_SUCCESS) { DPRINTF("query failed\n"); @@ -723,19 +759,21 @@ urndis_ctrl_query(struct urndis_softc *sc, uint32_t oid, const void **rbuf, uint } static uint32_t -urndis_ctrl_set(struct urndis_softc *sc, uint32_t oid, struct urndis_set_req *msg, uint16_t len) +urndis_ctrl_set(struct urndis_softc *sc, uint32_t oid, + struct urndis_set_req *msg, uint16_t len) { struct urndis_comp_hdr *hdr; - uint32_t rval; - uint32_t datalen = len - sizeof(*msg); + uint32_t datalen, rval; msg->rm_type = htole32(REMOTE_NDIS_SET_MSG); msg->rm_len = htole32(len); msg->rm_rid = 0; /* XXX */ msg->rm_oid = htole32(oid); + datalen = len - sizeof(*msg); msg->rm_infobuflen = htole32(datalen); if (datalen != 0) { - msg->rm_infobufoffset = htole32(sizeof(*msg) - RNDIS_HEADER_OFFSET); + msg->rm_infobufoffset = htole32(sizeof(*msg) - + RNDIS_HEADER_OFFSET); } else { msg->rm_infobufoffset = 0; } @@ -782,13 +820,11 @@ urndis_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); DPRINTFN(1, "received %u bytes in %u frames\n", actlen, aframes); for (offset = 0; actlen >= (uint32_t)sizeof(msg);) { - /* copy out header */ usbd_copy_out(pc, offset, &msg, sizeof(msg)); @@ -930,7 +966,7 @@ tr_setup: usbd_xfer_set_frame_offset(xfer, x * RNDIS_TX_MAXLEN, x); - next_pkt: +next_pkt: IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) diff --git a/sys/dev/vt/colors/vt_termcolors.c b/sys/dev/vt/colors/vt_termcolors.c index d8a5909..5075619 100644 --- a/sys/dev/vt/colors/vt_termcolors.c +++ b/sys/dev/vt/colors/vt_termcolors.c @@ -25,15 +25,16 @@ * 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. - * - * $FreeBSD$ */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + #include <sys/param.h> #include <dev/vt/colors/vt_termcolors.h> -static struct { +static const struct { unsigned char r; /* Red percentage value. */ unsigned char g; /* Green percentage value. */ unsigned char b; /* Blue percentage value. */ @@ -68,8 +69,8 @@ static const int cons_to_vga_colors[16] = { }; int -vt_generate_cons_palette(uint32_t *palette, int format, uint32_t rmax, int roffset, - uint32_t gmax, int goffset, uint32_t bmax, int boffset) +vt_generate_cons_palette(uint32_t *palette, int format, uint32_t rmax, + int roffset, uint32_t gmax, int goffset, uint32_t bmax, int boffset) { int i; |