OSDN Git Service

amd-xgbe: Add support for MDIO attached PHYs
authorLendacky, Thomas <Thomas.Lendacky@amd.com>
Thu, 10 Nov 2016 23:11:14 +0000 (17:11 -0600)
committerDavid S. Miller <davem@davemloft.net>
Sun, 13 Nov 2016 05:56:26 +0000 (00:56 -0500)
Use the phylib support in the kernel to communicate with and control an
MDIO attached PHY. Use the hardware's MDIO communication mechanism to
communicate with the PHY.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/amd/xgbe/xgbe-common.h
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
drivers/net/ethernet/amd/xgbe/xgbe.h

index b7140f9..ecd4f4d 100644 (file)
 #define MAC_HWF0R                      0x011c
 #define MAC_HWF1R                      0x0120
 #define MAC_HWF2R                      0x0124
+#define MAC_MDIOSCAR                   0x0200
+#define MAC_MDIOSCCDR                  0x0204
+#define MAC_MDIOISR                    0x0214
+#define MAC_MDIOIER                    0x0218
+#define MAC_MDIOCL22R                  0x0220
 #define MAC_GPIOCR                     0x0278
 #define MAC_GPIOSR                     0x027c
 #define MAC_MACA0HR                    0x0300
 #define MAC_ISR_MMCTXIS_WIDTH          1
 #define MAC_ISR_PMTIS_INDEX            4
 #define MAC_ISR_PMTIS_WIDTH            1
+#define MAC_ISR_SMI_INDEX              1
+#define MAC_ISR_SMI_WIDTH              1
 #define MAC_ISR_TSIS_INDEX             12
 #define MAC_ISR_TSIS_WIDTH             1
 #define MAC_MACA1HR_AE_INDEX           31
 #define MAC_MACA1HR_AE_WIDTH           1
+#define MAC_MDIOIER_SNGLCOMPIE_INDEX   12
+#define MAC_MDIOIER_SNGLCOMPIE_WIDTH   1
+#define MAC_MDIOISR_SNGLCOMPINT_INDEX  12
+#define MAC_MDIOISR_SNGLCOMPINT_WIDTH  1
+#define MAC_MDIOSCAR_DA_INDEX          21
+#define MAC_MDIOSCAR_DA_WIDTH          5
+#define MAC_MDIOSCAR_PA_INDEX          16
+#define MAC_MDIOSCAR_PA_WIDTH          5
+#define MAC_MDIOSCAR_RA_INDEX          0
+#define MAC_MDIOSCAR_RA_WIDTH          16
+#define MAC_MDIOSCAR_REG_INDEX         0
+#define MAC_MDIOSCAR_REG_WIDTH         21
+#define MAC_MDIOSCCDR_BUSY_INDEX       22
+#define MAC_MDIOSCCDR_BUSY_WIDTH       1
+#define MAC_MDIOSCCDR_CMD_INDEX                16
+#define MAC_MDIOSCCDR_CMD_WIDTH                2
+#define MAC_MDIOSCCDR_CR_INDEX         19
+#define MAC_MDIOSCCDR_CR_WIDTH         3
+#define MAC_MDIOSCCDR_DATA_INDEX       0
+#define MAC_MDIOSCCDR_DATA_WIDTH       16
+#define MAC_MDIOSCCDR_SADDR_INDEX      18
+#define MAC_MDIOSCCDR_SADDR_WIDTH      1
 #define MAC_PFR_HMC_INDEX              2
 #define MAC_PFR_HMC_WIDTH              1
 #define MAC_PFR_HPF_INDEX              10
 #define XP_PROP_3_GPIO_TX_FAULT_WIDTH          4
 #define XP_PROP_3_GPIO_ADDR_INDEX              8
 #define XP_PROP_3_GPIO_ADDR_WIDTH              3
