162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/net/ethernet/ibm/emac/phy.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for PowerPC 4xx on-chip ethernet controller, PHY support. 662306a36Sopenharmony_ci * Borrowed from sungem_phy.c, though I only kept the generic MII 762306a36Sopenharmony_ci * driver for now. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file should be shared with other drivers or eventually 1062306a36Sopenharmony_ci * merged as the "low level" part of miilib 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. 1362306a36Sopenharmony_ci * <benh@kernel.crashing.org> 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Based on the arch/ppc version of the driver: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org) 1862306a36Sopenharmony_ci * (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net> 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <linux/netdevice.h> 2562306a36Sopenharmony_ci#include <linux/mii.h> 2662306a36Sopenharmony_ci#include <linux/ethtool.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "emac.h" 3062306a36Sopenharmony_ci#include "phy.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define phy_read _phy_read 3362306a36Sopenharmony_ci#define phy_write _phy_write 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic inline int _phy_read(struct mii_phy *phy, int reg) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return phy->mdio_read(phy->dev, phy->address, reg); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic inline void _phy_write(struct mii_phy *phy, int reg, int val) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci phy->mdio_write(phy->dev, phy->address, reg, val); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic inline int gpcs_phy_read(struct mii_phy *phy, int reg) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci return phy->mdio_read(phy->dev, phy->gpcs_address, reg); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic inline void gpcs_phy_write(struct mii_phy *phy, int reg, int val) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci phy->mdio_write(phy->dev, phy->gpcs_address, reg, val); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciint emac_mii_reset_phy(struct mii_phy *phy) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int val; 5862306a36Sopenharmony_ci int limit = 10000; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci val = phy_read(phy, MII_BMCR); 6162306a36Sopenharmony_ci val &= ~(BMCR_ISOLATE | BMCR_ANENABLE); 6262306a36Sopenharmony_ci val |= BMCR_RESET; 6362306a36Sopenharmony_ci phy_write(phy, MII_BMCR, val); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci udelay(300); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci while (--limit) { 6862306a36Sopenharmony_ci val = phy_read(phy, MII_BMCR); 6962306a36Sopenharmony_ci if (val >= 0 && (val & BMCR_RESET) == 0) 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci udelay(10); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci if ((val & BMCR_ISOLATE) && limit > 0) 7462306a36Sopenharmony_ci phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return limit <= 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciint emac_mii_reset_gpcs(struct mii_phy *phy) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int val; 8262306a36Sopenharmony_ci int limit = 10000; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci val = gpcs_phy_read(phy, MII_BMCR); 8562306a36Sopenharmony_ci val &= ~(BMCR_ISOLATE | BMCR_ANENABLE); 8662306a36Sopenharmony_ci val |= BMCR_RESET; 8762306a36Sopenharmony_ci gpcs_phy_write(phy, MII_BMCR, val); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci udelay(300); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci while (--limit) { 9262306a36Sopenharmony_ci val = gpcs_phy_read(phy, MII_BMCR); 9362306a36Sopenharmony_ci if (val >= 0 && (val & BMCR_RESET) == 0) 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci udelay(10); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci if ((val & BMCR_ISOLATE) && limit > 0) 9862306a36Sopenharmony_ci gpcs_phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (limit > 0 && phy->mode == PHY_INTERFACE_MODE_SGMII) { 10162306a36Sopenharmony_ci /* Configure GPCS interface to recommended setting for SGMII */ 10262306a36Sopenharmony_ci gpcs_phy_write(phy, 0x04, 0x8120); /* AsymPause, FDX */ 10362306a36Sopenharmony_ci gpcs_phy_write(phy, 0x07, 0x2801); /* msg_pg, toggle */ 10462306a36Sopenharmony_ci gpcs_phy_write(phy, 0x00, 0x0140); /* 1Gbps, FDX */ 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return limit <= 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci int ctl, adv; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci phy->autoneg = AUTONEG_ENABLE; 11562306a36Sopenharmony_ci phy->speed = SPEED_10; 11662306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 11762306a36Sopenharmony_ci phy->pause = phy->asym_pause = 0; 11862306a36Sopenharmony_ci phy->advertising = advertise; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ctl = phy_read(phy, MII_BMCR); 12162306a36Sopenharmony_ci if (ctl < 0) 12262306a36Sopenharmony_ci return ctl; 12362306a36Sopenharmony_ci ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* First clear the PHY */ 12662306a36Sopenharmony_ci phy_write(phy, MII_BMCR, ctl); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Setup standard advertise */ 12962306a36Sopenharmony_ci adv = phy_read(phy, MII_ADVERTISE); 13062306a36Sopenharmony_ci if (adv < 0) 13162306a36Sopenharmony_ci return adv; 13262306a36Sopenharmony_ci adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | 13362306a36Sopenharmony_ci ADVERTISE_PAUSE_ASYM); 13462306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Half) 13562306a36Sopenharmony_ci adv |= ADVERTISE_10HALF; 13662306a36Sopenharmony_ci if (advertise & ADVERTISED_10baseT_Full) 13762306a36Sopenharmony_ci adv |= ADVERTISE_10FULL; 13862306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Half) 13962306a36Sopenharmony_ci adv |= ADVERTISE_100HALF; 14062306a36Sopenharmony_ci if (advertise & ADVERTISED_100baseT_Full) 14162306a36Sopenharmony_ci adv |= ADVERTISE_100FULL; 14262306a36Sopenharmony_ci if (advertise & ADVERTISED_Pause) 14362306a36Sopenharmony_ci adv |= ADVERTISE_PAUSE_CAP; 14462306a36Sopenharmony_ci if (advertise & ADVERTISED_Asym_Pause) 14562306a36Sopenharmony_ci adv |= ADVERTISE_PAUSE_ASYM; 14662306a36Sopenharmony_ci phy_write(phy, MII_ADVERTISE, adv); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (phy->features & 14962306a36Sopenharmony_ci (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) { 15062306a36Sopenharmony_ci adv = phy_read(phy, MII_CTRL1000); 15162306a36Sopenharmony_ci if (adv < 0) 15262306a36Sopenharmony_ci return adv; 15362306a36Sopenharmony_ci adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); 15462306a36Sopenharmony_ci if (advertise & ADVERTISED_1000baseT_Full) 15562306a36Sopenharmony_ci adv |= ADVERTISE_1000FULL; 15662306a36Sopenharmony_ci if (advertise & ADVERTISED_1000baseT_Half) 15762306a36Sopenharmony_ci adv |= ADVERTISE_1000HALF; 15862306a36Sopenharmony_ci phy_write(phy, MII_CTRL1000, adv); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Start/Restart aneg */ 16262306a36Sopenharmony_ci ctl = phy_read(phy, MII_BMCR); 16362306a36Sopenharmony_ci ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); 16462306a36Sopenharmony_ci phy_write(phy, MII_BMCR, ctl); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int ctl; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci phy->autoneg = AUTONEG_DISABLE; 17462306a36Sopenharmony_ci phy->speed = speed; 17562306a36Sopenharmony_ci phy->duplex = fd; 17662306a36Sopenharmony_ci phy->pause = phy->asym_pause = 0; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ctl = phy_read(phy, MII_BMCR); 17962306a36Sopenharmony_ci if (ctl < 0) 18062306a36Sopenharmony_ci return ctl; 18162306a36Sopenharmony_ci ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* First clear the PHY */ 18462306a36Sopenharmony_ci phy_write(phy, MII_BMCR, ctl | BMCR_RESET); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* Select speed & duplex */ 18762306a36Sopenharmony_ci switch (speed) { 18862306a36Sopenharmony_ci case SPEED_10: 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci case SPEED_100: 19162306a36Sopenharmony_ci ctl |= BMCR_SPEED100; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case SPEED_1000: 19462306a36Sopenharmony_ci ctl |= BMCR_SPEED1000; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci default: 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci if (fd == DUPLEX_FULL) 20062306a36Sopenharmony_ci ctl |= BMCR_FULLDPLX; 20162306a36Sopenharmony_ci phy_write(phy, MII_BMCR, ctl); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int genmii_poll_link(struct mii_phy *phy) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci int status; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Clear latched value with dummy read */ 21162306a36Sopenharmony_ci phy_read(phy, MII_BMSR); 21262306a36Sopenharmony_ci status = phy_read(phy, MII_BMSR); 21362306a36Sopenharmony_ci if (status < 0 || (status & BMSR_LSTATUS) == 0) 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE)) 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci return 1; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int genmii_read_link(struct mii_phy *phy) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci if (phy->autoneg == AUTONEG_ENABLE) { 22362306a36Sopenharmony_ci int glpa = 0; 22462306a36Sopenharmony_ci int lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE); 22562306a36Sopenharmony_ci if (lpa < 0) 22662306a36Sopenharmony_ci return lpa; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (phy->features & 22962306a36Sopenharmony_ci (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) { 23062306a36Sopenharmony_ci int adv = phy_read(phy, MII_CTRL1000); 23162306a36Sopenharmony_ci glpa = phy_read(phy, MII_STAT1000); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (glpa < 0 || adv < 0) 23462306a36Sopenharmony_ci return adv; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci glpa &= adv << 2; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci phy->speed = SPEED_10; 24062306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 24162306a36Sopenharmony_ci phy->pause = phy->asym_pause = 0; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (glpa & (LPA_1000FULL | LPA_1000HALF)) { 24462306a36Sopenharmony_ci phy->speed = SPEED_1000; 24562306a36Sopenharmony_ci if (glpa & LPA_1000FULL) 24662306a36Sopenharmony_ci phy->duplex = DUPLEX_FULL; 24762306a36Sopenharmony_ci } else if (lpa & (LPA_100FULL | LPA_100HALF)) { 24862306a36Sopenharmony_ci phy->speed = SPEED_100; 24962306a36Sopenharmony_ci if (lpa & LPA_100FULL) 25062306a36Sopenharmony_ci phy->duplex = DUPLEX_FULL; 25162306a36Sopenharmony_ci } else if (lpa & LPA_10FULL) 25262306a36Sopenharmony_ci phy->duplex = DUPLEX_FULL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (phy->duplex == DUPLEX_FULL) { 25562306a36Sopenharmony_ci phy->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; 25662306a36Sopenharmony_ci phy->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci int bmcr = phy_read(phy, MII_BMCR); 26062306a36Sopenharmony_ci if (bmcr < 0) 26162306a36Sopenharmony_ci return bmcr; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (bmcr & BMCR_FULLDPLX) 26462306a36Sopenharmony_ci phy->duplex = DUPLEX_FULL; 26562306a36Sopenharmony_ci else 26662306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 26762306a36Sopenharmony_ci if (bmcr & BMCR_SPEED1000) 26862306a36Sopenharmony_ci phy->speed = SPEED_1000; 26962306a36Sopenharmony_ci else if (bmcr & BMCR_SPEED100) 27062306a36Sopenharmony_ci phy->speed = SPEED_100; 27162306a36Sopenharmony_ci else 27262306a36Sopenharmony_ci phy->speed = SPEED_10; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci phy->pause = phy->asym_pause = 0; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* Generic implementation for most 10/100/1000 PHYs */ 28062306a36Sopenharmony_cistatic const struct mii_phy_ops generic_phy_ops = { 28162306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 28262306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 28362306a36Sopenharmony_ci .poll_link = genmii_poll_link, 28462306a36Sopenharmony_ci .read_link = genmii_read_link 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic struct mii_phy_def genmii_phy_def = { 28862306a36Sopenharmony_ci .phy_id = 0x00000000, 28962306a36Sopenharmony_ci .phy_id_mask = 0x00000000, 29062306a36Sopenharmony_ci .name = "Generic MII", 29162306a36Sopenharmony_ci .ops = &generic_phy_ops 29262306a36Sopenharmony_ci}; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* CIS8201 */ 29562306a36Sopenharmony_ci#define MII_CIS8201_10BTCSR 0x16 29662306a36Sopenharmony_ci#define TENBTCSR_ECHO_DISABLE 0x2000 29762306a36Sopenharmony_ci#define MII_CIS8201_EPCR 0x17 29862306a36Sopenharmony_ci#define EPCR_MODE_MASK 0x3000 29962306a36Sopenharmony_ci#define EPCR_GMII_MODE 0x0000 30062306a36Sopenharmony_ci#define EPCR_RGMII_MODE 0x1000 30162306a36Sopenharmony_ci#define EPCR_TBI_MODE 0x2000 30262306a36Sopenharmony_ci#define EPCR_RTBI_MODE 0x3000 30362306a36Sopenharmony_ci#define MII_CIS8201_ACSR 0x1c 30462306a36Sopenharmony_ci#define ACSR_PIN_PRIO_SELECT 0x0004 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int cis8201_init(struct mii_phy *phy) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci int epcr; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci epcr = phy_read(phy, MII_CIS8201_EPCR); 31162306a36Sopenharmony_ci if (epcr < 0) 31262306a36Sopenharmony_ci return epcr; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci epcr &= ~EPCR_MODE_MASK; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci switch (phy->mode) { 31762306a36Sopenharmony_ci case PHY_INTERFACE_MODE_TBI: 31862306a36Sopenharmony_ci epcr |= EPCR_TBI_MODE; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RTBI: 32162306a36Sopenharmony_ci epcr |= EPCR_RTBI_MODE; 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case PHY_INTERFACE_MODE_GMII: 32462306a36Sopenharmony_ci epcr |= EPCR_GMII_MODE; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 32762306a36Sopenharmony_ci default: 32862306a36Sopenharmony_ci epcr |= EPCR_RGMII_MODE; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci phy_write(phy, MII_CIS8201_EPCR, epcr); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* MII regs override strap pins */ 33462306a36Sopenharmony_ci phy_write(phy, MII_CIS8201_ACSR, 33562306a36Sopenharmony_ci phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */ 33862306a36Sopenharmony_ci phy_write(phy, MII_CIS8201_10BTCSR, 33962306a36Sopenharmony_ci phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic const struct mii_phy_ops cis8201_phy_ops = { 34562306a36Sopenharmony_ci .init = cis8201_init, 34662306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 34762306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 34862306a36Sopenharmony_ci .poll_link = genmii_poll_link, 34962306a36Sopenharmony_ci .read_link = genmii_read_link 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic struct mii_phy_def cis8201_phy_def = { 35362306a36Sopenharmony_ci .phy_id = 0x000fc410, 35462306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 35562306a36Sopenharmony_ci .name = "CIS8201 Gigabit Ethernet", 35662306a36Sopenharmony_ci .ops = &cis8201_phy_ops 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic struct mii_phy_def bcm5248_phy_def = { 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci .phy_id = 0x0143bc00, 36262306a36Sopenharmony_ci .phy_id_mask = 0x0ffffff0, 36362306a36Sopenharmony_ci .name = "BCM5248 10/100 SMII Ethernet", 36462306a36Sopenharmony_ci .ops = &generic_phy_ops 36562306a36Sopenharmony_ci}; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int m88e1111_init(struct mii_phy *phy) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci pr_debug("%s: Marvell 88E1111 Ethernet\n", __func__); 37062306a36Sopenharmony_ci phy_write(phy, 0x14, 0x0ce3); 37162306a36Sopenharmony_ci phy_write(phy, 0x18, 0x4101); 37262306a36Sopenharmony_ci phy_write(phy, 0x09, 0x0e00); 37362306a36Sopenharmony_ci phy_write(phy, 0x04, 0x01e1); 37462306a36Sopenharmony_ci phy_write(phy, 0x00, 0x9140); 37562306a36Sopenharmony_ci phy_write(phy, 0x00, 0x1140); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int m88e1112_init(struct mii_phy *phy) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci /* 38362306a36Sopenharmony_ci * Marvell 88E1112 PHY needs to have the SGMII MAC 38462306a36Sopenharmony_ci * interace (page 2) properly configured to 38562306a36Sopenharmony_ci * communicate with the 460EX/GT GPCS interface. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci u16 reg_short; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci pr_debug("%s: Marvell 88E1112 Ethernet\n", __func__); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Set access to Page 2 */ 39362306a36Sopenharmony_ci phy_write(phy, 0x16, 0x0002); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci phy_write(phy, 0x00, 0x0040); /* 1Gbps */ 39662306a36Sopenharmony_ci reg_short = (u16)(phy_read(phy, 0x1a)); 39762306a36Sopenharmony_ci reg_short |= 0x8000; /* bypass Auto-Negotiation */ 39862306a36Sopenharmony_ci phy_write(phy, 0x1a, reg_short); 39962306a36Sopenharmony_ci emac_mii_reset_phy(phy); /* reset MAC interface */ 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Reset access to Page 0 */ 40262306a36Sopenharmony_ci phy_write(phy, 0x16, 0x0000); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int et1011c_init(struct mii_phy *phy) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci u16 reg_short; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci reg_short = (u16)(phy_read(phy, 0x16)); 41262306a36Sopenharmony_ci reg_short &= ~(0x7); 41362306a36Sopenharmony_ci reg_short |= 0x6; /* RGMII Trace Delay*/ 41462306a36Sopenharmony_ci phy_write(phy, 0x16, reg_short); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci reg_short = (u16)(phy_read(phy, 0x17)); 41762306a36Sopenharmony_ci reg_short &= ~(0x40); 41862306a36Sopenharmony_ci phy_write(phy, 0x17, reg_short); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci phy_write(phy, 0x1c, 0x74f0); 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic const struct mii_phy_ops et1011c_phy_ops = { 42562306a36Sopenharmony_ci .init = et1011c_init, 42662306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 42762306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 42862306a36Sopenharmony_ci .poll_link = genmii_poll_link, 42962306a36Sopenharmony_ci .read_link = genmii_read_link 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic struct mii_phy_def et1011c_phy_def = { 43362306a36Sopenharmony_ci .phy_id = 0x0282f000, 43462306a36Sopenharmony_ci .phy_id_mask = 0x0fffff00, 43562306a36Sopenharmony_ci .name = "ET1011C Gigabit Ethernet", 43662306a36Sopenharmony_ci .ops = &et1011c_phy_ops 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic const struct mii_phy_ops m88e1111_phy_ops = { 44462306a36Sopenharmony_ci .init = m88e1111_init, 44562306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 44662306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 44762306a36Sopenharmony_ci .poll_link = genmii_poll_link, 44862306a36Sopenharmony_ci .read_link = genmii_read_link 44962306a36Sopenharmony_ci}; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic struct mii_phy_def m88e1111_phy_def = { 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci .phy_id = 0x01410CC0, 45462306a36Sopenharmony_ci .phy_id_mask = 0x0ffffff0, 45562306a36Sopenharmony_ci .name = "Marvell 88E1111 Ethernet", 45662306a36Sopenharmony_ci .ops = &m88e1111_phy_ops, 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic const struct mii_phy_ops m88e1112_phy_ops = { 46062306a36Sopenharmony_ci .init = m88e1112_init, 46162306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 46262306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 46362306a36Sopenharmony_ci .poll_link = genmii_poll_link, 46462306a36Sopenharmony_ci .read_link = genmii_read_link 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic struct mii_phy_def m88e1112_phy_def = { 46862306a36Sopenharmony_ci .phy_id = 0x01410C90, 46962306a36Sopenharmony_ci .phy_id_mask = 0x0ffffff0, 47062306a36Sopenharmony_ci .name = "Marvell 88E1112 Ethernet", 47162306a36Sopenharmony_ci .ops = &m88e1112_phy_ops, 47262306a36Sopenharmony_ci}; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int ar8035_init(struct mii_phy *phy) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci phy_write(phy, 0x1d, 0x5); /* Address debug register 5 */ 47762306a36Sopenharmony_ci phy_write(phy, 0x1e, 0x2d47); /* Value copied from u-boot */ 47862306a36Sopenharmony_ci phy_write(phy, 0x1d, 0xb); /* Address hib ctrl */ 47962306a36Sopenharmony_ci phy_write(phy, 0x1e, 0xbc20); /* Value copied from u-boot */ 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic const struct mii_phy_ops ar8035_phy_ops = { 48562306a36Sopenharmony_ci .init = ar8035_init, 48662306a36Sopenharmony_ci .setup_aneg = genmii_setup_aneg, 48762306a36Sopenharmony_ci .setup_forced = genmii_setup_forced, 48862306a36Sopenharmony_ci .poll_link = genmii_poll_link, 48962306a36Sopenharmony_ci .read_link = genmii_read_link, 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic struct mii_phy_def ar8035_phy_def = { 49362306a36Sopenharmony_ci .phy_id = 0x004dd070, 49462306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 49562306a36Sopenharmony_ci .name = "Atheros 8035 Gigabit Ethernet", 49662306a36Sopenharmony_ci .ops = &ar8035_phy_ops, 49762306a36Sopenharmony_ci}; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic struct mii_phy_def *mii_phy_table[] = { 50062306a36Sopenharmony_ci &et1011c_phy_def, 50162306a36Sopenharmony_ci &cis8201_phy_def, 50262306a36Sopenharmony_ci &bcm5248_phy_def, 50362306a36Sopenharmony_ci &m88e1111_phy_def, 50462306a36Sopenharmony_ci &m88e1112_phy_def, 50562306a36Sopenharmony_ci &ar8035_phy_def, 50662306a36Sopenharmony_ci &genmii_phy_def, 50762306a36Sopenharmony_ci NULL 50862306a36Sopenharmony_ci}; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ciint emac_mii_phy_probe(struct mii_phy *phy, int address) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct mii_phy_def *def; 51362306a36Sopenharmony_ci int i; 51462306a36Sopenharmony_ci u32 id; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci phy->autoneg = AUTONEG_DISABLE; 51762306a36Sopenharmony_ci phy->advertising = 0; 51862306a36Sopenharmony_ci phy->address = address; 51962306a36Sopenharmony_ci phy->speed = SPEED_10; 52062306a36Sopenharmony_ci phy->duplex = DUPLEX_HALF; 52162306a36Sopenharmony_ci phy->pause = phy->asym_pause = 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Take PHY out of isolate mode and reset it. */ 52462306a36Sopenharmony_ci if (emac_mii_reset_phy(phy)) 52562306a36Sopenharmony_ci return -ENODEV; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Read ID and find matching entry */ 52862306a36Sopenharmony_ci id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2); 52962306a36Sopenharmony_ci for (i = 0; (def = mii_phy_table[i]) != NULL; i++) 53062306a36Sopenharmony_ci if ((id & def->phy_id_mask) == def->phy_id) 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci /* Should never be NULL (we have a generic entry), but... */ 53362306a36Sopenharmony_ci if (!def) 53462306a36Sopenharmony_ci return -ENODEV; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci phy->def = def; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* Determine PHY features if needed */ 53962306a36Sopenharmony_ci phy->features = def->features; 54062306a36Sopenharmony_ci if (!phy->features) { 54162306a36Sopenharmony_ci u16 bmsr = phy_read(phy, MII_BMSR); 54262306a36Sopenharmony_ci if (bmsr & BMSR_ANEGCAPABLE) 54362306a36Sopenharmony_ci phy->features |= SUPPORTED_Autoneg; 54462306a36Sopenharmony_ci if (bmsr & BMSR_10HALF) 54562306a36Sopenharmony_ci phy->features |= SUPPORTED_10baseT_Half; 54662306a36Sopenharmony_ci if (bmsr & BMSR_10FULL) 54762306a36Sopenharmony_ci phy->features |= SUPPORTED_10baseT_Full; 54862306a36Sopenharmony_ci if (bmsr & BMSR_100HALF) 54962306a36Sopenharmony_ci phy->features |= SUPPORTED_100baseT_Half; 55062306a36Sopenharmony_ci if (bmsr & BMSR_100FULL) 55162306a36Sopenharmony_ci phy->features |= SUPPORTED_100baseT_Full; 55262306a36Sopenharmony_ci if (bmsr & BMSR_ESTATEN) { 55362306a36Sopenharmony_ci u16 esr = phy_read(phy, MII_ESTATUS); 55462306a36Sopenharmony_ci if (esr & ESTATUS_1000_TFULL) 55562306a36Sopenharmony_ci phy->features |= SUPPORTED_1000baseT_Full; 55662306a36Sopenharmony_ci if (esr & ESTATUS_1000_THALF) 55762306a36Sopenharmony_ci phy->features |= SUPPORTED_1000baseT_Half; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci phy->features |= SUPPORTED_MII; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Setup default advertising */ 56362306a36Sopenharmony_ci phy->advertising = phy->features; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 569