|
144 | 144 | #define ATH8035_PHY_ID 0x004dd072 |
145 | 145 | #define AT8030_PHY_ID_MASK 0xffffffef |
146 | 146 |
|
| 147 | +#define AT803X_PAGE_FIBER 0 |
| 148 | +#define AT803X_PAGE_COPPER 1 |
| 149 | + |
147 | 150 | MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); |
148 | 151 | MODULE_AUTHOR("Matus Ujhelyi"); |
149 | 152 | MODULE_LICENSE("GPL"); |
@@ -198,6 +201,35 @@ static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, |
198 | 201 | return phy_write(phydev, AT803X_DEBUG_DATA, val); |
199 | 202 | } |
200 | 203 |
|
| 204 | +static int at803x_write_page(struct phy_device *phydev, int page) |
| 205 | +{ |
| 206 | + int mask; |
| 207 | + int set; |
| 208 | + |
| 209 | + if (page == AT803X_PAGE_COPPER) { |
| 210 | + set = AT803X_BT_BX_REG_SEL; |
| 211 | + mask = 0; |
| 212 | + } else { |
| 213 | + set = 0; |
| 214 | + mask = AT803X_BT_BX_REG_SEL; |
| 215 | + } |
| 216 | + |
| 217 | + return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); |
| 218 | +} |
| 219 | + |
| 220 | +static int at803x_read_page(struct phy_device *phydev) |
| 221 | +{ |
| 222 | + int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); |
| 223 | + |
| 224 | + if (ccr < 0) |
| 225 | + return ccr; |
| 226 | + |
| 227 | + if (ccr & AT803X_BT_BX_REG_SEL) |
| 228 | + return AT803X_PAGE_COPPER; |
| 229 | + |
| 230 | + return AT803X_PAGE_FIBER; |
| 231 | +} |
| 232 | + |
201 | 233 | static int at803x_enable_rx_delay(struct phy_device *phydev) |
202 | 234 | { |
203 | 235 | return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, |
@@ -535,14 +567,28 @@ static int at803x_probe(struct phy_device *phydev) |
535 | 567 | { |
536 | 568 | struct device *dev = &phydev->mdio.dev; |
537 | 569 | struct at803x_priv *priv; |
| 570 | + int ret; |
538 | 571 |
|
539 | 572 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
540 | 573 | if (!priv) |
541 | 574 | return -ENOMEM; |
542 | 575 |
|
543 | 576 | phydev->priv = priv; |
544 | 577 |
|
545 | | - return at803x_parse_dt(phydev); |
| 578 | + ret = at803x_parse_dt(phydev); |
| 579 | + if (ret) |
| 580 | + return ret; |
| 581 | + |
| 582 | + /* Some bootloaders leave the fiber page selected. |
| 583 | + * Switch to the copper page, as otherwise we read |
| 584 | + * the PHY capabilities from the fiber side. |
| 585 | + */ |
| 586 | + if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { |
| 587 | + ret = phy_select_page(phydev, AT803X_PAGE_COPPER); |
| 588 | + ret = phy_restore_page(phydev, AT803X_PAGE_COPPER, ret); |
| 589 | + } |
| 590 | + |
| 591 | + return ret; |
546 | 592 | } |
547 | 593 |
|
548 | 594 | static void at803x_remove(struct phy_device *phydev) |
@@ -1166,6 +1212,8 @@ static struct phy_driver at803x_driver[] = { |
1166 | 1212 | .get_wol = at803x_get_wol, |
1167 | 1213 | .suspend = at803x_suspend, |
1168 | 1214 | .resume = at803x_resume, |
| 1215 | + .read_page = at803x_read_page, |
| 1216 | + .write_page = at803x_write_page, |
1169 | 1217 | /* PHY_GBIT_FEATURES */ |
1170 | 1218 | .read_status = at803x_read_status, |
1171 | 1219 | .config_intr = &at803x_config_intr, |
|
0 commit comments