+#define XP_PROP_3_MDIO_RESET_INDEX             0
+#define XP_PROP_3_MDIO_RESET_WIDTH             2
+#define XP_PROP_3_MDIO_RESET_I2C_ADDR_INDEX    8
+#define XP_PROP_3_MDIO_RESET_I2C_ADDR_WIDTH    3
+#define XP_PROP_3_MDIO_RESET_I2C_GPIO_INDEX    12
+#define XP_PROP_3_MDIO_RESET_I2C_GPIO_WIDTH    4
+#define XP_PROP_3_MDIO_RESET_INT_GPIO_INDEX    4
+#define XP_PROP_3_MDIO_RESET_INT_GPIO_WIDTH    2
 #define XP_PROP_4_MUX_ADDR_HI_INDEX            8
 #define XP_PROP_4_MUX_ADDR_HI_WIDTH            5
 #define XP_PROP_4_MUX_ADDR_LO_INDEX            0
index 78a2063..30056e2 100644 (file)
@@ -722,6 +722,9 @@ static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata)
        /* Enable all counter interrupts */
        XGMAC_IOWRITE_BITS(pdata, MMC_RIER, ALL_INTERRUPTS, 0xffffffff);
        XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xffffffff);
+
+       /* Enable MDIO single command completion interrupt */
+       XGMAC_IOWRITE_BITS(pdata, MAC_MDIOIER, SNGLCOMPIE, 1);
 }
 
 static void xgbe_enable_ecc_interrupts(struct xgbe_prv_data *pdata)
@@ -1092,6 +1095,36 @@ static int xgbe_config_rx_mode(struct xgbe_prv_data *pdata)
        return 0;
 }
 
+static int xgbe_clr_gpio(struct xgbe_prv_data *pdata, unsigned int gpio)
+{
+       unsigned int reg;
+
+       if (gpio > 16)
+               return -EINVAL;
+
+       reg = XGMAC_IOREAD(pdata, MAC_GPIOSR);
+
+       reg &= ~(1 << (gpio + 16));
+       XGMAC_IOWRITE(pdata, MAC_GPIOSR, reg);
+
+       return 0;
+}
+
+static int xgbe_set_gpio(struct xgbe_prv_data *pdata, unsigned int gpio)
+{
+       unsigned int reg;
+
+       if (gpio > 16)
+               return -EINVAL;
+
+       reg = XGMAC_IOREAD(pdata, MAC_GPIOSR);
+
+       reg |= (1 << (gpio + 16));
+       XGMAC_IOWRITE(pdata, MAC_GPIOSR, reg);
+
+       return 0;
+}
+
 static int xgbe_read_mmd_regs_v2(struct xgbe_prv_data *pdata, int prtad,
                                 int mmd_reg)
 {
@@ -1236,6 +1269,79 @@ static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
        }
 }
 
