0001
0002
0003
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
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
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
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");