dcphy.c revision 190117
1139749Simp/*- 254134Swpaul * Copyright (c) 1997, 1998, 1999 354134Swpaul * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. 454134Swpaul * 554134Swpaul * Redistribution and use in source and binary forms, with or without 654134Swpaul * modification, are permitted provided that the following conditions 754134Swpaul * are met: 854134Swpaul * 1. Redistributions of source code must retain the above copyright 954134Swpaul * notice, this list of conditions and the following disclaimer. 1054134Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1154134Swpaul * notice, this list of conditions and the following disclaimer in the 1254134Swpaul * documentation and/or other materials provided with the distribution. 1354134Swpaul * 3. All advertising materials mentioning features or use of this software 1454134Swpaul * must display the following acknowledgement: 1554134Swpaul * This product includes software developed by Bill Paul. 1654134Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1754134Swpaul * may be used to endorse or promote products derived from this software 1854134Swpaul * without specific prior written permission. 1954134Swpaul * 2054134Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2154134Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2254134Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2354134Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2454134Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2554134Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2654134Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2754134Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2854134Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2954134Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3054134Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3154134Swpaul */ 3254134Swpaul 33119418Sobrien#include <sys/cdefs.h> 34119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/dc/dcphy.c 190117 2009-03-19 22:34:55Z marius $"); 35119418Sobrien 3654134Swpaul/* 3754134Swpaul * Pseudo-driver for internal NWAY support on DEC 21143 and workalike 38183505Smarius * controllers. Technically we're abusing the miibus code to handle 3954134Swpaul * media selection and NWAY support here since there is no MII 40183505Smarius * interface. However the logical operations are roughly the same, 4154134Swpaul * and the alternative is to create a fake MII interface in the driver, 4254134Swpaul * which is harder to do. 4354134Swpaul */ 4454134Swpaul 4554134Swpaul#include <sys/param.h> 4654134Swpaul#include <sys/systm.h> 4754134Swpaul#include <sys/kernel.h> 4854134Swpaul#include <sys/socket.h> 4954134Swpaul#include <sys/errno.h> 5074914Sjhb#include <sys/lock.h> 5154134Swpaul#include <sys/module.h> 5267365Sjhb#include <sys/mutex.h> 5354134Swpaul#include <sys/bus.h> 5454134Swpaul 5554134Swpaul#include <net/if.h> 5654134Swpaul#include <net/if_arp.h> 5754134Swpaul#include <net/if_media.h> 5854134Swpaul 5954134Swpaul#include <dev/mii/mii.h> 6054134Swpaul#include <dev/mii/miivar.h> 61109514Sobrien#include "miidevs.h" 6254134Swpaul 6354134Swpaul#include <machine/bus.h> 6454134Swpaul#include <machine/resource.h> 6554134Swpaul#include <sys/bus.h> 6654134Swpaul 67119285Simp#include <dev/pci/pcivar.h> 6854134Swpaul 69151435Simp#include <dev/dc/if_dcreg.h> 7054134Swpaul 7154134Swpaul#include "miibus_if.h" 7254134Swpaul 7354134Swpaul#define DC_SETBIT(sc, reg, x) \ 7454134Swpaul CSR_WRITE_4(sc, reg, \ 7554134Swpaul CSR_READ_4(sc, reg) | x) 7654134Swpaul 7754134Swpaul#define DC_CLRBIT(sc, reg, x) \ 7854134Swpaul CSR_WRITE_4(sc, reg, \ 7954134Swpaul CSR_READ_4(sc, reg) & ~x) 8054134Swpaul 8154134Swpaul#define MIIF_AUTOTIMEOUT 0x0004 8254134Swpaul 8354577Swpaul/* 8454577Swpaul * This is the subsystem ID for the built-in 21143 ethernet 85183505Smarius * in several Compaq Presario systems. Apparently these are 8654577Swpaul * 10Mbps only, so we need to treat them specially. 8754577Swpaul */ 8854577Swpaul#define COMPAQ_PRESARIO_ID 0xb0bb0e11 8954577Swpaul 90105135Salfredstatic int dcphy_probe(device_t); 91105135Salfredstatic int dcphy_attach(device_t); 9254134Swpaul 9354134Swpaulstatic device_method_t dcphy_methods[] = { 9454134Swpaul /* device interface */ 9554134Swpaul DEVMETHOD(device_probe, dcphy_probe), 9654134Swpaul DEVMETHOD(device_attach, dcphy_attach), 9795722Sphk DEVMETHOD(device_detach, mii_phy_detach), 9854134Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 9954134Swpaul { 0, 0 } 10054134Swpaul}; 10154134Swpaul 10254134Swpaulstatic devclass_t dcphy_devclass; 10354134Swpaul 10454134Swpaulstatic driver_t dcphy_driver = { 10554134Swpaul "dcphy", 10654134Swpaul dcphy_methods, 10754134Swpaul sizeof(struct mii_softc) 10854134Swpaul}; 10954134Swpaul 11054134SwpaulDRIVER_MODULE(dcphy, miibus, dcphy_driver, dcphy_devclass, 0, 0); 11154134Swpaul 11292739Salfredstatic int dcphy_service(struct mii_softc *, struct mii_data *, int); 11392739Salfredstatic void dcphy_status(struct mii_softc *); 11492739Salfredstatic void dcphy_reset(struct mii_softc *); 11596026Sphkstatic int dcphy_auto(struct mii_softc *); 11654134Swpaul 117105135Salfredstatic int 118150763Simpdcphy_probe(device_t dev) 11954134Swpaul{ 12054134Swpaul struct mii_attach_args *ma; 12154134Swpaul 12254134Swpaul ma = device_get_ivars(dev); 12354134Swpaul 12454134Swpaul /* 12554134Swpaul * The dc driver will report the 21143 vendor and device 12654134Swpaul * ID to let us know that it wants us to attach. 12754134Swpaul */ 12854134Swpaul if (ma->mii_id1 != DC_VENDORID_DEC || 12954134Swpaul ma->mii_id2 != DC_DEVICEID_21143) 130183505Smarius return (ENXIO); 13154134Swpaul 13254134Swpaul device_set_desc(dev, "Intel 21143 NWAY media interface"); 13354134Swpaul 134160907Syongari return (BUS_PROBE_DEFAULT); 13554134Swpaul} 13654134Swpaul 137105135Salfredstatic int 138150763Simpdcphy_attach(device_t dev) 13954134Swpaul{ 14054134Swpaul struct mii_softc *sc; 14154134Swpaul struct mii_attach_args *ma; 14254134Swpaul struct mii_data *mii; 14354134Swpaul struct dc_softc *dc_sc; 144159201Sjhb device_t brdev; 14554134Swpaul 14654134Swpaul sc = device_get_softc(dev); 14754134Swpaul ma = device_get_ivars(dev); 14854134Swpaul sc->mii_dev = device_get_parent(dev); 14954134Swpaul mii = device_get_softc(sc->mii_dev); 15054134Swpaul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 15154134Swpaul 15254134Swpaul sc->mii_inst = mii->mii_instance; 15354134Swpaul sc->mii_phy = ma->mii_phyno; 15454134Swpaul sc->mii_service = dcphy_service; 15554134Swpaul sc->mii_pdata = mii; 15654134Swpaul 157190117Smarius /* 158190117Smarius * Apparently, we can neither isolate nor do loopback. 159190117Smarius */ 160190117Smarius sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP; 161190117Smarius 16254134Swpaul mii->mii_instance++; 16354134Swpaul 16454134Swpaul /*dcphy_reset(sc);*/ 16554134Swpaul dc_sc = mii->mii_ifp->if_softc; 16654134Swpaul CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0); 16754134Swpaul CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0); 16854134Swpaul 169159201Sjhb brdev = device_get_parent(sc->mii_dev); 170159201Sjhb switch (pci_get_subdevice(brdev) << 16 | pci_get_subvendor(brdev)) { 17154577Swpaul case COMPAQ_PRESARIO_ID: 17254134Swpaul /* Example of how to only allow 10Mbps modes. */ 173183505Smarius sc->mii_capabilities = BMSR_ANEG | BMSR_10TFDX | BMSR_10THDX; 17454134Swpaul break; 17554134Swpaul default: 176183505Smarius if (dc_sc->dc_pmode == DC_PMODE_SIA) 17766681Swpaul sc->mii_capabilities = 178183505Smarius BMSR_ANEG | BMSR_10TFDX | BMSR_10THDX; 179183505Smarius else 18066681Swpaul sc->mii_capabilities = 181183505Smarius BMSR_ANEG | BMSR_100TXFDX | BMSR_100TXHDX | 182183505Smarius BMSR_10TFDX | BMSR_10THDX; 18354134Swpaul break; 18454134Swpaul } 18554134Swpaul 18654134Swpaul sc->mii_capabilities &= ma->mii_capmask; 18754134Swpaul device_printf(dev, " "); 188190117Smarius mii_phy_add_media(sc); 18954134Swpaul printf("\n"); 19054134Swpaul 19154134Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 192183505Smarius return (0); 19354134Swpaul} 19454134Swpaul 19584145Sjlemonstatic int 196150763Simpdcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 19754134Swpaul{ 19854134Swpaul struct dc_softc *dc_sc; 19954134Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 20054134Swpaul int reg; 20154134Swpaul u_int32_t mode; 20254134Swpaul 20354134Swpaul dc_sc = mii->mii_ifp->if_softc; 20454134Swpaul 20554134Swpaul switch (cmd) { 20654134Swpaul case MII_POLLSTAT: 20754134Swpaul /* 20854134Swpaul * If we're not polling our PHY instance, just return. 20954134Swpaul */ 210183505Smarius if (IFM_INST(ife->ifm_media) != sc->mii_inst) 21154134Swpaul return (0); 21254134Swpaul break; 21354134Swpaul 21454134Swpaul case MII_MEDIACHG: 21554134Swpaul /* 21654134Swpaul * If the media indicates a different PHY instance, 21754134Swpaul * isolate ourselves. 21854134Swpaul */ 219183505Smarius if (IFM_INST(ife->ifm_media) != sc->mii_inst) 22054134Swpaul return (0); 22154134Swpaul 22254134Swpaul /* 22354134Swpaul * If the interface is not up, don't do anything. 22454134Swpaul */ 22554134Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 22654134Swpaul break; 22754134Swpaul 22854134Swpaul sc->mii_flags = 0; 22954134Swpaul mii->mii_media_active = IFM_NONE; 23054134Swpaul mode = CSR_READ_4(dc_sc, DC_NETCFG); 231183505Smarius mode &= ~(DC_NETCFG_FULLDUPLEX | DC_NETCFG_PORTSEL | 232183505Smarius DC_NETCFG_PCS | DC_NETCFG_SCRAMBLER | DC_NETCFG_SPEEDSEL); 23354134Swpaul 23454134Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 23554134Swpaul case IFM_AUTO: 23654134Swpaul /*dcphy_reset(sc);*/ 23796026Sphk (void) dcphy_auto(sc); 23854134Swpaul break; 23954134Swpaul case IFM_100_T4: 24054134Swpaul /* 24154134Swpaul * XXX Not supported as a manual setting right now. 24254134Swpaul */ 24354134Swpaul return (EINVAL); 24454134Swpaul case IFM_100_TX: 24554134Swpaul dcphy_reset(sc); 24654134Swpaul DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 247183505Smarius mode |= DC_NETCFG_PORTSEL | DC_NETCFG_PCS | 24854134Swpaul DC_NETCFG_SCRAMBLER; 24954134Swpaul if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 25054134Swpaul mode |= DC_NETCFG_FULLDUPLEX; 25154134Swpaul else 25254134Swpaul mode &= ~DC_NETCFG_FULLDUPLEX; 25354134Swpaul CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 25454134Swpaul break; 25554134Swpaul case IFM_10_T: 25654134Swpaul DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 25754134Swpaul DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF); 25854134Swpaul if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 25954134Swpaul DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D); 26054134Swpaul else 26154134Swpaul DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F); 26254134Swpaul DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 26354134Swpaul DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 26454134Swpaul mode &= ~DC_NETCFG_PORTSEL; 26554134Swpaul mode |= DC_NETCFG_SPEEDSEL; 26654134Swpaul if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 26754134Swpaul mode |= DC_NETCFG_FULLDUPLEX; 26854134Swpaul else 26954134Swpaul mode &= ~DC_NETCFG_FULLDUPLEX; 27054134Swpaul CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 27154134Swpaul break; 27254134Swpaul default: 273183505Smarius return (EINVAL); 27454134Swpaul } 27554134Swpaul break; 27654134Swpaul 27754134Swpaul case MII_TICK: 27854134Swpaul /* 27954134Swpaul * If we're not currently selected, just return. 28054134Swpaul */ 28154134Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 28254134Swpaul return (0); 28354134Swpaul 28454134Swpaul /* 28584145Sjlemon * Is the interface even up? 28654134Swpaul */ 28784145Sjlemon if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 28854134Swpaul return (0); 28954134Swpaul 29054134Swpaul /* 29184145Sjlemon * Only used for autonegotiation. 29254134Swpaul */ 29384145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 29484145Sjlemon break; 29554134Swpaul 29694994Smckay reg = CSR_READ_4(dc_sc, DC_10BTSTAT); 29761110Swpaul if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 29884145Sjlemon break; 29954134Swpaul 30061110Swpaul /* 30161110Swpaul * Only retry autonegotiation every 5 seconds. 30294994Smckay * 30394994Smckay * Otherwise, fall through to calling dcphy_status() 30494994Smckay * since real Intel 21143 chips don't show valid link 30594994Smckay * status until autonegotiation is switched off, and 30694994Smckay * that only happens in dcphy_status(). Without this, 307129845Smarius * successful autonegotiation is never recognised on 30894994Smckay * these chips. 30961110Swpaul */ 310128870Sandre if (++sc->mii_ticks <= 50) 31194994Smckay break; 31261110Swpaul 31354134Swpaul sc->mii_ticks = 0; 31496026Sphk dcphy_auto(sc); 31554134Swpaul 31654134Swpaul break; 31754134Swpaul } 31854134Swpaul 31954134Swpaul /* Update the media status. */ 32054134Swpaul dcphy_status(sc); 32154134Swpaul 32254134Swpaul /* Callback if something changed. */ 32384145Sjlemon mii_phy_update(sc, cmd); 32454134Swpaul return (0); 32554134Swpaul} 32654134Swpaul 32784145Sjlemonstatic void 328150763Simpdcphy_status(struct mii_softc *sc) 32954134Swpaul{ 33054134Swpaul struct mii_data *mii = sc->mii_pdata; 33161110Swpaul int reg, anlpar, tstat = 0; 33254134Swpaul struct dc_softc *dc_sc; 33354134Swpaul 33454134Swpaul dc_sc = mii->mii_ifp->if_softc; 33554134Swpaul 33654134Swpaul mii->mii_media_status = IFM_AVALID; 33754134Swpaul mii->mii_media_active = IFM_ETHER; 33854134Swpaul 33961290Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 34061290Swpaul return; 34161290Swpaul 34294994Smckay reg = CSR_READ_4(dc_sc, DC_10BTSTAT); 34354134Swpaul if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 34454134Swpaul mii->mii_media_status |= IFM_ACTIVE; 34554134Swpaul 34661110Swpaul if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) { 34754134Swpaul /* Erg, still trying, I guess... */ 34861110Swpaul tstat = CSR_READ_4(dc_sc, DC_10BTSTAT); 34961110Swpaul if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) { 35061110Swpaul if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) && 35161110Swpaul (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE) 35261110Swpaul goto skip; 35354134Swpaul mii->mii_media_active |= IFM_NONE; 35454134Swpaul return; 35554134Swpaul } 35654134Swpaul 35761110Swpaul if (tstat & DC_TSTAT_LP_CAN_NWAY) { 35861110Swpaul anlpar = tstat >> 16; 359173665Syongari if (anlpar & ANLPAR_TX_FD && 36061110Swpaul sc->mii_capabilities & BMSR_100TXFDX) 361183505Smarius mii->mii_media_active |= IFM_100_TX | IFM_FDX; 362173665Syongari else if (anlpar & ANLPAR_T4 && 363173665Syongari sc->mii_capabilities & BMSR_100T4) 364173665Syongari mii->mii_media_active |= IFM_100_T4; 36554577Swpaul else if (anlpar & ANLPAR_TX && 36654577Swpaul sc->mii_capabilities & BMSR_100TXHDX) 36754134Swpaul mii->mii_media_active |= IFM_100_TX; 36854134Swpaul else if (anlpar & ANLPAR_10_FD) 369183505Smarius mii->mii_media_active |= IFM_10_T | IFM_FDX; 37054134Swpaul else if (anlpar & ANLPAR_10) 37154134Swpaul mii->mii_media_active |= IFM_10_T; 37254134Swpaul else 37354134Swpaul mii->mii_media_active |= IFM_NONE; 37454134Swpaul if (DC_IS_INTEL(dc_sc)) 37554134Swpaul DC_CLRBIT(dc_sc, DC_10BTCTRL, 37654134Swpaul DC_TCTL_AUTONEGENBL); 37754134Swpaul return; 37854134Swpaul } 379183505Smarius 38054134Swpaul /* 38154134Swpaul * If the other side doesn't support NWAY, then the 38254134Swpaul * best we can do is determine if we have a 10Mbps or 383183505Smarius * 100Mbps link. There's no way to know if the link 38454134Swpaul * is full or half duplex, so we default to half duplex 38554134Swpaul * and hope that the user is clever enough to manually 38654134Swpaul * change the media settings if we're wrong. 38754134Swpaul */ 38854134Swpaul if (!(reg & DC_TSTAT_LS100)) 38954134Swpaul mii->mii_media_active |= IFM_100_TX; 39054134Swpaul else if (!(reg & DC_TSTAT_LS10)) 39154134Swpaul mii->mii_media_active |= IFM_10_T; 39254134Swpaul else 39354134Swpaul mii->mii_media_active |= IFM_NONE; 39454134Swpaul if (DC_IS_INTEL(dc_sc)) 39554134Swpaul DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 39654134Swpaul return; 39754134Swpaul } 39854134Swpaul 39961110Swpaulskip: 40066681Swpaul if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) 40166681Swpaul mii->mii_media_active |= IFM_10_T; 40266681Swpaul else 40354134Swpaul mii->mii_media_active |= IFM_100_TX; 40454134Swpaul if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) 40554134Swpaul mii->mii_media_active |= IFM_FDX; 40654134Swpaul} 40754134Swpaul 40854134Swpaulstatic int 409150763Simpdcphy_auto(struct mii_softc *mii) 41054134Swpaul{ 41154134Swpaul struct dc_softc *sc; 41254134Swpaul 41354134Swpaul sc = mii->mii_pdata->mii_ifp->if_softc; 41454134Swpaul 41596026Sphk DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); 41696026Sphk DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); 41796026Sphk DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 41896026Sphk if (mii->mii_capabilities & BMSR_100TXHDX) 41996026Sphk CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF); 42096026Sphk else 42196026Sphk CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF); 42296026Sphk DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 42396026Sphk DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 42496026Sphk DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE); 42554134Swpaul 426183505Smarius return (EJUSTRETURN); 42754134Swpaul} 42854134Swpaul 42954134Swpaulstatic void 430150763Simpdcphy_reset(struct mii_softc *mii) 43154134Swpaul{ 43254134Swpaul struct dc_softc *sc; 43354134Swpaul 43454134Swpaul sc = mii->mii_pdata->mii_ifp->if_softc; 43554134Swpaul 43654134Swpaul DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 43754134Swpaul DELAY(1000); 43854134Swpaul DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 43954134Swpaul} 440