+static int xgbe_write_ext_mii_regs(struct xgbe_prv_data *pdata, int addr,
+                                  int reg, u16 val)
+{
+       unsigned int mdio_sca, mdio_sccd;
+
+       reinit_completion(&pdata->mdio_complete);
+
+       mdio_sca = 0;
+       XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, REG, reg);
+       XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, addr);
+       XGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca);
+
+       mdio_sccd = 0;
+       XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, val);
+       XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 1);
+       XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
+       XGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
+
+       if (!wait_for_completion_timeout(&pdata->mdio_complete, HZ)) {
+               netdev_err(pdata->netdev, "mdio write operation timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int xgbe_read_ext_mii_regs(struct xgbe_prv_data *pdata, int addr,
+                                 int reg)
+{
+       unsigned int mdio_sca, mdio_sccd;
+
+       reinit_completion(&pdata->mdio_complete);
+
+       mdio_sca = 0;
+       XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, REG, reg);
+       XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, addr);
+       XGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca);
+
+       mdio_sccd = 0;
+       XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 3);
+       XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
+       XGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
+
+       if (!wait_for_completion_timeout(&pdata->mdio_complete, HZ)) {
+               netdev_err(pdata->netdev, "mdio read operation timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return XGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+}
+
+static int xgbe_set_ext_mii_mode(struct xgbe_prv_data *pdata, unsigned int port,
+                                enum xgbe_mdio_mode mode)
+{
+       unsigned int reg_val = 0;
+
+       switch (mode) {
+       case XGBE_MDIO_MODE_CL22:
+               if (port > XGMAC_MAX_C22_PORT)
+                       return -EINVAL;
+               reg_val |= (1 << port);
+               break;
+       case XGBE_MDIO_MODE_CL45:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       XGMAC_IOWRITE(pdata, MAC_MDIOCL22R, reg_val);
+
+       return 0;
+}
+
 static int xgbe_tx_complete(struct xgbe_ring_desc *rdesc)
 {
        return !XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN);
@@ -3386,6 +3492,13 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
 
        hw_if->set_speed = xgbe_set_speed;
 
+       hw_if->set_ext_mii_mode = xgbe_set_ext_mii_mode;
+       hw_if->read_ext_mii_regs = xgbe_read_ext_mii_regs;
+       hw_if->write_ext_mii_regs = xgbe_write_ext_mii_regs;
+
+       hw_if->set_gpio = xgbe_set_gpio;
+       hw_if->clr_gpio = xgbe_clr_gpio;
+
        hw_if->enable_tx = xgbe_enable_tx;
        hw_if->disable_tx = xgbe_disable_tx;
        hw_if->enable_rx = xgbe_enable_rx;
index 7af358f..155190d 100644 (file)
@@ -443,7 +443,7 @@ static irqreturn_t xgbe_isr(int irq, void *data)
        struct xgbe_hw_if *hw_if = &pdata->hw_if;
        struct xgbe_channel *channel;
        unsigned int dma_isr, dma_ch_isr;
-       unsigned int mac_isr, mac_tssr;
+       unsigned int mac_isr, mac_tssr, mac_mdioisr;
        unsigned int i;
 
        /* The DMA interrupt status register also reports MAC and MTL
@@ -503,6 +503,9 @@ static irqreturn_t xgbe_isr(int irq, void *data)
        if (XGMAC_GET_BITS(dma_isr, DMA_ISR, MACIS)) {
                mac_isr = XGMAC_IOREAD(pdata, MAC_ISR);
 
+               netif_dbg(pdata, intr, pdata->netdev, "MAC_ISR=%#010x\n",
+                         mac_isr);
+
                if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCTXIS))
                        hw_if->tx_mmc_int(pdata);
 
@@ -512,6 +515,9 @@ static irqreturn_t xgbe_isr(int irq, void *data)
                if (XGMAC_GET_BITS(mac_isr, MAC_ISR, TSIS)) {
                        mac_tssr = XGMAC_IOREAD(pdata, MAC_TSSR);
 
+                       netif_dbg(pdata, intr, pdata->netdev,
+                                 "MAC_TSSR=%#010x\n", mac_tssr);
+
                        if (XGMAC_GET_BITS(mac_tssr, MAC_TSSR, TXTSC)) {
                                /* Read Tx Timestamp to clear interrupt */
                                pdata->tx_tstamp =
@@ -520,6 +526,17 @@ static irqreturn_t xgbe_isr(int irq, void *data)
                                           &pdata->tx_tstamp_work);
                        }
                }
+
+               if (XGMAC_GET_BITS(mac_isr, MAC_ISR, SMI)) {
+                       mac_mdioisr = XGMAC_IOREAD(pdata, MAC_MDIOISR);
+
+                       netif_dbg(pdata, intr, pdata->netdev,
+                                 "MAC_MDIOISR=%#010x\n", mac_mdioisr);
+
+                       if (XGMAC_GET_BITS(mac_mdioisr, MAC_MDIOISR,
+                                          SNGLCOMPINT))
+                               complete(&pdata->mdio_complete);
+               }
        }
 
        /* If there is not a separate AN irq, handle it here */
index 385b7f6..b87a899 100644 (file)
@@ -189,6 +189,7 @@ struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev)
        spin_lock_init(&pdata->tstamp_lock);
        mutex_init(&pdata->i2c_mutex);
        init_completion(&pdata->i2c_complete);
+       init_completion(&pdata->mdio_complete);
 
        pdata->msg_enable = netif_msg_init(debug, default_msg_level);
 
index 5d8cd8b..9848f74 100644 (file)
@@ -269,6 +269,14 @@ struct xgbe_sfp_ascii {
        } u;
 };
 
