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: releng/11.0/sys/dev/mii/rdcphy.c 257184 2013-10-26 18:40:17Z glebius $"); 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 mii_phy_setmedia(sc); 125216828Syongari switch (IFM_SUBTYPE(ife->ifm_media)) { 126216828Syongari case IFM_100_TX: 127216828Syongari case IFM_10_T: 128216828Syongari /* 129216828Syongari * Report fake lost link event to parent 130216828Syongari * driver. This will stop MAC of parent 131216828Syongari * driver and make it possible to reconfigure 132216828Syongari * MAC after completion of link establishment. 133216828Syongari * Note, the parent MAC seems to require 134216828Syongari * restarting MAC when underlying any PHY 135216828Syongari * configuration was changed even if the 136216828Syongari * resolved speed/duplex was not changed at 137216828Syongari * all. 138216828Syongari */ 139216828Syongari mii->mii_media_status = 0; 140216828Syongari mii->mii_media_active = IFM_ETHER | IFM_NONE; 141216828Syongari rsc->mii_link_tick = RDCPHY_MANNEG_TICK; 142216828Syongari /* Immediately report link down. */ 143216828Syongari mii_phy_update(sc, MII_MEDIACHG); 144216828Syongari return (0); 145216828Syongari default: 146216828Syongari break; 147216828Syongari } 148216828Syongari break; 149216828Syongari 150216828Syongari case MII_TICK: 151216828Syongari if (mii_phy_tick(sc) == EJUSTRETURN) 152216828Syongari return (0); 153216828Syongari if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 154216828Syongari /* 155216828Syongari * It seems the PHY hardware does not correctly 156216828Syongari * report link status changes when manual link 157216828Syongari * configuration is in progress. It is also 158216828Syongari * possible for the PHY to complete establishing 159216828Syongari * a link within one second such that mii(4) 160216828Syongari * did not notice the link change. To workaround 161216828Syongari * the issue, emulate lost link event and wait 162216828Syongari * for 3 seconds when manual link configuration 163216828Syongari * is in progress. 3 seconds would be long 164216828Syongari * enough to absorb transient link flips. 165216828Syongari */ 166216828Syongari if (rsc->mii_link_tick > 0) { 167216828Syongari rsc->mii_link_tick--; 168216828Syongari return (0); 169216828Syongari } 170216828Syongari } 171216828Syongari break; 172216828Syongari } 173216828Syongari 174216828Syongari /* Update the media status. */ 175221407Smarius PHY_STATUS(sc); 176216828Syongari 177216828Syongari /* Callback if something changed. */ 178216828Syongari mii_phy_update(sc, cmd); 179216828Syongari return (0); 180216828Syongari} 181216828Syongari 182216828Syongaristatic void 183216828Syongarirdcphy_status(struct mii_softc *sc) 184216828Syongari{ 185216828Syongari struct mii_data *mii; 186216828Syongari struct ifmedia_entry *ife; 187216828Syongari int bmsr, bmcr, physts; 188216828Syongari 189216828Syongari mii = sc->mii_pdata; 190216828Syongari ife = mii->mii_media.ifm_cur; 191216828Syongari 192216828Syongari mii->mii_media_status = IFM_AVALID; 193216828Syongari mii->mii_media_active = IFM_ETHER; 194216828Syongari 195216828Syongari bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 196216828Syongari physts = PHY_READ(sc, MII_RDCPHY_STATUS); 197216828Syongari 198216828Syongari if ((physts & STATUS_LINK_UP) != 0) 199216828Syongari mii->mii_media_status |= IFM_ACTIVE; 200216828Syongari 201216828Syongari bmcr = PHY_READ(sc, MII_BMCR); 202216828Syongari if ((bmcr & BMCR_ISO) != 0) { 203216828Syongari mii->mii_media_active |= IFM_NONE; 204216828Syongari mii->mii_media_status = 0; 205216828Syongari return; 206216828Syongari } 207216828Syongari 208216828Syongari if ((bmcr & BMCR_LOOP) != 0) 209216828Syongari mii->mii_media_active |= IFM_LOOP; 210216828Syongari 211216828Syongari if ((bmcr & BMCR_AUTOEN) != 0) { 212216828Syongari if ((bmsr & BMSR_ACOMP) == 0) { 213216828Syongari /* Erg, still trying, I guess... */ 214216828Syongari mii->mii_media_active |= IFM_NONE; 215216828Syongari return; 216216828Syongari } 217216828Syongari } 218216828Syongari 219216828Syongari switch (physts & STATUS_SPEED_MASK) { 220216828Syongari case STATUS_SPEED_100: 221216828Syongari mii->mii_media_active |= IFM_100_TX; 222216828Syongari break; 223216828Syongari case STATUS_SPEED_10: 224216828Syongari mii->mii_media_active |= IFM_10_T; 225216828Syongari break; 226216828Syongari default: 227216828Syongari mii->mii_media_active |= IFM_NONE; 228216828Syongari return; 229216828Syongari } 230216828Syongari if ((physts & STATUS_FULL_DUPLEX) != 0) 231216828Syongari mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 232216828Syongari else 233216828Syongari mii->mii_media_active |= IFM_HDX; 234216828Syongari} 235