162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PHY drivers for the sungem ethernet driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file could be shared with other drivers. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (c) 2002-2007, Benjamin Herrenscmidt (benh@kernel.crashing.org) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * TODO: 1062306a36Sopenharmony_ci * - Add support for PHYs that provide an IRQ line 1162306a36Sopenharmony_ci * - Eventually moved the entire polling state machine in 1262306a36Sopenharmony_ci * there (out of the eth driver), so that it can easily be 1362306a36Sopenharmony_ci * skipped on PHYs that implement it in hardware. 1462306a36Sopenharmony_ci * - On LXT971 & BCM5201, Apple uses some chip specific regs 1562306a36Sopenharmony_ci * to read the link status. Figure out why and if it makes 1662306a36Sopenharmony_ci * sense to do the same (magic aneg ?) 1762306a36Sopenharmony_ci * - Apple has some additional power management code for some 1862306a36Sopenharmony_ci * Broadcom PHYs that they "hide" from the OpenSource version 1962306a36Sopenharmony_ci * of darwin, still need to reverse engineer that 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/types.h> 2762306a36Sopenharmony_ci#include <linux/netdevice.h> 2862306a36Sopenharmony_ci#include <linux/etherdevice.h> 2962306a36Sopenharmony_ci#include <linux/mii.h> 3062306a36Sopenharmony_ci#include <linux/ethtool.h> 3162306a36Sopenharmony_ci#include <linux/delay.h> 3262306a36Sopenharmony_ci#include <linux/of.h> 3362306a36Sopenharmony_ci#include <linux/sungem_phy.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Link modes of the BCM5400 PHY */ 3662306a36Sopenharmony_cistatic const int phy_BCM5400_link_table[8][3] = { 3762306a36Sopenharmony_ci { 0, 0, 0 }, /* No link */ 3862306a36Sopenharmony_ci { 0, 0, 0 }, /* 10BT Half Duplex */ 3962306a36Sopenharmony_ci { 1, 0, 0 }, /* 10BT Full Duplex */ 4062306a36Sopenharmony_ci { 0, 1, 0 }, /* 100BT Half Duplex */ 4162306a36Sopenharmony_ci { 0, 1, 0 }, /* 100BT Half Duplex */ 4262306a36Sopenharmony_ci { 1, 1, 0 }, /* 100BT Full Duplex*/ 4362306a36Sopenharmony_ci { 1, 0, 1 }, /* 1000BT */ 4462306a36Sopenharmony_ci { 1, 0, 1 }, /* 1000BT */ 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic inline int __sungem_phy_read(struct mii_phy* phy, int id, int reg) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci return phy->mdio_read(phy->dev, id, reg); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline void __sungem_phy_write(struct mii_phy* phy, int id, int reg, int val) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci phy->mdio_write(phy->dev, id, reg, val); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic inline int sungem_phy_read(struct mii_phy* phy, int reg) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return phy->mdio_read(phy->dev, phy->mii_id, reg); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic inline void sungem_phy_write(struct mii_phy* phy, int reg, int val) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci phy->mdio_write(phy->dev, phy->mii_id, reg, val); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int reset_one_mii_phy(struct mii_phy* phy, int phy_id) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci u16 val; 7062306a36Sopenharmony_ci int limit = 10000; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci val = __sungem_phy_read(phy, phy_id, MII_BMCR); 7362306a36Sopenharmony_ci val &= ~(BMCR_ISOLATE | BMCR_PDOWN); 7462306a36Sopenharmony_ci val |= BMCR_RESET; 7562306a36Sopenharmony_ci __sungem_phy_write(phy, phy_id, MII_BMCR, val); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci udelay(100); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci while (--limit) { 8062306a36Sopenharmony_ci val = __sungem_phy_read(phy, phy_id, MII_BMCR); 8162306a36Sopenharmony_ci if ((val & BMCR_RESET) == 0) 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci udelay(10); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci if ((val & BMCR_ISOLATE) && limit > 0) 8662306a36Sopenharmony_ci __sungem_phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return limit <= 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int bcm5201_init(struct mii_phy* phy) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci u16 data; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5201_MULTIPHY); 9662306a36Sopenharmony_ci data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE; 9762306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5201_MULTIPHY, data); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int bcm5201_suspend(struct mii_phy* phy) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0); 10762306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int bcm5221_init(struct mii_phy* phy) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u16 data; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_TEST); 11762306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_TEST, 11862306a36Sopenharmony_ci data | MII_BCM5221_TEST_ENABLE_SHADOWS); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2); 12162306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2, 12262306a36Sopenharmony_ci data | MII_BCM5221_SHDOW_AUX_STAT2_APD); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4); 12562306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4, 12662306a36Sopenharmony_ci data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_TEST); 12962306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_TEST, 13062306a36Sopenharmony_ci data & ~MII_BCM5221_TEST_ENABLE_SHADOWS); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int bcm5221_suspend(struct mii_phy* phy) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u16 data; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_TEST); 14062306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_TEST, 14162306a36Sopenharmony_ci data | MII_BCM5221_TEST_ENABLE_SHADOWS); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4); 14462306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4, 14562306a36Sopenharmony_ci data | MII_BCM5221_SHDOW_AUX_MODE4_IDDQMODE); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int bcm5241_init(struct mii_phy* phy) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u16 data; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_TEST); 15562306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_TEST, 15662306a36Sopenharmony_ci data | MII_BCM5221_TEST_ENABLE_SHADOWS); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2); 15962306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2, 16062306a36Sopenharmony_ci data | MII_BCM5221_SHDOW_AUX_STAT2_APD); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4); 16362306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4, 16462306a36Sopenharmony_ci data & ~MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_TEST); 16762306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_TEST, 16862306a36Sopenharmony_ci data & ~MII_BCM5221_TEST_ENABLE_SHADOWS); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int bcm5241_suspend(struct mii_phy* phy) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci u16 data; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_TEST); 17862306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_TEST, 17962306a36Sopenharmony_ci data | MII_BCM5221_TEST_ENABLE_SHADOWS); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4); 18262306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4, 18362306a36Sopenharmony_ci data | MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int bcm5400_init(struct mii_phy* phy) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci u16 data; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Configure for gigabit full duplex */ 19362306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL); 19462306a36Sopenharmony_ci data |= MII_BCM5400_AUXCONTROL_PWR10BASET; 19562306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL); 19862306a36Sopenharmony_ci data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; 19962306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci udelay(100); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Reset and configure cascaded 10/100 PHY */ 20462306a36Sopenharmony_ci (void)reset_one_mii_phy(phy, 0x1f); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY); 20762306a36Sopenharmony_ci data |= MII_BCM5201_MULTIPHY_SERIALMODE; 20862306a36Sopenharmony_ci __sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL); 21162306a36Sopenharmony_ci data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET; 21262306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int bcm5400_suspend(struct mii_phy* phy) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci#if 0 /* Commented out in Darwin... someone has those dawn docs ? */ 22062306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN); 22162306a36Sopenharmony_ci#endif 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int bcm5401_init(struct mii_phy* phy) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci u16 data; 22862306a36Sopenharmony_ci int rev; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f; 23162306a36Sopenharmony_ci if (rev == 0 || rev == 3) { 23262306a36Sopenharmony_ci /* Some revisions of 5401 appear to need this 23362306a36Sopenharmony_ci * initialisation sequence to disable, according 23462306a36Sopenharmony_ci * to OF, "tap power management" 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * WARNING ! OF and Darwin don't agree on the 23762306a36Sopenharmony_ci * register addresses. OF seem to interpret the 23862306a36Sopenharmony_ci * register numbers below as decimal 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Note: This should (and does) match tg3_init_5401phy_dsp 24162306a36Sopenharmony_ci * in the tg3.c driver. -DaveM 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci sungem_phy_write(phy, 0x18, 0x0c20); 24462306a36Sopenharmony_ci sungem_phy_write(phy, 0x17, 0x0012); 24562306a36Sopenharmony_ci sungem_phy_write(phy, 0x15, 0x1804); 24662306a36Sopenharmony_ci sungem_phy_write(phy, 0x17, 0x0013); 24762306a36Sopenharmony_ci sungem_phy_write(phy, 0x15, 0x1204); 24862306a36Sopenharmony_ci sungem_phy_write(phy, 0x17, 0x8006); 24962306a36Sopenharmony_ci sungem_phy_write(phy, 0x15, 0x0132); 25062306a36Sopenharmony_ci sungem_phy_write(phy, 0x17, 0x8006); 25162306a36Sopenharmony_ci sungem_phy_write(phy, 0x15, 0x0232); 25262306a36Sopenharmony_ci sungem_phy_write(phy, 0x17, 0x201f); 25362306a36Sopenharmony_ci sungem_phy_write(phy, 0x15, 0x0a20); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Configure for gigabit full duplex */ 25762306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL); 25862306a36Sopenharmony_ci data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; 25962306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci udelay(10); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Reset and configure cascaded 10/100 PHY */ 26462306a36Sopenharmony_ci (void)reset_one_mii_phy(phy, 0x1f); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY); 26762306a36Sopenharmony_ci data |= MII_BCM5201_MULTIPHY_SERIALMODE; 26862306a36Sopenharmony_ci __sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int bcm5401_suspend(struct mii_phy* phy) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci#if 0 /* Commented out in Darwin... someone has those dawn docs ? */ 27662306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN); 27762306a36Sopenharmony_ci#endif 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int bcm5411_init(struct mii_phy* phy) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci u16 data; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Here's some more Apple black magic to setup 28662306a36Sopenharmony_ci * some voltage stuffs. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci sungem_phy_write(phy, 0x1c, 0x8c23); 28962306a36Sopenharmony_ci sungem_phy_write(phy, 0x1c, 0x8ca3); 29062306a36Sopenharmony_ci sungem_phy_write(phy, 0x1c, 0x8c23); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Here, Apple seems to want to reset it, do 29362306a36Sopenharmony_ci * it as well 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, BMCR_RESET); 29662306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, 0x1340); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL); 29962306a36Sopenharmony_ci data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; 30062306a36Sopenharmony_ci sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci udelay(10); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* Reset and configure cascaded 10/100 PHY */ 30562306a36Sopenharmony_ci (void)reset_one_mii_phy(phy, 0x1f); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci u16 ctl, adv; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci phy->autoneg = 1; 31562306a36Sopenharmony_ci phy->speed = SPEED_10; 31662306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 31762306a36Sopenharmony_ci phy->pause = 0; 31862306a36Sopenharmony_ci phy->advertising = advertise; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Setup standard advertise */ 32162306a36Sopenharmony_ci adv = sungem_phy_read(phy, MII_ADVERTISE); 32262306a36Sopenharmony_ci adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); 32362306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Half) 32462306a36Sopenharmony_ci adv |= ADVERTISE_10HALF; 32562306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Full) 32662306a36Sopenharmony_ci adv |= ADVERTISE_10FULL; 32762306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Half) 32862306a36Sopenharmony_ci adv |= ADVERTISE_100HALF; 32962306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Full) 33062306a36Sopenharmony_ci adv |= ADVERTISE_100FULL; 33162306a36Sopenharmony_ci sungem_phy_write(phy, MII_ADVERTISE, adv); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Start/Restart aneg */ 33462306a36Sopenharmony_ci ctl = sungem_phy_read(phy, MII_BMCR); 33562306a36Sopenharmony_ci ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); 33662306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci u16 ctl; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci phy->autoneg = 0; 34662306a36Sopenharmony_ci phy->speed = speed; 34762306a36Sopenharmony_ci phy->duplex = fd; 34862306a36Sopenharmony_ci phy->pause = 0; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci ctl = sungem_phy_read(phy, MII_BMCR); 35162306a36Sopenharmony_ci ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* First reset the PHY */ 35462306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Select speed & duplex */ 35762306a36Sopenharmony_ci switch(speed) { 35862306a36Sopenharmony_ci case SPEED_10: 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case SPEED_100: 36162306a36Sopenharmony_ci ctl |= BMCR_SPEED100; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci case SPEED_1000: 36462306a36Sopenharmony_ci default: 36562306a36Sopenharmony_ci return -EINVAL; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci if (fd == DUPLEX_FULL) 36862306a36Sopenharmony_ci ctl |= BMCR_FULLDPLX; 36962306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int genmii_poll_link(struct mii_phy *phy) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci u16 status; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci (void)sungem_phy_read(phy, MII_BMSR); 37962306a36Sopenharmony_ci status = sungem_phy_read(phy, MII_BMSR); 38062306a36Sopenharmony_ci if ((status & BMSR_LSTATUS) == 0) 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE)) 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci return 1; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int genmii_read_link(struct mii_phy *phy) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci u16 lpa; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (phy->autoneg) { 39262306a36Sopenharmony_ci lpa = sungem_phy_read(phy, MII_LPA); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (lpa & (LPA_10FULL | LPA_100FULL)) 39562306a36Sopenharmony_ci phy->duplex = DUPLEX_FULL; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 39862306a36Sopenharmony_ci if (lpa & (LPA_100FULL | LPA_100HALF)) 39962306a36Sopenharmony_ci phy->speed = SPEED_100; 40062306a36Sopenharmony_ci else 40162306a36Sopenharmony_ci phy->speed = SPEED_10; 40262306a36Sopenharmony_ci phy->pause = 0; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci /* On non-aneg, we assume what we put in BMCR is the speed, 40562306a36Sopenharmony_ci * though magic-aneg shouldn't prevent this case from occurring 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int generic_suspend(struct mii_phy* phy) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int bcm5421_init(struct mii_phy* phy) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u16 data; 42162306a36Sopenharmony_ci unsigned int id; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2)); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Revision 0 of 5421 needs some fixups */ 42662306a36Sopenharmony_ci if (id == 0x002060e0) { 42762306a36Sopenharmony_ci /* This is borrowed from MacOS 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci sungem_phy_write(phy, 0x18, 0x1007); 43062306a36Sopenharmony_ci data = sungem_phy_read(phy, 0x18); 43162306a36Sopenharmony_ci sungem_phy_write(phy, 0x18, data | 0x0400); 43262306a36Sopenharmony_ci sungem_phy_write(phy, 0x18, 0x0007); 43362306a36Sopenharmony_ci data = sungem_phy_read(phy, 0x18); 43462306a36Sopenharmony_ci sungem_phy_write(phy, 0x18, data | 0x0800); 43562306a36Sopenharmony_ci sungem_phy_write(phy, 0x17, 0x000a); 43662306a36Sopenharmony_ci data = sungem_phy_read(phy, 0x15); 43762306a36Sopenharmony_ci sungem_phy_write(phy, 0x15, data | 0x0200); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Pick up some init code from OF for K2 version */ 44162306a36Sopenharmony_ci if ((id & 0xfffffff0) == 0x002062e0) { 44262306a36Sopenharmony_ci sungem_phy_write(phy, 4, 0x01e1); 44362306a36Sopenharmony_ci sungem_phy_write(phy, 9, 0x0300); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Check if we can enable automatic low power */ 44762306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 44862306a36Sopenharmony_ci if (phy->platform_data) { 44962306a36Sopenharmony_ci struct device_node *np = of_get_parent(phy->platform_data); 45062306a36Sopenharmony_ci int can_low_power = 1; 45162306a36Sopenharmony_ci if (np == NULL || of_get_property(np, "no-autolowpower", NULL)) 45262306a36Sopenharmony_ci can_low_power = 0; 45362306a36Sopenharmony_ci of_node_put(np); 45462306a36Sopenharmony_ci if (can_low_power) { 45562306a36Sopenharmony_ci /* Enable automatic low-power */ 45662306a36Sopenharmony_ci sungem_phy_write(phy, 0x1c, 0x9002); 45762306a36Sopenharmony_ci sungem_phy_write(phy, 0x1c, 0xa821); 45862306a36Sopenharmony_ci sungem_phy_write(phy, 0x1c, 0x941d); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci u16 ctl, adv; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci phy->autoneg = 1; 47162306a36Sopenharmony_ci phy->speed = SPEED_10; 47262306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 47362306a36Sopenharmony_ci phy->pause = 0; 47462306a36Sopenharmony_ci phy->advertising = advertise; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Setup standard advertise */ 47762306a36Sopenharmony_ci adv = sungem_phy_read(phy, MII_ADVERTISE); 47862306a36Sopenharmony_ci adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); 47962306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Half) 48062306a36Sopenharmony_ci adv |= ADVERTISE_10HALF; 48162306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Full) 48262306a36Sopenharmony_ci adv |= ADVERTISE_10FULL; 48362306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Half) 48462306a36Sopenharmony_ci adv |= ADVERTISE_100HALF; 48562306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Full) 48662306a36Sopenharmony_ci adv |= ADVERTISE_100FULL; 48762306a36Sopenharmony_ci if (advertise & ADVERTISED_Pause) 48862306a36Sopenharmony_ci adv |= ADVERTISE_PAUSE_CAP; 48962306a36Sopenharmony_ci if (advertise & ADVERTISED_Asym_Pause) 49062306a36Sopenharmony_ci adv |= ADVERTISE_PAUSE_ASYM; 49162306a36Sopenharmony_ci sungem_phy_write(phy, MII_ADVERTISE, adv); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Setup 1000BT advertise */ 49462306a36Sopenharmony_ci adv = sungem_phy_read(phy, MII_1000BASETCONTROL); 49562306a36Sopenharmony_ci adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP); 49662306a36Sopenharmony_ci if (advertise & SUPPORTED_1000baseT_Half) 49762306a36Sopenharmony_ci adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP; 49862306a36Sopenharmony_ci if (advertise & SUPPORTED_1000baseT_Full) 49962306a36Sopenharmony_ci adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP; 50062306a36Sopenharmony_ci sungem_phy_write(phy, MII_1000BASETCONTROL, adv); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* Start/Restart aneg */ 50362306a36Sopenharmony_ci ctl = sungem_phy_read(phy, MII_BMCR); 50462306a36Sopenharmony_ci ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); 50562306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci u16 ctl; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci phy->autoneg = 0; 51562306a36Sopenharmony_ci phy->speed = speed; 51662306a36Sopenharmony_ci phy->duplex = fd; 51762306a36Sopenharmony_ci phy->pause = 0; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ctl = sungem_phy_read(phy, MII_BMCR); 52062306a36Sopenharmony_ci ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* First reset the PHY */ 52362306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Select speed & duplex */ 52662306a36Sopenharmony_ci switch(speed) { 52762306a36Sopenharmony_ci case SPEED_10: 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci case SPEED_100: 53062306a36Sopenharmony_ci ctl |= BMCR_SPEED100; 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci case SPEED_1000: 53362306a36Sopenharmony_ci ctl |= BMCR_SPD2; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci if (fd == DUPLEX_FULL) 53662306a36Sopenharmony_ci ctl |= BMCR_FULLDPLX; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci // XXX Should we set the sungem to GII now on 1000BT ? 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int bcm54xx_read_link(struct mii_phy *phy) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci int link_mode; 54862306a36Sopenharmony_ci u16 val; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (phy->autoneg) { 55162306a36Sopenharmony_ci val = sungem_phy_read(phy, MII_BCM5400_AUXSTATUS); 55262306a36Sopenharmony_ci link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >> 55362306a36Sopenharmony_ci MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT); 55462306a36Sopenharmony_ci phy->duplex = phy_BCM5400_link_table[link_mode][0] ? 55562306a36Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 55662306a36Sopenharmony_ci phy->speed = phy_BCM5400_link_table[link_mode][2] ? 55762306a36Sopenharmony_ci SPEED_1000 : 55862306a36Sopenharmony_ci (phy_BCM5400_link_table[link_mode][1] ? 55962306a36Sopenharmony_ci SPEED_100 : SPEED_10); 56062306a36Sopenharmony_ci val = sungem_phy_read(phy, MII_LPA); 56162306a36Sopenharmony_ci phy->pause = (phy->duplex == DUPLEX_FULL) && 56262306a36Sopenharmony_ci ((val & LPA_PAUSE) != 0); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci /* On non-aneg, we assume what we put in BMCR is the speed, 56562306a36Sopenharmony_ci * though magic-aneg shouldn't prevent this case from occurring 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int marvell88e1111_init(struct mii_phy* phy) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci u16 rev; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* magic init sequence for rev 0 */ 57662306a36Sopenharmony_ci rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f; 57762306a36Sopenharmony_ci if (rev == 0) { 57862306a36Sopenharmony_ci sungem_phy_write(phy, 0x1d, 0x000a); 57962306a36Sopenharmony_ci sungem_phy_write(phy, 0x1e, 0x0821); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci sungem_phy_write(phy, 0x1d, 0x0006); 58262306a36Sopenharmony_ci sungem_phy_write(phy, 0x1e, 0x8600); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci sungem_phy_write(phy, 0x1d, 0x000b); 58562306a36Sopenharmony_ci sungem_phy_write(phy, 0x1e, 0x0100); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci sungem_phy_write(phy, 0x1d, 0x0004); 58862306a36Sopenharmony_ci sungem_phy_write(phy, 0x1e, 0x4850); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci#define BCM5421_MODE_MASK (1 << 5) 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int bcm5421_poll_link(struct mii_phy* phy) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci u32 phy_reg; 59862306a36Sopenharmony_ci int mode; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* find out in what mode we are */ 60162306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x1000); 60262306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci mode = (phy_reg & BCM5421_MODE_MASK) >> 5; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if ( mode == BCM54XX_COPPER) 60762306a36Sopenharmony_ci return genmii_poll_link(phy); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* try to find out whether we have a link */ 61062306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x2000); 61162306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (phy_reg & 0x0020) 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci else 61662306a36Sopenharmony_ci return 1; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int bcm5421_read_link(struct mii_phy* phy) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci u32 phy_reg; 62262306a36Sopenharmony_ci int mode; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* find out in what mode we are */ 62562306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x1000); 62662306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mode = (phy_reg & BCM5421_MODE_MASK ) >> 5; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if ( mode == BCM54XX_COPPER) 63162306a36Sopenharmony_ci return bcm54xx_read_link(phy); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci phy->speed = SPEED_1000; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* find out whether we are running half- or full duplex */ 63662306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x2000); 63762306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if ( (phy_reg & 0x0080) >> 7) 64062306a36Sopenharmony_ci phy->duplex |= DUPLEX_HALF; 64162306a36Sopenharmony_ci else 64262306a36Sopenharmony_ci phy->duplex |= DUPLEX_FULL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci /* enable fiber mode */ 65062306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x9020); 65162306a36Sopenharmony_ci /* LEDs active in both modes, autosense prio = fiber */ 65262306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x945f); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (!autoneg) { 65562306a36Sopenharmony_ci /* switch off fibre autoneg */ 65662306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0xfc01); 65762306a36Sopenharmony_ci sungem_phy_write(phy, 0x0b, 0x0004); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci phy->autoneg = autoneg; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci#define BCM5461_FIBER_LINK (1 << 2) 66662306a36Sopenharmony_ci#define BCM5461_MODE_MASK (3 << 1) 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int bcm5461_poll_link(struct mii_phy* phy) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci u32 phy_reg; 67162306a36Sopenharmony_ci int mode; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* find out in what mode we are */ 67462306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x7c00); 67562306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci mode = (phy_reg & BCM5461_MODE_MASK ) >> 1; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if ( mode == BCM54XX_COPPER) 68062306a36Sopenharmony_ci return genmii_poll_link(phy); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* find out whether we have a link */ 68362306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x7000); 68462306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (phy_reg & BCM5461_FIBER_LINK) 68762306a36Sopenharmony_ci return 1; 68862306a36Sopenharmony_ci else 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci#define BCM5461_FIBER_DUPLEX (1 << 3) 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int bcm5461_read_link(struct mii_phy* phy) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci u32 phy_reg; 69762306a36Sopenharmony_ci int mode; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* find out in what mode we are */ 70062306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x7c00); 70162306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci mode = (phy_reg & BCM5461_MODE_MASK ) >> 1; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if ( mode == BCM54XX_COPPER) { 70662306a36Sopenharmony_ci return bcm54xx_read_link(phy); 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci phy->speed = SPEED_1000; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* find out whether we are running half- or full duplex */ 71262306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0x7000); 71362306a36Sopenharmony_ci phy_reg = sungem_phy_read(phy, MII_NCONFIG); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (phy_reg & BCM5461_FIBER_DUPLEX) 71662306a36Sopenharmony_ci phy->duplex |= DUPLEX_FULL; 71762306a36Sopenharmony_ci else 71862306a36Sopenharmony_ci phy->duplex |= DUPLEX_HALF; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return 0; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci /* select fiber mode, enable 1000 base-X registers */ 72662306a36Sopenharmony_ci sungem_phy_write(phy, MII_NCONFIG, 0xfc0b); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (autoneg) { 72962306a36Sopenharmony_ci /* enable fiber with no autonegotiation */ 73062306a36Sopenharmony_ci sungem_phy_write(phy, MII_ADVERTISE, 0x01e0); 73162306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, 0x1140); 73262306a36Sopenharmony_ci } else { 73362306a36Sopenharmony_ci /* enable fiber with autonegotiation */ 73462306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, 0x0140); 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci phy->autoneg = autoneg; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic int marvell_setup_aneg(struct mii_phy *phy, u32 advertise) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci u16 ctl, adv; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci phy->autoneg = 1; 74762306a36Sopenharmony_ci phy->speed = SPEED_10; 74862306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 74962306a36Sopenharmony_ci phy->pause = 0; 75062306a36Sopenharmony_ci phy->advertising = advertise; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Setup standard advertise */ 75362306a36Sopenharmony_ci adv = sungem_phy_read(phy, MII_ADVERTISE); 75462306a36Sopenharmony_ci adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); 75562306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Half) 75662306a36Sopenharmony_ci adv |= ADVERTISE_10HALF; 75762306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Full) 75862306a36Sopenharmony_ci adv |= ADVERTISE_10FULL; 75962306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Half) 76062306a36Sopenharmony_ci adv |= ADVERTISE_100HALF; 76162306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Full) 76262306a36Sopenharmony_ci adv |= ADVERTISE_100FULL; 76362306a36Sopenharmony_ci if (advertise & ADVERTISED_Pause) 76462306a36Sopenharmony_ci adv |= ADVERTISE_PAUSE_CAP; 76562306a36Sopenharmony_ci if (advertise & ADVERTISED_Asym_Pause) 76662306a36Sopenharmony_ci adv |= ADVERTISE_PAUSE_ASYM; 76762306a36Sopenharmony_ci sungem_phy_write(phy, MII_ADVERTISE, adv); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Setup 1000BT advertise & enable crossover detect 77062306a36Sopenharmony_ci * XXX How do we advertise 1000BT ? Darwin source is 77162306a36Sopenharmony_ci * confusing here, they read from specific control and 77262306a36Sopenharmony_ci * write to control... Someone has specs for those 77362306a36Sopenharmony_ci * beasts ? 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci adv = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL); 77662306a36Sopenharmony_ci adv |= MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX; 77762306a36Sopenharmony_ci adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP | 77862306a36Sopenharmony_ci MII_1000BASETCONTROL_HALFDUPLEXCAP); 77962306a36Sopenharmony_ci if (advertise & SUPPORTED_1000baseT_Half) 78062306a36Sopenharmony_ci adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP; 78162306a36Sopenharmony_ci if (advertise & SUPPORTED_1000baseT_Full) 78262306a36Sopenharmony_ci adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP; 78362306a36Sopenharmony_ci sungem_phy_write(phy, MII_1000BASETCONTROL, adv); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* Start/Restart aneg */ 78662306a36Sopenharmony_ci ctl = sungem_phy_read(phy, MII_BMCR); 78762306a36Sopenharmony_ci ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); 78862306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic int marvell_setup_forced(struct mii_phy *phy, int speed, int fd) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci u16 ctl, ctl2; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci phy->autoneg = 0; 79862306a36Sopenharmony_ci phy->speed = speed; 79962306a36Sopenharmony_ci phy->duplex = fd; 80062306a36Sopenharmony_ci phy->pause = 0; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ctl = sungem_phy_read(phy, MII_BMCR); 80362306a36Sopenharmony_ci ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE); 80462306a36Sopenharmony_ci ctl |= BMCR_RESET; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Select speed & duplex */ 80762306a36Sopenharmony_ci switch(speed) { 80862306a36Sopenharmony_ci case SPEED_10: 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci case SPEED_100: 81162306a36Sopenharmony_ci ctl |= BMCR_SPEED100; 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci /* I'm not sure about the one below, again, Darwin source is 81462306a36Sopenharmony_ci * quite confusing and I lack chip specs 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_ci case SPEED_1000: 81762306a36Sopenharmony_ci ctl |= BMCR_SPD2; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci if (fd == DUPLEX_FULL) 82062306a36Sopenharmony_ci ctl |= BMCR_FULLDPLX; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* Disable crossover. Again, the way Apple does it is strange, 82362306a36Sopenharmony_ci * though I don't assume they are wrong ;) 82462306a36Sopenharmony_ci */ 82562306a36Sopenharmony_ci ctl2 = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL); 82662306a36Sopenharmony_ci ctl2 &= ~(MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX | 82762306a36Sopenharmony_ci MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX | 82862306a36Sopenharmony_ci MII_1000BASETCONTROL_FULLDUPLEXCAP | 82962306a36Sopenharmony_ci MII_1000BASETCONTROL_HALFDUPLEXCAP); 83062306a36Sopenharmony_ci if (speed == SPEED_1000) 83162306a36Sopenharmony_ci ctl2 |= (fd == DUPLEX_FULL) ? 83262306a36Sopenharmony_ci MII_1000BASETCONTROL_FULLDUPLEXCAP : 83362306a36Sopenharmony_ci MII_1000BASETCONTROL_HALFDUPLEXCAP; 83462306a36Sopenharmony_ci sungem_phy_write(phy, MII_1000BASETCONTROL, ctl2); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci // XXX Should we set the sungem to GII now on 1000BT ? 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci sungem_phy_write(phy, MII_BMCR, ctl); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int marvell_read_link(struct mii_phy *phy) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci u16 status, pmask; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (phy->autoneg) { 84862306a36Sopenharmony_ci status = sungem_phy_read(phy, MII_M1011_PHY_SPEC_STATUS); 84962306a36Sopenharmony_ci if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0) 85062306a36Sopenharmony_ci return -EAGAIN; 85162306a36Sopenharmony_ci if (status & MII_M1011_PHY_SPEC_STATUS_1000) 85262306a36Sopenharmony_ci phy->speed = SPEED_1000; 85362306a36Sopenharmony_ci else if (status & MII_M1011_PHY_SPEC_STATUS_100) 85462306a36Sopenharmony_ci phy->speed = SPEED_100; 85562306a36Sopenharmony_ci else 85662306a36Sopenharmony_ci phy->speed = SPEED_10; 85762306a36Sopenharmony_ci if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX) 85862306a36Sopenharmony_ci phy->duplex = DUPLEX_FULL; 85962306a36Sopenharmony_ci else 86062306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 86162306a36Sopenharmony_ci pmask = MII_M1011_PHY_SPEC_STATUS_TX_PAUSE | 86262306a36Sopenharmony_ci MII_M1011_PHY_SPEC_STATUS_RX_PAUSE; 86362306a36Sopenharmony_ci phy->pause = (status & pmask) == pmask; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci /* On non-aneg, we assume what we put in BMCR is the speed, 86662306a36Sopenharmony_ci * though magic-aneg shouldn't prevent this case from occurring 86762306a36Sopenharmony_ci */ 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci#define MII_BASIC_FEATURES \ 87362306a36Sopenharmony_ci (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ 87462306a36Sopenharmony_ci SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \ 87562306a36Sopenharmony_ci SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | \ 87662306a36Sopenharmony_ci SUPPORTED_Pause) 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/* On gigabit capable PHYs, we advertise Pause support but not asym pause 87962306a36Sopenharmony_ci * support for now as I'm not sure it's supported and Darwin doesn't do 88062306a36Sopenharmony_ci * it neither. --BenH. 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_ci#define MII_GBIT_FEATURES \ 88362306a36Sopenharmony_ci (MII_BASIC_FEATURES | \ 88462306a36Sopenharmony_ci SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full) 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci/* Broadcom BCM 5201 */ 88762306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5201_phy_ops = { 88862306a36Sopenharmony_ci .init = bcm5201_init, 88962306a36Sopenharmony_ci .suspend = bcm5201_suspend, 89062306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 89162306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 89262306a36Sopenharmony_ci .poll_link = genmii_poll_link, 89362306a36Sopenharmony_ci .read_link = genmii_read_link, 89462306a36Sopenharmony_ci}; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic struct mii_phy_def bcm5201_phy_def = { 89762306a36Sopenharmony_ci .phy_id = 0x00406210, 89862306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 89962306a36Sopenharmony_ci .name = "BCM5201", 90062306a36Sopenharmony_ci .features = MII_BASIC_FEATURES, 90162306a36Sopenharmony_ci .magic_aneg = 1, 90262306a36Sopenharmony_ci .ops = &bcm5201_phy_ops 90362306a36Sopenharmony_ci}; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/* Broadcom BCM 5221 */ 90662306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5221_phy_ops = { 90762306a36Sopenharmony_ci .suspend = bcm5221_suspend, 90862306a36Sopenharmony_ci .init = bcm5221_init, 90962306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 91062306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 91162306a36Sopenharmony_ci .poll_link = genmii_poll_link, 91262306a36Sopenharmony_ci .read_link = genmii_read_link, 91362306a36Sopenharmony_ci}; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic struct mii_phy_def bcm5221_phy_def = { 91662306a36Sopenharmony_ci .phy_id = 0x004061e0, 91762306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 91862306a36Sopenharmony_ci .name = "BCM5221", 91962306a36Sopenharmony_ci .features = MII_BASIC_FEATURES, 92062306a36Sopenharmony_ci .magic_aneg = 1, 92162306a36Sopenharmony_ci .ops = &bcm5221_phy_ops 92262306a36Sopenharmony_ci}; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci/* Broadcom BCM 5241 */ 92562306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5241_phy_ops = { 92662306a36Sopenharmony_ci .suspend = bcm5241_suspend, 92762306a36Sopenharmony_ci .init = bcm5241_init, 92862306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 92962306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 93062306a36Sopenharmony_ci .poll_link = genmii_poll_link, 93162306a36Sopenharmony_ci .read_link = genmii_read_link, 93262306a36Sopenharmony_ci}; 93362306a36Sopenharmony_cistatic struct mii_phy_def bcm5241_phy_def = { 93462306a36Sopenharmony_ci .phy_id = 0x0143bc30, 93562306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 93662306a36Sopenharmony_ci .name = "BCM5241", 93762306a36Sopenharmony_ci .features = MII_BASIC_FEATURES, 93862306a36Sopenharmony_ci .magic_aneg = 1, 93962306a36Sopenharmony_ci .ops = &bcm5241_phy_ops 94062306a36Sopenharmony_ci}; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci/* Broadcom BCM 5400 */ 94362306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5400_phy_ops = { 94462306a36Sopenharmony_ci .init = bcm5400_init, 94562306a36Sopenharmony_ci .suspend = bcm5400_suspend, 94662306a36Sopenharmony_ci .setup_aneg = bcm54xx_setup_aneg, 94762306a36Sopenharmony_ci .setup_forced = bcm54xx_setup_forced, 94862306a36Sopenharmony_ci .poll_link = genmii_poll_link, 94962306a36Sopenharmony_ci .read_link = bcm54xx_read_link, 95062306a36Sopenharmony_ci}; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic struct mii_phy_def bcm5400_phy_def = { 95362306a36Sopenharmony_ci .phy_id = 0x00206040, 95462306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 95562306a36Sopenharmony_ci .name = "BCM5400", 95662306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 95762306a36Sopenharmony_ci .magic_aneg = 1, 95862306a36Sopenharmony_ci .ops = &bcm5400_phy_ops 95962306a36Sopenharmony_ci}; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* Broadcom BCM 5401 */ 96262306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5401_phy_ops = { 96362306a36Sopenharmony_ci .init = bcm5401_init, 96462306a36Sopenharmony_ci .suspend = bcm5401_suspend, 96562306a36Sopenharmony_ci .setup_aneg = bcm54xx_setup_aneg, 96662306a36Sopenharmony_ci .setup_forced = bcm54xx_setup_forced, 96762306a36Sopenharmony_ci .poll_link = genmii_poll_link, 96862306a36Sopenharmony_ci .read_link = bcm54xx_read_link, 96962306a36Sopenharmony_ci}; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic struct mii_phy_def bcm5401_phy_def = { 97262306a36Sopenharmony_ci .phy_id = 0x00206050, 97362306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 97462306a36Sopenharmony_ci .name = "BCM5401", 97562306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 97662306a36Sopenharmony_ci .magic_aneg = 1, 97762306a36Sopenharmony_ci .ops = &bcm5401_phy_ops 97862306a36Sopenharmony_ci}; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/* Broadcom BCM 5411 */ 98162306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5411_phy_ops = { 98262306a36Sopenharmony_ci .init = bcm5411_init, 98362306a36Sopenharmony_ci .suspend = generic_suspend, 98462306a36Sopenharmony_ci .setup_aneg = bcm54xx_setup_aneg, 98562306a36Sopenharmony_ci .setup_forced = bcm54xx_setup_forced, 98662306a36Sopenharmony_ci .poll_link = genmii_poll_link, 98762306a36Sopenharmony_ci .read_link = bcm54xx_read_link, 98862306a36Sopenharmony_ci}; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic struct mii_phy_def bcm5411_phy_def = { 99162306a36Sopenharmony_ci .phy_id = 0x00206070, 99262306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 99362306a36Sopenharmony_ci .name = "BCM5411", 99462306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 99562306a36Sopenharmony_ci .magic_aneg = 1, 99662306a36Sopenharmony_ci .ops = &bcm5411_phy_ops 99762306a36Sopenharmony_ci}; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci/* Broadcom BCM 5421 */ 100062306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5421_phy_ops = { 100162306a36Sopenharmony_ci .init = bcm5421_init, 100262306a36Sopenharmony_ci .suspend = generic_suspend, 100362306a36Sopenharmony_ci .setup_aneg = bcm54xx_setup_aneg, 100462306a36Sopenharmony_ci .setup_forced = bcm54xx_setup_forced, 100562306a36Sopenharmony_ci .poll_link = bcm5421_poll_link, 100662306a36Sopenharmony_ci .read_link = bcm5421_read_link, 100762306a36Sopenharmony_ci .enable_fiber = bcm5421_enable_fiber, 100862306a36Sopenharmony_ci}; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic struct mii_phy_def bcm5421_phy_def = { 101162306a36Sopenharmony_ci .phy_id = 0x002060e0, 101262306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 101362306a36Sopenharmony_ci .name = "BCM5421", 101462306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 101562306a36Sopenharmony_ci .magic_aneg = 1, 101662306a36Sopenharmony_ci .ops = &bcm5421_phy_ops 101762306a36Sopenharmony_ci}; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/* Broadcom BCM 5421 built-in K2 */ 102062306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5421k2_phy_ops = { 102162306a36Sopenharmony_ci .init = bcm5421_init, 102262306a36Sopenharmony_ci .suspend = generic_suspend, 102362306a36Sopenharmony_ci .setup_aneg = bcm54xx_setup_aneg, 102462306a36Sopenharmony_ci .setup_forced = bcm54xx_setup_forced, 102562306a36Sopenharmony_ci .poll_link = genmii_poll_link, 102662306a36Sopenharmony_ci .read_link = bcm54xx_read_link, 102762306a36Sopenharmony_ci}; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic struct mii_phy_def bcm5421k2_phy_def = { 103062306a36Sopenharmony_ci .phy_id = 0x002062e0, 103162306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 103262306a36Sopenharmony_ci .name = "BCM5421-K2", 103362306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 103462306a36Sopenharmony_ci .magic_aneg = 1, 103562306a36Sopenharmony_ci .ops = &bcm5421k2_phy_ops 103662306a36Sopenharmony_ci}; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5461_phy_ops = { 103962306a36Sopenharmony_ci .init = bcm5421_init, 104062306a36Sopenharmony_ci .suspend = generic_suspend, 104162306a36Sopenharmony_ci .setup_aneg = bcm54xx_setup_aneg, 104262306a36Sopenharmony_ci .setup_forced = bcm54xx_setup_forced, 104362306a36Sopenharmony_ci .poll_link = bcm5461_poll_link, 104462306a36Sopenharmony_ci .read_link = bcm5461_read_link, 104562306a36Sopenharmony_ci .enable_fiber = bcm5461_enable_fiber, 104662306a36Sopenharmony_ci}; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic struct mii_phy_def bcm5461_phy_def = { 104962306a36Sopenharmony_ci .phy_id = 0x002060c0, 105062306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 105162306a36Sopenharmony_ci .name = "BCM5461", 105262306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 105362306a36Sopenharmony_ci .magic_aneg = 1, 105462306a36Sopenharmony_ci .ops = &bcm5461_phy_ops 105562306a36Sopenharmony_ci}; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci/* Broadcom BCM 5462 built-in Vesta */ 105862306a36Sopenharmony_cistatic const struct mii_phy_ops bcm5462V_phy_ops = { 105962306a36Sopenharmony_ci .init = bcm5421_init, 106062306a36Sopenharmony_ci .suspend = generic_suspend, 106162306a36Sopenharmony_ci .setup_aneg = bcm54xx_setup_aneg, 106262306a36Sopenharmony_ci .setup_forced = bcm54xx_setup_forced, 106362306a36Sopenharmony_ci .poll_link = genmii_poll_link, 106462306a36Sopenharmony_ci .read_link = bcm54xx_read_link, 106562306a36Sopenharmony_ci}; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic struct mii_phy_def bcm5462V_phy_def = { 106862306a36Sopenharmony_ci .phy_id = 0x002060d0, 106962306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 107062306a36Sopenharmony_ci .name = "BCM5462-Vesta", 107162306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 107262306a36Sopenharmony_ci .magic_aneg = 1, 107362306a36Sopenharmony_ci .ops = &bcm5462V_phy_ops 107462306a36Sopenharmony_ci}; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci/* Marvell 88E1101 amd 88E1111 */ 107762306a36Sopenharmony_cistatic const struct mii_phy_ops marvell88e1101_phy_ops = { 107862306a36Sopenharmony_ci .suspend = generic_suspend, 107962306a36Sopenharmony_ci .setup_aneg = marvell_setup_aneg, 108062306a36Sopenharmony_ci .setup_forced = marvell_setup_forced, 108162306a36Sopenharmony_ci .poll_link = genmii_poll_link, 108262306a36Sopenharmony_ci .read_link = marvell_read_link 108362306a36Sopenharmony_ci}; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic const struct mii_phy_ops marvell88e1111_phy_ops = { 108662306a36Sopenharmony_ci .init = marvell88e1111_init, 108762306a36Sopenharmony_ci .suspend = generic_suspend, 108862306a36Sopenharmony_ci .setup_aneg = marvell_setup_aneg, 108962306a36Sopenharmony_ci .setup_forced = marvell_setup_forced, 109062306a36Sopenharmony_ci .poll_link = genmii_poll_link, 109162306a36Sopenharmony_ci .read_link = marvell_read_link 109262306a36Sopenharmony_ci}; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci/* two revs in darwin for the 88e1101 ... I could use a datasheet 109562306a36Sopenharmony_ci * to get the proper names... 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_cistatic struct mii_phy_def marvell88e1101v1_phy_def = { 109862306a36Sopenharmony_ci .phy_id = 0x01410c20, 109962306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 110062306a36Sopenharmony_ci .name = "Marvell 88E1101v1", 110162306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 110262306a36Sopenharmony_ci .magic_aneg = 1, 110362306a36Sopenharmony_ci .ops = &marvell88e1101_phy_ops 110462306a36Sopenharmony_ci}; 110562306a36Sopenharmony_cistatic struct mii_phy_def marvell88e1101v2_phy_def = { 110662306a36Sopenharmony_ci .phy_id = 0x01410c60, 110762306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 110862306a36Sopenharmony_ci .name = "Marvell 88E1101v2", 110962306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 111062306a36Sopenharmony_ci .magic_aneg = 1, 111162306a36Sopenharmony_ci .ops = &marvell88e1101_phy_ops 111262306a36Sopenharmony_ci}; 111362306a36Sopenharmony_cistatic struct mii_phy_def marvell88e1111_phy_def = { 111462306a36Sopenharmony_ci .phy_id = 0x01410cc0, 111562306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 111662306a36Sopenharmony_ci .name = "Marvell 88E1111", 111762306a36Sopenharmony_ci .features = MII_GBIT_FEATURES, 111862306a36Sopenharmony_ci .magic_aneg = 1, 111962306a36Sopenharmony_ci .ops = &marvell88e1111_phy_ops 112062306a36Sopenharmony_ci}; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci/* Generic implementation for most 10/100 PHYs */ 112362306a36Sopenharmony_cistatic const struct mii_phy_ops generic_phy_ops = { 112462306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 112562306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 112662306a36Sopenharmony_ci .poll_link = genmii_poll_link, 112762306a36Sopenharmony_ci .read_link = genmii_read_link 112862306a36Sopenharmony_ci}; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic struct mii_phy_def genmii_phy_def = { 113162306a36Sopenharmony_ci .phy_id = 0x00000000, 113262306a36Sopenharmony_ci .phy_id_mask = 0x00000000, 113362306a36Sopenharmony_ci .name = "Generic MII", 113462306a36Sopenharmony_ci .features = MII_BASIC_FEATURES, 113562306a36Sopenharmony_ci .magic_aneg = 0, 113662306a36Sopenharmony_ci .ops = &generic_phy_ops 113762306a36Sopenharmony_ci}; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic struct mii_phy_def* mii_phy_table[] = { 114062306a36Sopenharmony_ci &bcm5201_phy_def, 114162306a36Sopenharmony_ci &bcm5221_phy_def, 114262306a36Sopenharmony_ci &bcm5241_phy_def, 114362306a36Sopenharmony_ci &bcm5400_phy_def, 114462306a36Sopenharmony_ci &bcm5401_phy_def, 114562306a36Sopenharmony_ci &bcm5411_phy_def, 114662306a36Sopenharmony_ci &bcm5421_phy_def, 114762306a36Sopenharmony_ci &bcm5421k2_phy_def, 114862306a36Sopenharmony_ci &bcm5461_phy_def, 114962306a36Sopenharmony_ci &bcm5462V_phy_def, 115062306a36Sopenharmony_ci &marvell88e1101v1_phy_def, 115162306a36Sopenharmony_ci &marvell88e1101v2_phy_def, 115262306a36Sopenharmony_ci &marvell88e1111_phy_def, 115362306a36Sopenharmony_ci &genmii_phy_def, 115462306a36Sopenharmony_ci NULL 115562306a36Sopenharmony_ci}; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ciint sungem_phy_probe(struct mii_phy *phy, int mii_id) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci int rc; 116062306a36Sopenharmony_ci u32 id; 116162306a36Sopenharmony_ci struct mii_phy_def* def; 116262306a36Sopenharmony_ci int i; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci /* We do not reset the mii_phy structure as the driver 116562306a36Sopenharmony_ci * may re-probe the PHY regulary 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ci phy->mii_id = mii_id; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Take PHY out of isloate mode and reset it. */ 117062306a36Sopenharmony_ci rc = reset_one_mii_phy(phy, mii_id); 117162306a36Sopenharmony_ci if (rc) 117262306a36Sopenharmony_ci goto fail; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci /* Read ID and find matching entry */ 117562306a36Sopenharmony_ci id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2)); 117662306a36Sopenharmony_ci printk(KERN_DEBUG KBUILD_MODNAME ": " "PHY ID: %x, addr: %x\n", 117762306a36Sopenharmony_ci id, mii_id); 117862306a36Sopenharmony_ci for (i=0; (def = mii_phy_table[i]) != NULL; i++) 117962306a36Sopenharmony_ci if ((id & def->phy_id_mask) == def->phy_id) 118062306a36Sopenharmony_ci break; 118162306a36Sopenharmony_ci /* Should never be NULL (we have a generic entry), but... */ 118262306a36Sopenharmony_ci if (def == NULL) 118362306a36Sopenharmony_ci goto fail; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci phy->def = def; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci return 0; 118862306a36Sopenharmony_cifail: 118962306a36Sopenharmony_ci phy->speed = 0; 119062306a36Sopenharmony_ci phy->duplex = 0; 119162306a36Sopenharmony_ci phy->pause = 0; 119262306a36Sopenharmony_ci phy->advertising = 0; 119362306a36Sopenharmony_ci return -ENODEV; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ciEXPORT_SYMBOL(sungem_phy_probe); 119762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1198