+/* MDIO PHY reset types */
+enum xgbe_mdio_reset {
+       XGBE_MDIO_RESET_NONE = 0,
+       XGBE_MDIO_RESET_I2C_GPIO,
+       XGBE_MDIO_RESET_INT_GPIO,
+       XGBE_MDIO_RESET_MAX,
+};
+
 /* PHY related configuration information */
 struct xgbe_phy_data {
        enum xgbe_port_mode port_mode;
@@ -316,6 +324,9 @@ struct xgbe_phy_data {
        enum xgbe_mdio_mode phydev_mode;
        struct mii_bus *mii;
        struct phy_device *phydev;
+       enum xgbe_mdio_reset mdio_reset;
+       unsigned int mdio_reset_addr;
+       unsigned int mdio_reset_gpio;
 };
 
 /* I2C, MDIO and GPIO lines are muxed, so only one device at a time */
@@ -486,6 +497,22 @@ static int xgbe_phy_get_comm_ownership(struct xgbe_prv_data *pdata)
        return -ETIMEDOUT;
 }
 
+static int xgbe_phy_mdio_mii_write(struct xgbe_prv_data *pdata, int addr,
+                                  int reg, u16 val)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       if (reg & MII_ADDR_C45) {
+               if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL45)
+                       return -ENOTSUPP;
+       } else {
+               if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL22)
+                       return -ENOTSUPP;
+       }
+
+       return pdata->hw_if.write_ext_mii_regs(pdata, addr, reg, val);
+}
+
 static int xgbe_phy_i2c_mii_write(struct xgbe_prv_data *pdata, int reg, u16 val)
 {
        __be16 *mii_val;
@@ -520,6 +547,8 @@ static int xgbe_phy_mii_write(struct mii_bus *mii, int addr, int reg, u16 val)
 
        if (phy_data->conn_type == XGBE_CONN_TYPE_SFP)
                ret = xgbe_phy_i2c_mii_write(pdata, reg, val);
+       else if (phy_data->conn_type & XGBE_CONN_TYPE_MDIO)
+               ret = xgbe_phy_mdio_mii_write(pdata, addr, reg, val);
        else
                ret = -ENOTSUPP;
 
@@ -528,6 +557,22 @@ static int xgbe_phy_mii_write(struct mii_bus *mii, int addr, int reg, u16 val)
        return ret;
 }
 
+static int xgbe_phy_mdio_mii_read(struct xgbe_prv_data *pdata, int addr,
+                                 int reg)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       if (reg & MII_ADDR_C45) {
+               if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL45)
+                       return -ENOTSUPP;
+       } else {
+               if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL22)
+                       return -ENOTSUPP;
+       }
+
+       return pdata->hw_if.read_ext_mii_regs(pdata, addr, reg);
+}
+
 static int xgbe_phy_i2c_mii_read(struct xgbe_prv_data *pdata, int reg)
 {
        __be16 mii_val;
@@ -562,6 +607,8 @@ static int xgbe_phy_mii_read(struct mii_bus *mii, int addr, int reg)
 
        if (phy_data->conn_type == XGBE_CONN_TYPE_SFP)
                ret = xgbe_phy_i2c_mii_read(pdata, reg);
+       else if (phy_data->conn_type & XGBE_CONN_TYPE_MDIO)
+               ret = xgbe_phy_mdio_mii_read(pdata, addr, reg);
        else
                ret = -ENOTSUPP;
 
@@ -1323,9 +1370,13 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
        case XGBE_PORT_MODE_BACKPLANE_2500:
                return XGBE_AN_MODE_NONE;
        case XGBE_PORT_MODE_1000BASE_T:
+               return XGBE_AN_MODE_CL37_SGMII;
        case XGBE_PORT_MODE_1000BASE_X:
+               return XGBE_AN_MODE_CL37;
        case XGBE_PORT_MODE_NBASE_T:
+               return XGBE_AN_MODE_CL37_SGMII;
        case XGBE_PORT_MODE_10GBASE_T:
+               return XGBE_AN_MODE_CL73;
        case XGBE_PORT_MODE_10GBASE_R:
                return XGBE_AN_MODE_NONE;
        case XGBE_PORT_MODE_SFP:
@@ -1587,6 +1638,24 @@ static enum xgbe_mode xgbe_phy_cur_mode(struct xgbe_prv_data *pdata)
        return phy_data->cur_mode;
 }
 
