1216828Syongari/*- 2216828Syongari * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org> 3216828Syongari * All rights reserved. 4216828Syongari * 5216828Syongari * Redistribution and use in source and binary forms, with or without 6216828Syongari * modification, are permitted provided that the following conditions 7216828Syongari * are met: 8216828Syongari * 1. Redistributions of source code must retain the above copyright 9216828Syongari * notice unmodified, this list of conditions, and the following 10216828Syongari * disclaimer. 11216828Syongari * 2. Redistributions in binary form must reproduce the above copyright 12216828Syongari * notice, this list of conditions and the following disclaimer in the 13216828Syongari * documentation and/or other materials provided with the distribution. 14216828Syongari * 15216828Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16216828Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17216828Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18216828Syongari * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19216828Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20216828Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21216828Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22216828Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23216828Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24216828Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25216828Syongari * SUCH DAMAGE. 26216828Syongari */ 27216828Syongari 28216828Syongari#include <sys/cdefs.h> 29216828Syongari__FBSDID("$FreeBSD$"); 30216828Syongari 31216828Syongari/* 32216828Syongari * Driver for the RDC Semiconductor R6040 10/100 PHY. 33216828Syongari */ 34216828Syongari 35216828Syongari#include <sys/param.h> 36216828Syongari#include <sys/systm.h> 37216828Syongari#include <sys/kernel.h> 38216828Syongari#include <sys/module.h> 39216828Syongari#include <sys/socket.h> 40216828Syongari#include <sys/bus.h> 41216828Syongari 42216828Syongari#include <net/if.h> 43216828Syongari#include <net/if_media.h> 44216828Syongari 45216828Syongari#include <dev/mii/mii.h> 46216828Syongari#include <dev/mii/miivar.h> 47216828Syongari#include "miidevs.h" 48216828Syongari 49216828Syongari#include <dev/mii/rdcphyreg.h> 50216828Syongari 51216828Syongari#include "miibus_if.h" 52216828Syongari 53216828Syongaristatic device_probe_t rdcphy_probe; 54216828Syongaristatic device_attach_t rdcphy_attach; 55216828Syongari 56216828Syongaristruct rdcphy_softc { 57216828Syongari struct mii_softc mii_sc; 58216828Syongari int mii_link_tick; 59216828Syongari#define RDCPHY_MANNEG_TICK 3 60216828Syongari}; 61216828Syongari 62216828Syongaristatic device_method_t rdcphy_methods[] = { 63216828Syongari /* device interface */ 64216828Syongari DEVMETHOD(device_probe, rdcphy_probe), 65216828Syongari DEVMETHOD(device_attach, rdcphy_attach), 66216828Syongari DEVMETHOD(device_detach, mii_phy_detach), 67216828Syongari DEVMETHOD(device_shutdown, bus_generic_shutdown), 68227848Smarius DEVMETHOD_END 69216828Syongari}; 70216828Syongari 71216828Syongaristatic devclass_t rdcphy_devclass; 72216828Syongari 73216828Syongaristatic driver_t rdcphy_driver = { 74216828Syongari "rdcphy", 75216828Syongari rdcphy_methods, 76216828Syongari sizeof(struct rdcphy_softc) 77216828Syongari}; 78216828Syongari 79216828SyongariDRIVER_MODULE(rdcphy, miibus, rdcphy_driver, rdcphy_devclass, 0, 0); 80216828Syongari 81216828Syongaristatic int rdcphy_service(struct mii_softc *, struct mii_data *, int); 82216828Syongaristatic void rdcphy_status(struct mii_softc *); 83216828Syongari 84216828Syongaristatic const struct mii_phydesc rdcphys[] = { 85216828Syongari MII_PHY_DESC(RDC, R6040), 86216828Syongari MII_PHY_END 87216828Syongari}; 88216828Syongari 89221407Smariusstatic const struct mii_phy_funcs rdcphy_funcs = { 90221407Smarius rdcphy_service, 91221407Smarius rdcphy_status, 92221407Smarius mii_phy_reset 93221407Smarius}; 94221407Smarius 95216828Syongaristatic int 96216828Syongarirdcphy_probe(device_t dev) 97216828Syongari{ 98216828Syongari 99216828Syongari return (mii_phy_dev_probe(dev, rdcphys, BUS_PROBE_DEFAULT)); 100216828Syongari} 101216828Syongari 102216828Syongaristatic int 103216828Syongarirdcphy_attach(device_t dev) 104216828Syongari{ 105216828Syongari 106221407Smarius mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &rdcphy_funcs, 1); 107216828Syongari return (0); 108216828Syongari} 109216828Syongari 110216828Syongaristatic int 111216828Syongarirdcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 112216828Syongari{ 113216828Syongari struct rdcphy_softc *rsc; 114216828Syongari struct ifmedia_entry *ife; 115216828Syongari 116216828Syongari rsc = (struct rdcphy_softc *)sc; 117216828Syongari ife = mii->mii_media.ifm_cur; 118216828Syongari 119216828Syongari switch (cmd) { 120216828Syongari case MII_POLLSTAT: 121216828Syongari break; 122216828Syongari 123216828Syongari case MII_MEDIACHG: 124216828Syongari /* 125216828Syongari * If the interface is not up, don't do anything. 126216828Syongari */ 127216828Syongari if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 128216828Syongari break; 129216828Syongari 130216828Syongari mii_phy_setmedia(sc); 131216828Syongari switch (IFM_SUBTYPE(ife->ifm_media)) { 132216828Syongari case IFM_100_TX: 133216828Syongari case IFM_10_T: 134216828Syongari /* 135216828Syongari * Report fake lost link event to parent 136216828Syongari * driver. This will stop MAC of parent 137216828Syongari * driver and make it possible to reconfigure 138216828Syongari * MAC after completion of link establishment. 139216828Syongari * Note, the parent MAC seems to require 140216828Syongari * restarting MAC when underlying any PHY 141216828Syongari * configuration was changed even if the 142216828Syongari * resolved speed/duplex was not changed at 143216828Syongari * all. 144216828Syongari */ 145216828Syongari mii->mii_media_status = 0; 146216828Syongari mii->mii_media_active = IFM_ETHER | IFM_NONE; 147216828Syongari rsc->mii_link_tick = RDCPHY_MANNEG_TICK; 148216828Syongari /* Immediately report link down. */ 149216828Syongari mii_phy_update(sc, MII_MEDIACHG); 150216828Syongari return (0); 151216828Syongari default: 152216828Syongari break; 153216828Syongari } 154216828Syongari break; 155216828Syongari 156216828Syongari case MII_TICK: 157216828Syongari if (mii_phy_tick(sc) == EJUSTRETURN) 158216828Syongari return (0); 159216828Syongari if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 160216828Syongari /* 161216828Syongari * It seems the PHY hardware does not correctly 162216828Syongari * report link status changes when manual link 163216828Syongari * configuration is in progress. It is also 164216828Syongari * possible for the PHY to complete establishing 165216828Syongari * a link within one second such that mii(4) 166216828Syongari * did not notice the link change. To workaround 167216828Syongari * the issue, emulate lost link event and wait 168216828Syongari * for 3 seconds when manual link configuration 169216828Syongari * is in progress. 3 seconds would be long 170216828Syongari * enough to absorb transient link flips. 171216828Syongari */ 172216828Syongari if (rsc->mii_link_tick > 0) { 173216828Syongari rsc->mii_link_tick--; 174216828Syongari return (0); 175216828Syongari } 176216828Syongari } 177216828Syongari break; 178216828Syongari } 179216828Syongari 180216828Syongari /* Update the media status. */ 181221407Smarius PHY_STATUS(sc); 182216828Syongari 183216828Syongari /* Callback if something changed. */ 184216828Syongari mii_phy_update(sc, cmd); 185216828Syongari return (0); 186216828Syongari} 187216828Syongari 188216828Syongaristatic void 189216828Syongarirdcphy_status(struct mii_softc *sc) 190216828Syongari{ 191216828Syongari struct mii_data *mii; 192216828Syongari struct ifmedia_entry *ife; 193216828Syongari int bmsr, bmcr, physts; 194216828Syongari 195216828Syongari mii = sc->mii_pdata; 196216828Syongari ife = mii->mii_media.ifm_cur; 197216828Syongari 198216828Syongari mii->mii_media_status = IFM_AVALID; 199216828Syongari mii->mii_media_active = IFM_ETHER; 200216828Syongari 201216828Syongari bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 202216828Syongari physts = PHY_READ(sc, MII_RDCPHY_STATUS); 203216828Syongari 204216828Syongari if ((physts & STATUS_LINK_UP) != 0) 205216828Syongari mii->mii_media_status |= IFM_ACTIVE; 206216828Syongari 207216828Syongari bmcr = PHY_READ(sc, MII_BMCR); 208216828Syongari if ((bmcr & BMCR_ISO) != 0) { 209216828Syongari mii->mii_media_active |= IFM_NONE; 210216828Syongari mii->mii_media_status = 0; 211216828Syongari return; 212216828Syongari } 213216828Syongari 214216828Syongari if ((bmcr & BMCR_LOOP) != 0) 215216828Syongari mii->mii_media_active |= IFM_LOOP; 216216828Syongari 217216828Syongari if ((bmcr & BMCR_AUTOEN) != 0) { 218216828Syongari if ((bmsr & BMSR_ACOMP) == 0) { 219216828Syongari /* Erg, still trying, I guess... */ 220216828Syongari mii->mii_media_active |= IFM_NONE; 221216828Syongari return; 222216828Syongari } 223216828Syongari } 224216828Syongari 225216828Syongari switch (physts & STATUS_SPEED_MASK) { 226216828Syongari case STATUS_SPEED_100: 227216828Syongari mii->mii_media_active |= IFM_100_TX; 228216828Syongari break; 229216828Syongari case STATUS_SPEED_10: 230216828Syongari mii->mii_media_active |= IFM_10_T; 231216828Syongari break; 232216828Syongari default: 233216828Syongari mii->mii_media_active |= IFM_NONE; 234216828Syongari return; 235216828Syongari } 236216828Syongari if ((physts & STATUS_FULL_DUPLEX) != 0) 237216828Syongari mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 238216828Syongari else 239216828Syongari mii->mii_media_active |= IFM_HDX; 240216828Syongari} 241