895 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Broadcom BCM54140 Quad SGMII/QSGMII Copper/Fiber Gigabit PHY3 *4 * Copyright (c) 2020 Michael Walle <michael@walle.cc>5 */6 7#include <linux/bitfield.h>8#include <linux/brcmphy.h>9#include <linux/hwmon.h>10#include <linux/module.h>11#include <linux/phy.h>12 13#include "bcm-phy-lib.h"14 15/* RDB per-port registers16 */17#define BCM54140_RDB_ISR 0x00a /* interrupt status */18#define BCM54140_RDB_IMR 0x00b /* interrupt mask */19#define BCM54140_RDB_INT_LINK BIT(1) /* link status changed */20#define BCM54140_RDB_INT_SPEED BIT(2) /* link speed change */21#define BCM54140_RDB_INT_DUPLEX BIT(3) /* duplex mode changed */22#define BCM54140_RDB_SPARE1 0x012 /* spare control 1 */23#define BCM54140_RDB_SPARE1_LSLM BIT(2) /* link speed LED mode */24#define BCM54140_RDB_SPARE2 0x014 /* spare control 2 */25#define BCM54140_RDB_SPARE2_WS_RTRY_DIS BIT(8) /* wirespeed retry disable */26#define BCM54140_RDB_SPARE2_WS_RTRY_LIMIT GENMASK(4, 2) /* retry limit */27#define BCM54140_RDB_SPARE3 0x015 /* spare control 3 */28#define BCM54140_RDB_SPARE3_BIT0 BIT(0)29#define BCM54140_RDB_LED_CTRL 0x019 /* LED control */30#define BCM54140_RDB_LED_CTRL_ACTLINK0 BIT(4)31#define BCM54140_RDB_LED_CTRL_ACTLINK1 BIT(8)32#define BCM54140_RDB_C_APWR 0x01a /* auto power down control */33#define BCM54140_RDB_C_APWR_SINGLE_PULSE BIT(8) /* single pulse */34#define BCM54140_RDB_C_APWR_APD_MODE_DIS 0 /* ADP disable */35#define BCM54140_RDB_C_APWR_APD_MODE_EN 1 /* ADP enable */36#define BCM54140_RDB_C_APWR_APD_MODE_DIS2 2 /* ADP disable */37#define BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG 3 /* ADP enable w/ aneg */38#define BCM54140_RDB_C_APWR_APD_MODE_MASK GENMASK(6, 5)39#define BCM54140_RDB_C_APWR_SLP_TIM_MASK BIT(4)/* sleep timer */40#define BCM54140_RDB_C_APWR_SLP_TIM_2_7 0 /* 2.7s */41#define BCM54140_RDB_C_APWR_SLP_TIM_5_4 1 /* 5.4s */42#define BCM54140_RDB_C_PWR 0x02a /* copper power control */43#define BCM54140_RDB_C_PWR_ISOLATE BIT(5) /* super isolate mode */44#define BCM54140_RDB_C_MISC_CTRL 0x02f /* misc copper control */45#define BCM54140_RDB_C_MISC_CTRL_WS_EN BIT(4) /* wirespeed enable */46 47/* RDB global registers48 */49#define BCM54140_RDB_TOP_IMR 0x82d /* interrupt mask */50#define BCM54140_RDB_TOP_IMR_PORT0 BIT(4)51#define BCM54140_RDB_TOP_IMR_PORT1 BIT(5)52#define BCM54140_RDB_TOP_IMR_PORT2 BIT(6)53#define BCM54140_RDB_TOP_IMR_PORT3 BIT(7)54#define BCM54140_RDB_MON_CTRL 0x831 /* monitor control */55#define BCM54140_RDB_MON_CTRL_V_MODE BIT(3) /* voltage mode */56#define BCM54140_RDB_MON_CTRL_SEL_MASK GENMASK(2, 1)57#define BCM54140_RDB_MON_CTRL_SEL_TEMP 0 /* meassure temperature */58#define BCM54140_RDB_MON_CTRL_SEL_1V0 1 /* meassure AVDDL 1.0V */59#define BCM54140_RDB_MON_CTRL_SEL_3V3 2 /* meassure AVDDH 3.3V */60#define BCM54140_RDB_MON_CTRL_SEL_RR 3 /* meassure all round-robin */61#define BCM54140_RDB_MON_CTRL_PWR_DOWN BIT(0) /* power-down monitor */62#define BCM54140_RDB_MON_TEMP_VAL 0x832 /* temperature value */63#define BCM54140_RDB_MON_TEMP_MAX 0x833 /* temperature high thresh */64#define BCM54140_RDB_MON_TEMP_MIN 0x834 /* temperature low thresh */65#define BCM54140_RDB_MON_TEMP_DATA_MASK GENMASK(9, 0)66#define BCM54140_RDB_MON_1V0_VAL 0x835 /* AVDDL 1.0V value */67#define BCM54140_RDB_MON_1V0_MAX 0x836 /* AVDDL 1.0V high thresh */68#define BCM54140_RDB_MON_1V0_MIN 0x837 /* AVDDL 1.0V low thresh */69#define BCM54140_RDB_MON_1V0_DATA_MASK GENMASK(10, 0)70#define BCM54140_RDB_MON_3V3_VAL 0x838 /* AVDDH 3.3V value */71#define BCM54140_RDB_MON_3V3_MAX 0x839 /* AVDDH 3.3V high thresh */72#define BCM54140_RDB_MON_3V3_MIN 0x83a /* AVDDH 3.3V low thresh */73#define BCM54140_RDB_MON_3V3_DATA_MASK GENMASK(11, 0)74#define BCM54140_RDB_MON_ISR 0x83b /* interrupt status */75#define BCM54140_RDB_MON_ISR_3V3 BIT(2) /* AVDDH 3.3V alarm */76#define BCM54140_RDB_MON_ISR_1V0 BIT(1) /* AVDDL 1.0V alarm */77#define BCM54140_RDB_MON_ISR_TEMP BIT(0) /* temperature alarm */78 79/* According to the datasheet the formula is:80 * T = 413.35 - (0.49055 * bits[9:0])81 */82#define BCM54140_HWMON_TO_TEMP(v) (413350L - (v) * 491)83#define BCM54140_HWMON_FROM_TEMP(v) DIV_ROUND_CLOSEST_ULL(413350L - (v), 491)84 85/* According to the datasheet the formula is:86 * U = bits[11:0] / 1024 * 220 / 0.287 *88 * Normalized:89 * U = bits[11:0] / 4096 * 251490 */91#define BCM54140_HWMON_TO_IN_1V0(v) ((v) * 2514 >> 11)92#define BCM54140_HWMON_FROM_IN_1V0(v) DIV_ROUND_CLOSEST_ULL(((v) << 11), 2514)93 94/* According to the datasheet the formula is:95 * U = bits[10:0] / 1024 * 880 / 0.796 *97 * Normalized:98 * U = bits[10:0] / 2048 * 440099 */100#define BCM54140_HWMON_TO_IN_3V3(v) ((v) * 4400 >> 12)101#define BCM54140_HWMON_FROM_IN_3V3(v) DIV_ROUND_CLOSEST_ULL(((v) << 12), 4400)102 103#define BCM54140_HWMON_TO_IN(ch, v) ((ch) ? BCM54140_HWMON_TO_IN_3V3(v) \104 : BCM54140_HWMON_TO_IN_1V0(v))105#define BCM54140_HWMON_FROM_IN(ch, v) ((ch) ? BCM54140_HWMON_FROM_IN_3V3(v) \106 : BCM54140_HWMON_FROM_IN_1V0(v))107#define BCM54140_HWMON_IN_MASK(ch) ((ch) ? BCM54140_RDB_MON_3V3_DATA_MASK \108 : BCM54140_RDB_MON_1V0_DATA_MASK)109#define BCM54140_HWMON_IN_VAL_REG(ch) ((ch) ? BCM54140_RDB_MON_3V3_VAL \110 : BCM54140_RDB_MON_1V0_VAL)111#define BCM54140_HWMON_IN_MIN_REG(ch) ((ch) ? BCM54140_RDB_MON_3V3_MIN \112 : BCM54140_RDB_MON_1V0_MIN)113#define BCM54140_HWMON_IN_MAX_REG(ch) ((ch) ? BCM54140_RDB_MON_3V3_MAX \114 : BCM54140_RDB_MON_1V0_MAX)115#define BCM54140_HWMON_IN_ALARM_BIT(ch) ((ch) ? BCM54140_RDB_MON_ISR_3V3 \116 : BCM54140_RDB_MON_ISR_1V0)117 118/* This PHY has two different PHY IDs depening on its MODE_SEL pin. This119 * pin choses between 4x SGMII and QSGMII mode:120 * AE02_5009 4x SGMII121 * AE02_5019 QSGMII122 */123#define BCM54140_PHY_ID_MASK 0xffffffe8124 125#define BCM54140_PHY_ID_REV(phy_id) ((phy_id) & 0x7)126#define BCM54140_REV_B0 1127 128#define BCM54140_DEFAULT_DOWNSHIFT 5129#define BCM54140_MAX_DOWNSHIFT 9130 131enum bcm54140_global_phy {132 BCM54140_BASE_ADDR = 0,133};134 135struct bcm54140_priv {136 int port;137 int base_addr;138#if IS_ENABLED(CONFIG_HWMON)139 /* protect the alarm bits */140 struct mutex alarm_lock;141 u16 alarm;142#endif143};144 145#if IS_ENABLED(CONFIG_HWMON)146static umode_t bcm54140_hwmon_is_visible(const void *data,147 enum hwmon_sensor_types type,148 u32 attr, int channel)149{150 switch (type) {151 case hwmon_in:152 switch (attr) {153 case hwmon_in_min:154 case hwmon_in_max:155 return 0644;156 case hwmon_in_label:157 case hwmon_in_input:158 case hwmon_in_alarm:159 return 0444;160 default:161 return 0;162 }163 case hwmon_temp:164 switch (attr) {165 case hwmon_temp_min:166 case hwmon_temp_max:167 return 0644;168 case hwmon_temp_input:169 case hwmon_temp_alarm:170 return 0444;171 default:172 return 0;173 }174 default:175 return 0;176 }177}178 179static int bcm54140_hwmon_read_alarm(struct device *dev, unsigned int bit,180 long *val)181{182 struct phy_device *phydev = dev_get_drvdata(dev);183 struct bcm54140_priv *priv = phydev->priv;184 int tmp, ret = 0;185 186 mutex_lock(&priv->alarm_lock);187 188 /* latch any alarm bits */189 tmp = bcm_phy_read_rdb(phydev, BCM54140_RDB_MON_ISR);190 if (tmp < 0) {191 ret = tmp;192 goto out;193 }194 priv->alarm |= tmp;195 196 *val = !!(priv->alarm & bit);197 priv->alarm &= ~bit;198 199out:200 mutex_unlock(&priv->alarm_lock);201 return ret;202}203 204static int bcm54140_hwmon_read_temp(struct device *dev, u32 attr, long *val)205{206 struct phy_device *phydev = dev_get_drvdata(dev);207 u16 reg;208 int tmp;209 210 switch (attr) {211 case hwmon_temp_input:212 reg = BCM54140_RDB_MON_TEMP_VAL;213 break;214 case hwmon_temp_min:215 reg = BCM54140_RDB_MON_TEMP_MIN;216 break;217 case hwmon_temp_max:218 reg = BCM54140_RDB_MON_TEMP_MAX;219 break;220 case hwmon_temp_alarm:221 return bcm54140_hwmon_read_alarm(dev,222 BCM54140_RDB_MON_ISR_TEMP,223 val);224 default:225 return -EOPNOTSUPP;226 }227 228 tmp = bcm_phy_read_rdb(phydev, reg);229 if (tmp < 0)230 return tmp;231 232 *val = BCM54140_HWMON_TO_TEMP(tmp & BCM54140_RDB_MON_TEMP_DATA_MASK);233 234 return 0;235}236 237static int bcm54140_hwmon_read_in(struct device *dev, u32 attr,238 int channel, long *val)239{240 struct phy_device *phydev = dev_get_drvdata(dev);241 u16 bit, reg;242 int tmp;243 244 switch (attr) {245 case hwmon_in_input:246 reg = BCM54140_HWMON_IN_VAL_REG(channel);247 break;248 case hwmon_in_min:249 reg = BCM54140_HWMON_IN_MIN_REG(channel);250 break;251 case hwmon_in_max:252 reg = BCM54140_HWMON_IN_MAX_REG(channel);253 break;254 case hwmon_in_alarm:255 bit = BCM54140_HWMON_IN_ALARM_BIT(channel);256 return bcm54140_hwmon_read_alarm(dev, bit, val);257 default:258 return -EOPNOTSUPP;259 }260 261 tmp = bcm_phy_read_rdb(phydev, reg);262 if (tmp < 0)263 return tmp;264 265 tmp &= BCM54140_HWMON_IN_MASK(channel);266 *val = BCM54140_HWMON_TO_IN(channel, tmp);267 268 return 0;269}270 271static int bcm54140_hwmon_read(struct device *dev,272 enum hwmon_sensor_types type, u32 attr,273 int channel, long *val)274{275 switch (type) {276 case hwmon_temp:277 return bcm54140_hwmon_read_temp(dev, attr, val);278 case hwmon_in:279 return bcm54140_hwmon_read_in(dev, attr, channel, val);280 default:281 return -EOPNOTSUPP;282 }283}284 285static const char *const bcm54140_hwmon_in_labels[] = {286 "AVDDL",287 "AVDDH",288};289 290static int bcm54140_hwmon_read_string(struct device *dev,291 enum hwmon_sensor_types type, u32 attr,292 int channel, const char **str)293{294 switch (type) {295 case hwmon_in:296 switch (attr) {297 case hwmon_in_label:298 *str = bcm54140_hwmon_in_labels[channel];299 return 0;300 default:301 return -EOPNOTSUPP;302 }303 default:304 return -EOPNOTSUPP;305 }306}307 308static int bcm54140_hwmon_write_temp(struct device *dev, u32 attr,309 int channel, long val)310{311 struct phy_device *phydev = dev_get_drvdata(dev);312 u16 mask = BCM54140_RDB_MON_TEMP_DATA_MASK;313 u16 reg;314 315 val = clamp_val(val, BCM54140_HWMON_TO_TEMP(mask),316 BCM54140_HWMON_TO_TEMP(0));317 318 switch (attr) {319 case hwmon_temp_min:320 reg = BCM54140_RDB_MON_TEMP_MIN;321 break;322 case hwmon_temp_max:323 reg = BCM54140_RDB_MON_TEMP_MAX;324 break;325 default:326 return -EOPNOTSUPP;327 }328 329 return bcm_phy_modify_rdb(phydev, reg, mask,330 BCM54140_HWMON_FROM_TEMP(val));331}332 333static int bcm54140_hwmon_write_in(struct device *dev, u32 attr,334 int channel, long val)335{336 struct phy_device *phydev = dev_get_drvdata(dev);337 u16 mask = BCM54140_HWMON_IN_MASK(channel);338 u16 reg;339 340 val = clamp_val(val, 0, BCM54140_HWMON_TO_IN(channel, mask));341 342 switch (attr) {343 case hwmon_in_min:344 reg = BCM54140_HWMON_IN_MIN_REG(channel);345 break;346 case hwmon_in_max:347 reg = BCM54140_HWMON_IN_MAX_REG(channel);348 break;349 default:350 return -EOPNOTSUPP;351 }352 353 return bcm_phy_modify_rdb(phydev, reg, mask,354 BCM54140_HWMON_FROM_IN(channel, val));355}356 357static int bcm54140_hwmon_write(struct device *dev,358 enum hwmon_sensor_types type, u32 attr,359 int channel, long val)360{361 switch (type) {362 case hwmon_temp:363 return bcm54140_hwmon_write_temp(dev, attr, channel, val);364 case hwmon_in:365 return bcm54140_hwmon_write_in(dev, attr, channel, val);366 default:367 return -EOPNOTSUPP;368 }369}370 371static const struct hwmon_channel_info * const bcm54140_hwmon_info[] = {372 HWMON_CHANNEL_INFO(temp,373 HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |374 HWMON_T_ALARM),375 HWMON_CHANNEL_INFO(in,376 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |377 HWMON_I_ALARM | HWMON_I_LABEL,378 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |379 HWMON_I_ALARM | HWMON_I_LABEL),380 NULL381};382 383static const struct hwmon_ops bcm54140_hwmon_ops = {384 .is_visible = bcm54140_hwmon_is_visible,385 .read = bcm54140_hwmon_read,386 .read_string = bcm54140_hwmon_read_string,387 .write = bcm54140_hwmon_write,388};389 390static const struct hwmon_chip_info bcm54140_chip_info = {391 .ops = &bcm54140_hwmon_ops,392 .info = bcm54140_hwmon_info,393};394 395static int bcm54140_enable_monitoring(struct phy_device *phydev)396{397 u16 mask, set;398 399 /* 3.3V voltage mode */400 set = BCM54140_RDB_MON_CTRL_V_MODE;401 402 /* select round-robin */403 mask = BCM54140_RDB_MON_CTRL_SEL_MASK;404 set |= FIELD_PREP(BCM54140_RDB_MON_CTRL_SEL_MASK,405 BCM54140_RDB_MON_CTRL_SEL_RR);406 407 /* remove power-down bit */408 mask |= BCM54140_RDB_MON_CTRL_PWR_DOWN;409 410 return bcm_phy_modify_rdb(phydev, BCM54140_RDB_MON_CTRL, mask, set);411}412 413static int bcm54140_probe_once(struct phy_device *phydev)414{415 struct device *hwmon;416 int ret;417 418 /* enable hardware monitoring */419 ret = bcm54140_enable_monitoring(phydev);420 if (ret)421 return ret;422 423 hwmon = devm_hwmon_device_register_with_info(&phydev->mdio.dev,424 "BCM54140", phydev,425 &bcm54140_chip_info,426 NULL);427 return PTR_ERR_OR_ZERO(hwmon);428}429#endif430 431static int bcm54140_base_read_rdb(struct phy_device *phydev, u16 rdb)432{433 int ret;434 435 phy_lock_mdio_bus(phydev);436 ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,437 MII_BCM54XX_RDB_ADDR, rdb);438 if (ret < 0)439 goto out;440 441 ret = __phy_package_read(phydev, BCM54140_BASE_ADDR,442 MII_BCM54XX_RDB_DATA);443 444out:445 phy_unlock_mdio_bus(phydev);446 return ret;447}448 449static int bcm54140_base_write_rdb(struct phy_device *phydev,450 u16 rdb, u16 val)451{452 int ret;453 454 phy_lock_mdio_bus(phydev);455 ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,456 MII_BCM54XX_RDB_ADDR, rdb);457 if (ret < 0)458 goto out;459 460 ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,461 MII_BCM54XX_RDB_DATA, val);462 463out:464 phy_unlock_mdio_bus(phydev);465 return ret;466}467 468/* Under some circumstances a core PLL may not lock, this will then prevent469 * a successful link establishment. Restart the PLL after the voltages are470 * stable to workaround this issue.471 */472static int bcm54140_b0_workaround(struct phy_device *phydev)473{474 int spare3;475 int ret;476 477 spare3 = bcm_phy_read_rdb(phydev, BCM54140_RDB_SPARE3);478 if (spare3 < 0)479 return spare3;480 481 spare3 &= ~BCM54140_RDB_SPARE3_BIT0;482 483 ret = bcm_phy_write_rdb(phydev, BCM54140_RDB_SPARE3, spare3);484 if (ret)485 return ret;486 487 ret = phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);488 if (ret)489 return ret;490 491 ret = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);492 if (ret)493 return ret;494 495 spare3 |= BCM54140_RDB_SPARE3_BIT0;496 497 return bcm_phy_write_rdb(phydev, BCM54140_RDB_SPARE3, spare3);498}499 500/* The BCM54140 is a quad PHY where only the first port has access to the501 * global register. Thus we need to find out its PHY address.502 *503 */504static int bcm54140_get_base_addr_and_port(struct phy_device *phydev)505{506 struct bcm54140_priv *priv = phydev->priv;507 struct mii_bus *bus = phydev->mdio.bus;508 int addr, min_addr, max_addr;509 int step = 1;510 u32 phy_id;511 int tmp;512 513 min_addr = phydev->mdio.addr;514 max_addr = phydev->mdio.addr;515 addr = phydev->mdio.addr;516 517 /* We scan forward and backwards and look for PHYs which have the518 * same phy_id like we do. Step 1 will scan forward, step 2519 * backwards. Once we are finished, we have a min_addr and520 * max_addr which resembles the range of PHY addresses of the same521 * type of PHY. There is one caveat; there may be many PHYs of522 * the same type, but we know that each PHY takes exactly 4523 * consecutive addresses. Therefore we can deduce our offset524 * to the base address of this quad PHY.525 */526 527 while (1) {528 if (step == 3) {529 break;530 } else if (step == 1) {531 max_addr = addr;532 addr++;533 } else {534 min_addr = addr;535 addr--;536 }537 538 if (addr < 0 || addr >= PHY_MAX_ADDR) {539 addr = phydev->mdio.addr;540 step++;541 continue;542 }543 544 /* read the PHY id */545 tmp = mdiobus_read(bus, addr, MII_PHYSID1);546 if (tmp < 0)547 return tmp;548 phy_id = tmp << 16;549 tmp = mdiobus_read(bus, addr, MII_PHYSID2);550 if (tmp < 0)551 return tmp;552 phy_id |= tmp;553 554 /* see if it is still the same PHY */555 if ((phy_id & phydev->drv->phy_id_mask) !=556 (phydev->drv->phy_id & phydev->drv->phy_id_mask)) {557 addr = phydev->mdio.addr;558 step++;559 }560 }561 562 /* The range we get should be a multiple of four. Please note that both563 * the min_addr and max_addr are inclusive. So we have to add one if we564 * subtract them.565 */566 if ((max_addr - min_addr + 1) % 4) {567 dev_err(&phydev->mdio.dev,568 "Detected Quad PHY IDs %d..%d doesn't make sense.\n",569 min_addr, max_addr);570 return -EINVAL;571 }572 573 priv->port = (phydev->mdio.addr - min_addr) % 4;574 priv->base_addr = phydev->mdio.addr - priv->port;575 576 return 0;577}578 579static int bcm54140_probe(struct phy_device *phydev)580{581 struct bcm54140_priv *priv;582 int ret;583 584 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);585 if (!priv)586 return -ENOMEM;587 588 phydev->priv = priv;589 590 ret = bcm54140_get_base_addr_and_port(phydev);591 if (ret)592 return ret;593 594 devm_phy_package_join(&phydev->mdio.dev, phydev, priv->base_addr, 0);595 596#if IS_ENABLED(CONFIG_HWMON)597 mutex_init(&priv->alarm_lock);598 599 if (phy_package_init_once(phydev)) {600 ret = bcm54140_probe_once(phydev);601 if (ret)602 return ret;603 }604#endif605 606 phydev_dbg(phydev, "probed (port %d, base PHY address %d)\n",607 priv->port, priv->base_addr);608 609 return 0;610}611 612static int bcm54140_config_init(struct phy_device *phydev)613{614 u16 reg = 0xffff;615 int ret;616 617 /* Apply hardware errata */618 if (BCM54140_PHY_ID_REV(phydev->phy_id) == BCM54140_REV_B0) {619 ret = bcm54140_b0_workaround(phydev);620 if (ret)621 return ret;622 }623 624 /* Unmask events we are interested in. */625 reg &= ~(BCM54140_RDB_INT_DUPLEX |626 BCM54140_RDB_INT_SPEED |627 BCM54140_RDB_INT_LINK);628 ret = bcm_phy_write_rdb(phydev, BCM54140_RDB_IMR, reg);629 if (ret)630 return ret;631 632 /* LED1=LINKSPD[1], LED2=LINKSPD[2], LED3=LINK/ACTIVITY */633 ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_SPARE1,634 0, BCM54140_RDB_SPARE1_LSLM);635 if (ret)636 return ret;637 638 ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_LED_CTRL,639 0, BCM54140_RDB_LED_CTRL_ACTLINK0);640 if (ret)641 return ret;642 643 /* disable super isolate mode */644 return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_PWR,645 BCM54140_RDB_C_PWR_ISOLATE, 0);646}647 648static irqreturn_t bcm54140_handle_interrupt(struct phy_device *phydev)649{650 int irq_status, irq_mask;651 652 irq_status = bcm_phy_read_rdb(phydev, BCM54140_RDB_ISR);653 if (irq_status < 0) {654 phy_error(phydev);655 return IRQ_NONE;656 }657 658 irq_mask = bcm_phy_read_rdb(phydev, BCM54140_RDB_IMR);659 if (irq_mask < 0) {660 phy_error(phydev);661 return IRQ_NONE;662 }663 irq_mask = ~irq_mask;664 665 if (!(irq_status & irq_mask))666 return IRQ_NONE;667 668 phy_trigger_machine(phydev);669 670 return IRQ_HANDLED;671}672 673static int bcm54140_ack_intr(struct phy_device *phydev)674{675 int reg;676 677 /* clear pending interrupts */678 reg = bcm_phy_read_rdb(phydev, BCM54140_RDB_ISR);679 if (reg < 0)680 return reg;681 682 return 0;683}684 685static int bcm54140_config_intr(struct phy_device *phydev)686{687 struct bcm54140_priv *priv = phydev->priv;688 static const u16 port_to_imr_bit[] = {689 BCM54140_RDB_TOP_IMR_PORT0, BCM54140_RDB_TOP_IMR_PORT1,690 BCM54140_RDB_TOP_IMR_PORT2, BCM54140_RDB_TOP_IMR_PORT3,691 };692 int reg, err;693 694 if (priv->port >= ARRAY_SIZE(port_to_imr_bit))695 return -EINVAL;696 697 reg = bcm54140_base_read_rdb(phydev, BCM54140_RDB_TOP_IMR);698 if (reg < 0)699 return reg;700 701 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {702 err = bcm54140_ack_intr(phydev);703 if (err)704 return err;705 706 reg &= ~port_to_imr_bit[priv->port];707 err = bcm54140_base_write_rdb(phydev, BCM54140_RDB_TOP_IMR, reg);708 } else {709 reg |= port_to_imr_bit[priv->port];710 err = bcm54140_base_write_rdb(phydev, BCM54140_RDB_TOP_IMR, reg);711 if (err)712 return err;713 714 err = bcm54140_ack_intr(phydev);715 }716 717 return err;718}719 720static int bcm54140_get_downshift(struct phy_device *phydev, u8 *data)721{722 int val;723 724 val = bcm_phy_read_rdb(phydev, BCM54140_RDB_C_MISC_CTRL);725 if (val < 0)726 return val;727 728 if (!(val & BCM54140_RDB_C_MISC_CTRL_WS_EN)) {729 *data = DOWNSHIFT_DEV_DISABLE;730 return 0;731 }732 733 val = bcm_phy_read_rdb(phydev, BCM54140_RDB_SPARE2);734 if (val < 0)735 return val;736 737 if (val & BCM54140_RDB_SPARE2_WS_RTRY_DIS)738 *data = 1;739 else740 *data = FIELD_GET(BCM54140_RDB_SPARE2_WS_RTRY_LIMIT, val) + 2;741 742 return 0;743}744 745static int bcm54140_set_downshift(struct phy_device *phydev, u8 cnt)746{747 u16 mask, set;748 int ret;749 750 if (cnt > BCM54140_MAX_DOWNSHIFT && cnt != DOWNSHIFT_DEV_DEFAULT_COUNT)751 return -EINVAL;752 753 if (!cnt)754 return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_MISC_CTRL,755 BCM54140_RDB_C_MISC_CTRL_WS_EN, 0);756 757 if (cnt == DOWNSHIFT_DEV_DEFAULT_COUNT)758 cnt = BCM54140_DEFAULT_DOWNSHIFT;759 760 if (cnt == 1) {761 mask = 0;762 set = BCM54140_RDB_SPARE2_WS_RTRY_DIS;763 } else {764 mask = BCM54140_RDB_SPARE2_WS_RTRY_DIS;765 mask |= BCM54140_RDB_SPARE2_WS_RTRY_LIMIT;766 set = FIELD_PREP(BCM54140_RDB_SPARE2_WS_RTRY_LIMIT, cnt - 2);767 }768 ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_SPARE2,769 mask, set);770 if (ret)771 return ret;772 773 return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_MISC_CTRL,774 0, BCM54140_RDB_C_MISC_CTRL_WS_EN);775}776 777static int bcm54140_get_edpd(struct phy_device *phydev, u16 *tx_interval)778{779 int val;780 781 val = bcm_phy_read_rdb(phydev, BCM54140_RDB_C_APWR);782 if (val < 0)783 return val;784 785 switch (FIELD_GET(BCM54140_RDB_C_APWR_APD_MODE_MASK, val)) {786 case BCM54140_RDB_C_APWR_APD_MODE_DIS:787 case BCM54140_RDB_C_APWR_APD_MODE_DIS2:788 *tx_interval = ETHTOOL_PHY_EDPD_DISABLE;789 break;790 case BCM54140_RDB_C_APWR_APD_MODE_EN:791 case BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG:792 switch (FIELD_GET(BCM54140_RDB_C_APWR_SLP_TIM_MASK, val)) {793 case BCM54140_RDB_C_APWR_SLP_TIM_2_7:794 *tx_interval = 2700;795 break;796 case BCM54140_RDB_C_APWR_SLP_TIM_5_4:797 *tx_interval = 5400;798 break;799 }800 }801 802 return 0;803}804 805static int bcm54140_set_edpd(struct phy_device *phydev, u16 tx_interval)806{807 u16 mask, set;808 809 mask = BCM54140_RDB_C_APWR_APD_MODE_MASK;810 if (tx_interval == ETHTOOL_PHY_EDPD_DISABLE)811 set = FIELD_PREP(BCM54140_RDB_C_APWR_APD_MODE_MASK,812 BCM54140_RDB_C_APWR_APD_MODE_DIS);813 else814 set = FIELD_PREP(BCM54140_RDB_C_APWR_APD_MODE_MASK,815 BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG);816 817 /* enable single pulse mode */818 set |= BCM54140_RDB_C_APWR_SINGLE_PULSE;819 820 /* set sleep timer */821 mask |= BCM54140_RDB_C_APWR_SLP_TIM_MASK;822 switch (tx_interval) {823 case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:824 case ETHTOOL_PHY_EDPD_DISABLE:825 case 2700:826 set |= BCM54140_RDB_C_APWR_SLP_TIM_2_7;827 break;828 case 5400:829 set |= BCM54140_RDB_C_APWR_SLP_TIM_5_4;830 break;831 default:832 return -EINVAL;833 }834 835 return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_APWR, mask, set);836}837 838static int bcm54140_get_tunable(struct phy_device *phydev,839 struct ethtool_tunable *tuna, void *data)840{841 switch (tuna->id) {842 case ETHTOOL_PHY_DOWNSHIFT:843 return bcm54140_get_downshift(phydev, data);844 case ETHTOOL_PHY_EDPD:845 return bcm54140_get_edpd(phydev, data);846 default:847 return -EOPNOTSUPP;848 }849}850 851static int bcm54140_set_tunable(struct phy_device *phydev,852 struct ethtool_tunable *tuna, const void *data)853{854 switch (tuna->id) {855 case ETHTOOL_PHY_DOWNSHIFT:856 return bcm54140_set_downshift(phydev, *(const u8 *)data);857 case ETHTOOL_PHY_EDPD:858 return bcm54140_set_edpd(phydev, *(const u16 *)data);859 default:860 return -EOPNOTSUPP;861 }862}863 864static struct phy_driver bcm54140_drivers[] = {865 {866 .phy_id = PHY_ID_BCM54140,867 .phy_id_mask = BCM54140_PHY_ID_MASK,868 .name = "Broadcom BCM54140",869 .flags = PHY_POLL_CABLE_TEST,870 .features = PHY_GBIT_FEATURES,871 .config_init = bcm54140_config_init,872 .handle_interrupt = bcm54140_handle_interrupt,873 .config_intr = bcm54140_config_intr,874 .probe = bcm54140_probe,875 .suspend = genphy_suspend,876 .resume = genphy_resume,877 .soft_reset = genphy_soft_reset,878 .get_tunable = bcm54140_get_tunable,879 .set_tunable = bcm54140_set_tunable,880 .cable_test_start = bcm_phy_cable_test_start_rdb,881 .cable_test_get_status = bcm_phy_cable_test_get_status_rdb,882 },883};884module_phy_driver(bcm54140_drivers);885 886static struct mdio_device_id __maybe_unused bcm54140_tbl[] = {887 { PHY_ID_BCM54140, BCM54140_PHY_ID_MASK },888 { }889};890 891MODULE_AUTHOR("Michael Walle");892MODULE_DESCRIPTION("Broadcom BCM54140 PHY driver");893MODULE_DEVICE_TABLE(mdio, bcm54140_tbl);894MODULE_LICENSE("GPL");895