+static enum xgbe_mode xgbe_phy_switch_baset_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       /* No switching if not 10GBase-T */
+       if (phy_data->port_mode != XGBE_PORT_MODE_10GBASE_T)
+               return xgbe_phy_cur_mode(pdata);
+
+       switch (xgbe_phy_cur_mode(pdata)) {
+       case XGBE_MODE_SGMII_100:
+       case XGBE_MODE_SGMII_1000:
+               return XGBE_MODE_KR;
+       case XGBE_MODE_KR:
+       default:
+               return XGBE_MODE_SGMII_1000;
+       }
+}
+
 static enum xgbe_mode xgbe_phy_switch_bp_2500_mode(struct xgbe_prv_data *pdata)
 {
        return XGBE_MODE_KX_2500;
@@ -1614,11 +1683,11 @@ static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata)
        case XGBE_PORT_MODE_BACKPLANE_2500:
                return xgbe_phy_switch_bp_2500_mode(pdata);
        case XGBE_PORT_MODE_1000BASE_T:
-       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_NBASE_T:
        case XGBE_PORT_MODE_10GBASE_T:
+               return xgbe_phy_switch_baset_mode(pdata);
+       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_10GBASE_R:
-               return XGBE_MODE_UNKNOWN;
        case XGBE_PORT_MODE_SFP:
                /* No switching, so just return current mode */
                return xgbe_phy_cur_mode(pdata);
@@ -1627,6 +1696,34 @@ static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata)
        }
 }
 
+static enum xgbe_mode xgbe_phy_get_basex_mode(struct xgbe_phy_data *phy_data,
+                                             int speed)
+{
+       switch (speed) {
+       case SPEED_1000:
+               return XGBE_MODE_X;
+       case SPEED_10000:
+               return XGBE_MODE_KR;
+       default:
+               return XGBE_MODE_UNKNOWN;
+       }
+}
+
+static enum xgbe_mode xgbe_phy_get_baset_mode(struct xgbe_phy_data *phy_data,
+                                             int speed)
+{
+       switch (speed) {
+       case SPEED_100:
+               return XGBE_MODE_SGMII_100;
+       case SPEED_1000:
+               return XGBE_MODE_SGMII_1000;
+       case SPEED_10000:
+               return XGBE_MODE_KR;
+       default:
+               return XGBE_MODE_UNKNOWN;
+       }
+}
+
 static enum xgbe_mode xgbe_phy_get_sfp_mode(struct xgbe_phy_data *phy_data,
                                            int speed)
 {
@@ -1679,11 +1776,12 @@ static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata,
        case XGBE_PORT_MODE_BACKPLANE_2500:
                return xgbe_phy_get_bp_2500_mode(speed);
        case XGBE_PORT_MODE_1000BASE_T:
-       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_NBASE_T:
        case XGBE_PORT_MODE_10GBASE_T:
+               return xgbe_phy_get_baset_mode(phy_data, speed);
+       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_10GBASE_R:
-               return XGBE_MODE_UNKNOWN;
+               return xgbe_phy_get_basex_mode(phy_data, speed);
        case XGBE_PORT_MODE_SFP:
                return xgbe_phy_get_sfp_mode(phy_data, speed);
        default:
@@ -1737,6 +1835,39 @@ static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata,
        return false;
 }
 
+static bool xgbe_phy_use_basex_mode(struct xgbe_prv_data *pdata,
+                                   enum xgbe_mode mode)
+{
+       switch (mode) {
+       case XGBE_MODE_X:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_1000baseT_Full);
+       case XGBE_MODE_KR:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_10000baseT_Full);
+       default:
+               return false;
+       }
+}
+
+static bool xgbe_phy_use_baset_mode(struct xgbe_prv_data *pdata,
+                                   enum xgbe_mode mode)
+{
+       switch (mode) {
+       case XGBE_MODE_SGMII_100:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_100baseT_Full);
+       case XGBE_MODE_SGMII_1000:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_1000baseT_Full);
+       case XGBE_MODE_KR:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_10000baseT_Full);
+       default:
+               return false;
+       }
+}
+
 static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata,
                                  enum xgbe_mode mode)
 {
@@ -1803,11 +1934,12 @@ static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode)
        case XGBE_PORT_MODE_BACKPLANE_2500:
                return xgbe_phy_use_bp_2500_mode(pdata, mode);
        case XGBE_PORT_MODE_1000BASE_T:
