Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2011 - 2012 Cavium, Inc.
0004  */
0005 
0006 #include <linux/module.h>
0007 #include <linux/phy.h>
0008 #include <linux/of.h>
0009 
0010 #define PHY_ID_BCM8706  0x0143bdc1
0011 #define PHY_ID_BCM8727  0x0143bff0
0012 
0013 #define BCM87XX_PMD_RX_SIGNAL_DETECT    0x000a
0014 #define BCM87XX_10GBASER_PCS_STATUS 0x0020
0015 #define BCM87XX_XGXS_LANE_STATUS    0x0018
0016 
0017 #define BCM87XX_LASI_CONTROL        0x9002
0018 #define BCM87XX_LASI_STATUS     0x9005
0019 
0020 #if IS_ENABLED(CONFIG_OF_MDIO)
0021 /* Set and/or override some configuration registers based on the
0022  * broadcom,c45-reg-init property stored in the of_node for the phydev.
0023  *
0024  * broadcom,c45-reg-init = <devid reg mask value>,...;
0025  *
0026  * There may be one or more sets of <devid reg mask value>:
0027  *
0028  * devid: which sub-device to use.
0029  * reg: the register.
0030  * mask: if non-zero, ANDed with existing register value.
0031  * value: ORed with the masked value and written to the regiser.
0032  *
0033  */
0034 static int bcm87xx_of_reg_init(struct phy_device *phydev)
0035 {
0036     const __be32 *paddr;
0037     const __be32 *paddr_end;
0038     int len, ret;
0039 
0040     if (!phydev->mdio.dev.of_node)
0041         return 0;
0042 
0043     paddr = of_get_property(phydev->mdio.dev.of_node,
0044                 "broadcom,c45-reg-init", &len);
0045     if (!paddr)
0046         return 0;
0047 
0048     paddr_end = paddr + (len /= sizeof(*paddr));
0049 
0050     ret = 0;
0051 
0052     while (paddr + 3 < paddr_end) {
0053         u16 devid   = be32_to_cpup(paddr++);
0054         u16 reg     = be32_to_cpup(paddr++);
0055         u16 mask    = be32_to_cpup(paddr++);
0056         u16 val_bits    = be32_to_cpup(paddr++);
0057         int val = 0;
0058 
0059         if (mask) {
0060             val = phy_read_mmd(phydev, devid, reg);
0061             if (val < 0) {
0062                 ret = val;
0063                 goto err;
0064             }
0065             val &= mask;
0066         }
0067         val |= val_bits;
0068 
0069         ret = phy_write_mmd(phydev, devid, reg, val);
0070         if (ret < 0)
0071             goto err;
0072     }
0073 err:
0074     return ret;
0075 }
0076 #else
0077 static int bcm87xx_of_reg_init(struct phy_device *phydev)
0078 {
0079     return 0;
0080 }
0081 #endif /* CONFIG_OF_MDIO */
0082 
0083 static int bcm87xx_get_features(struct phy_device *phydev)
0084 {
0085     linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
0086              phydev->supported);
0087     return 0;
0088 }
0089 
0090 static int bcm87xx_config_init(struct phy_device *phydev)
0091 {
0092     return bcm87xx_of_reg_init(phydev);
0093 }
0094 
0095 static int bcm87xx_config_aneg(struct phy_device *phydev)
0096 {
0097     return -EINVAL;
0098 }
0099 
0100 static int bcm87xx_read_status(struct phy_device *phydev)
0101 {
0102     int rx_signal_detect;
0103     int pcs_status;
0104     int xgxs_lane_status;
0105 
0106     rx_signal_detect = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
0107                     BCM87XX_PMD_RX_SIGNAL_DETECT);
0108     if (rx_signal_detect < 0)
0109         return rx_signal_detect;
0110 
0111     if ((rx_signal_detect & 1) == 0)
0112         goto no_link;
0113 
0114     pcs_status = phy_read_mmd(phydev, MDIO_MMD_PCS,
0115                   BCM87XX_10GBASER_PCS_STATUS);
0116     if (pcs_status < 0)
0117         return pcs_status;
0118 
0119     if ((pcs_status & 1) == 0)
0120         goto no_link;
0121 
0122     xgxs_lane_status = phy_read_mmd(phydev, MDIO_MMD_PHYXS,
0123                     BCM87XX_XGXS_LANE_STATUS);
0124     if (xgxs_lane_status < 0)
0125         return xgxs_lane_status;
0126 
0127     if ((xgxs_lane_status & 0x1000) == 0)
0128         goto no_link;
0129 
0130     phydev->speed = 10000;
0131     phydev->link = 1;
0132     phydev->duplex = 1;
0133     return 0;
0134 
0135 no_link:
0136     phydev->link = 0;
0137     return 0;
0138 }
0139 
0140 static int bcm87xx_config_intr(struct phy_device *phydev)
0141 {
0142     int reg, err;
0143 
0144     reg = phy_read_mmd(phydev, MDIO_MMD_PCS, BCM87XX_LASI_CONTROL);
0145 
0146     if (reg < 0)
0147         return reg;
0148 
0149     if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
0150         err = phy_read_mmd(phydev, MDIO_MMD_PCS, BCM87XX_LASI_STATUS);
0151         if (err)
0152             return err;
0153 
0154         reg |= 1;
0155         err = phy_write_mmd(phydev, MDIO_MMD_PCS,
0156                     BCM87XX_LASI_CONTROL, reg);
0157     } else {
0158         reg &= ~1;
0159         err = phy_write_mmd(phydev, MDIO_MMD_PCS,
0160                     BCM87XX_LASI_CONTROL, reg);
0161         if (err)
0162             return err;
0163 
0164         err = phy_read_mmd(phydev, MDIO_MMD_PCS, BCM87XX_LASI_STATUS);
0165     }
0166 
0167     return err;
0168 }
0169 
0170 static irqreturn_t bcm87xx_handle_interrupt(struct phy_device *phydev)
0171 {
0172     int irq_status;
0173 
0174     irq_status = phy_read(phydev, BCM87XX_LASI_STATUS);
0175     if (irq_status < 0) {
0176         phy_error(phydev);
0177         return IRQ_NONE;
0178     }
0179 
0180     if (irq_status == 0)
0181         return IRQ_NONE;
0182 
0183     phy_trigger_machine(phydev);
0184 
0185     return IRQ_HANDLED;
0186 }
0187 
0188 static int bcm8706_match_phy_device(struct phy_device *phydev)
0189 {
0190     return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706;
0191 }
0192 
0193 static int bcm8727_match_phy_device(struct phy_device *phydev)
0194 {
0195     return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727;
0196 }
0197 
0198 static struct phy_driver bcm87xx_driver[] = {
0199 {
0200     .phy_id     = PHY_ID_BCM8706,
0201     .phy_id_mask    = 0xffffffff,
0202     .name       = "Broadcom BCM8706",
0203     .get_features   = bcm87xx_get_features,
0204     .config_init    = bcm87xx_config_init,
0205     .config_aneg    = bcm87xx_config_aneg,
0206     .read_status    = bcm87xx_read_status,
0207     .config_intr    = bcm87xx_config_intr,
0208     .handle_interrupt = bcm87xx_handle_interrupt,
0209     .match_phy_device = bcm8706_match_phy_device,
0210 }, {
0211     .phy_id     = PHY_ID_BCM8727,
0212     .phy_id_mask    = 0xffffffff,
0213     .name       = "Broadcom BCM8727",
0214     .get_features   = bcm87xx_get_features,
0215     .config_init    = bcm87xx_config_init,
0216     .config_aneg    = bcm87xx_config_aneg,
0217     .read_status    = bcm87xx_read_status,
0218     .config_intr    = bcm87xx_config_intr,
0219     .handle_interrupt = bcm87xx_handle_interrupt,
0220     .match_phy_device = bcm8727_match_phy_device,
0221 } };
0222 
0223 module_phy_driver(bcm87xx_driver);
0224 
0225 MODULE_LICENSE("GPL v2");