-       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_NBASE_T:
        case XGBE_PORT_MODE_10GBASE_T:
+               return xgbe_phy_use_baset_mode(pdata, mode);
+       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_10GBASE_R:
-               return false;
+               return xgbe_phy_use_basex_mode(pdata, mode);
        case XGBE_PORT_MODE_SFP:
                return xgbe_phy_use_sfp_mode(pdata, mode);
        default:
@@ -1815,6 +1947,33 @@ static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode)
        }
 }
 
+static bool xgbe_phy_valid_speed_basex_mode(struct xgbe_phy_data *phy_data,
+                                           int speed)
+{
+       switch (speed) {
+       case SPEED_1000:
+               return (phy_data->port_mode == XGBE_PORT_MODE_1000BASE_X);
+       case SPEED_10000:
+               return (phy_data->port_mode == XGBE_PORT_MODE_10GBASE_R);
+       default:
+               return false;
+       }
+}
+
+static bool xgbe_phy_valid_speed_baset_mode(struct xgbe_phy_data *phy_data,
+                                           int speed)
+{
+       switch (speed) {
+       case SPEED_100:
+       case SPEED_1000:
+               return true;
+       case SPEED_10000:
+               return (phy_data->port_mode == XGBE_PORT_MODE_10GBASE_T);
+       default:
+               return false;
+       }
+}
+
 static bool xgbe_phy_valid_speed_sfp_mode(struct xgbe_phy_data *phy_data,
                                          int speed)
 {
@@ -1862,11 +2021,12 @@ static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed)
        case XGBE_PORT_MODE_BACKPLANE_2500:
                return xgbe_phy_valid_speed_bp_2500_mode(speed);
        case XGBE_PORT_MODE_1000BASE_T:
-       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_NBASE_T:
        case XGBE_PORT_MODE_10GBASE_T:
+               return xgbe_phy_valid_speed_baset_mode(phy_data, speed);
+       case XGBE_PORT_MODE_1000BASE_X:
        case XGBE_PORT_MODE_10GBASE_R:
-               return false;
+               return xgbe_phy_valid_speed_basex_mode(phy_data, speed);
        case XGBE_PORT_MODE_SFP:
                return xgbe_phy_valid_speed_sfp_mode(phy_data, speed);
        default:
@@ -1992,6 +2152,121 @@ static void xgbe_phy_sfp_setup(struct xgbe_prv_data *pdata)
        xgbe_phy_sfp_gpio_setup(pdata);
 }
 
+static int xgbe_phy_int_mdio_reset(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int ret;
+
+       ret = pdata->hw_if.set_gpio(pdata, phy_data->mdio_reset_gpio);
+       if (ret)
+               return ret;
+
+       ret = pdata->hw_if.clr_gpio(pdata, phy_data->mdio_reset_gpio);
+
+       return ret;
+}
+
+static int xgbe_phy_i2c_mdio_reset(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       u8 gpio_reg, gpio_ports[2], gpio_data[3];
+       int ret;
+
+       /* Read the output port registers */
+       gpio_reg = 2;
+       ret = xgbe_phy_i2c_read(pdata, phy_data->mdio_reset_addr,
+                               &gpio_reg, sizeof(gpio_reg),
+                               gpio_ports, sizeof(gpio_ports));
+       if (ret)
+               return ret;
+
+       /* Prepare to write the GPIO data */
+       gpio_data[0] = 2;
+       gpio_data[1] = gpio_ports[0];
+       gpio_data[2] = gpio_ports[1];
+
+       /* Set the GPIO pin */
+       if (phy_data->mdio_reset_gpio < 8)
+               gpio_data[1] |= (1 << (phy_data->mdio_reset_gpio % 8));
+       else
+               gpio_data[2] |= (1 << (phy_data->mdio_reset_gpio % 8));
+
+       /* Write the output port registers */
+       ret = xgbe_phy_i2c_write(pdata, phy_data->mdio_reset_addr,
+                                gpio_data, sizeof(gpio_data));
+       if (ret)
+               return ret;
+
+       /* Clear the GPIO pin */
+       if (phy_data->mdio_reset_gpio < 8)
+               gpio_data[1] &= ~(1 << (phy_data->mdio_reset_gpio % 8));
+       else
+               gpio_data[2] &= ~(1 << (phy_data->mdio_reset_gpio % 8));
+
+       /* Write the output port registers */
+       ret = xgbe_phy_i2c_write(pdata, phy_data->mdio_reset_addr,
+                                gpio_data, sizeof(gpio_data));
+
+       return ret;
+}
+
+static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       int ret;
+
+       if (phy_data->conn_type != XGBE_CONN_TYPE_MDIO)
+               return 0;
+
+       ret = xgbe_phy_get_comm_ownership(pdata);
+       if (ret)
+               return ret;
+
+       if (phy_data->mdio_reset == XGBE_MDIO_RESET_I2C_GPIO)
+               ret = xgbe_phy_i2c_mdio_reset(pdata);
+       else if (phy_data->mdio_reset == XGBE_MDIO_RESET_INT_GPIO)
+               ret = xgbe_phy_int_mdio_reset(pdata);
+
+       xgbe_phy_put_comm_ownership(pdata);
+
+       return ret;
+}
+
+static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int reg;
+
+       if (phy_data->conn_type != XGBE_CONN_TYPE_MDIO)
+               return 0;
+
+       reg = XP_IOREAD(pdata, XP_PROP_3);
+       phy_data->mdio_reset = XP_GET_BITS(reg, XP_PROP_3, MDIO_RESET);
+       switch (phy_data->mdio_reset) {
+       case XGBE_MDIO_RESET_NONE:
+       case XGBE_MDIO_RESET_I2C_GPIO:
+       case XGBE_MDIO_RESET_INT_GPIO:
+               break;
+       default:
+               dev_err(pdata->dev, "unsupported MDIO reset (%#x)\n",
+                       phy_data->mdio_reset);
+               return -EINVAL;
+       }
+
+       if (phy_data->mdio_reset == XGBE_MDIO_RESET_I2C_GPIO) {
+               phy_data->mdio_reset_addr = XGBE_GPIO_ADDRESS_PCA9555 +
+                                           XP_GET_BITS(reg, XP_PROP_3,
+                                                       MDIO_RESET_I2C_ADDR);
+               phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3,
+                                                       MDIO_RESET_I2C_GPIO);
+       } else if (phy_data->mdio_reset == XGBE_MDIO_RESET_INT_GPIO) {
+               phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3,
+                                                       MDIO_RESET_INT_GPIO);
+       }
+
+       return 0;
+}
+
 static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -2022,7 +2297,8 @@ static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata)
                        return false;
                break;
        case XGBE_PORT_MODE_10GBASE_T:
-               if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) ||
+               if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) ||
                    (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000))
                        return false;
                break;
@@ -2142,6 +2418,7 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
        enum xgbe_mode cur_mode;
+       int ret;
 
        /* Reset by power cycling the PHY */
        cur_mode = phy_data->cur_mode;
@@ -2152,6 +2429,10 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
                return 0;
 
        /* Reset the external PHY */
+       ret = xgbe_phy_mdio_reset(pdata);
+       if (ret)
+               return ret;
+
        return phy_init_hw(phy_data->phydev);
 }
 
@@ -2213,6 +2494,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                return -EINVAL;
        }
 
+       /* Check for and validate MDIO reset support */
+       ret = xgbe_phy_mdio_reset_setup(pdata);
+       if (ret)
+               return ret;
+
        /* Indicate current mode is unknown */
        phy_data->cur_mode = XGBE_MODE_UNKNOWN;
 
@@ -2220,6 +2506,7 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
        pdata->phy.supported = 0;
 
        switch (phy_data->port_mode) {
+       /* Backplane support */
        case XGBE_PORT_MODE_BACKPLANE:
                pdata->phy.supported |= SUPPORTED_Autoneg;
                pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
@@ -2246,12 +2533,91 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
 
                phy_data->phydev_mode = XGBE_MDIO_MODE_NONE;
                break;
+
+       /* MDIO 1GBase-T support */
        case XGBE_PORT_MODE_1000BASE_T:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) {
+                       pdata->phy.supported |= SUPPORTED_100baseT_Full;
+                       phy_data->start_mode = XGBE_MODE_SGMII_100;
+               }
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+                       phy_data->start_mode = XGBE_MODE_SGMII_1000;
+               }
+
+               phy_data->phydev_mode = XGBE_MDIO_MODE_CL22;
+               break;
+
+       /* MDIO Base-X support */
        case XGBE_PORT_MODE_1000BASE_X:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_FIBRE;
+               pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+               phy_data->start_mode = XGBE_MODE_X;
+
+               phy_data->phydev_mode = XGBE_MDIO_MODE_CL22;
+               break;
+
+       /* MDIO NBase-T support */
        case XGBE_PORT_MODE_NBASE_T:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) {
+                       pdata->phy.supported |= SUPPORTED_100baseT_Full;
+                       phy_data->start_mode = XGBE_MODE_SGMII_100;
+               }
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+                       phy_data->start_mode = XGBE_MODE_SGMII_1000;
+               }
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500) {
+                       pdata->phy.supported |= SUPPORTED_2500baseX_Full;
+                       phy_data->start_mode = XGBE_MODE_KX_2500;
+               }
+
+               phy_data->phydev_mode = XGBE_MDIO_MODE_CL45;
+               break;
+
+       /* 10GBase-T support */
        case XGBE_PORT_MODE_10GBASE_T:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) {
+                       pdata->phy.supported |= SUPPORTED_100baseT_Full;
+                       phy_data->start_mode = XGBE_MODE_SGMII_100;
+               }
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+                       phy_data->start_mode = XGBE_MODE_SGMII_1000;
+               }
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) {
+                       pdata->phy.supported |= SUPPORTED_10000baseT_Full;
+                       phy_data->start_mode = XGBE_MODE_KR;
+               }
+
+               phy_data->phydev_mode = XGBE_MDIO_MODE_NONE;
+               break;
+
+       /* 10GBase-R support */
        case XGBE_PORT_MODE_10GBASE_R:
-               return -ENODEV;
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               pdata->phy.supported |= SUPPORTED_10000baseT_Full;
+               if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE)
+                       pdata->phy.supported |= SUPPORTED_10000baseR_FEC;
+               phy_data->start_mode = XGBE_MODE_SFI;
+
+               phy_data->phydev_mode = XGBE_MDIO_MODE_NONE;
+               break;
+
+       /* SFP support */
        case XGBE_PORT_MODE_SFP:
                pdata->phy.supported |= SUPPORTED_Autoneg;
                pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
index a691f84..34db470 100644 (file)
 /* ECC correctable error notification window (seconds) */
 #define XGBE_ECC_LIMIT                 60
 
+/* MDIO port types */
+#define XGMAC_MAX_C22_PORT             3
+
 struct xgbe_prv_data;
 
 struct xgbe_packet_data {
@@ -675,6 +678,14 @@ struct xgbe_hw_if {
        void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int);
        int (*set_speed)(struct xgbe_prv_data *, int);
 
+       int (*set_ext_mii_mode)(struct xgbe_prv_data *, unsigned int,
+                               enum xgbe_mdio_mode);
+       int (*read_ext_mii_regs)(struct xgbe_prv_data *, int, int);
+       int (*write_ext_mii_regs)(struct xgbe_prv_data *, int, int, u16);
+
+       int (*set_gpio)(struct xgbe_prv_data *, unsigned int);
+       int (*clr_gpio)(struct xgbe_prv_data *, unsigned int);
+
        void (*enable_tx)(struct xgbe_prv_data *);
        void (*disable_tx)(struct xgbe_prv_data *);
        void (*enable_rx)(struct xgbe_prv_data *);
@@ -1111,6 +1122,7 @@ struct xgbe_prv_data {
        struct xgbe_phy phy;
        int mdio_mmd;
        unsigned long link_check;
+       struct completion mdio_complete;
 
        char an_name[IFNAMSIZ + 32];
        struct workqueue_struct *an_workqueue;