664 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3#include <linux/phy.h>4#include <linux/module.h>5 6#include "qcom.h"7 8/* ADC threshold */9#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c8010#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0)11#define QCA808X_ADC_THRESHOLD_80MV 012#define QCA808X_ADC_THRESHOLD_100MV 0xf013#define QCA808X_ADC_THRESHOLD_200MV 0x0f14#define QCA808X_ADC_THRESHOLD_300MV 0xff15 16/* CLD control */17#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x800718#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4)19#define QCA808X_8023AZ_AFE_EN 0x9020 21/* AZ control */22#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x800823#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c3224 25#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x801426#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x52927 28#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E29#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x34130 31#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E32#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x41933 34#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x802035#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x34136 37#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c38#define QCA808X_TOP_OPTION1_DATA 0x039 40#define QCA808X_PHY_MMD3_DEBUG_1 0xa10041#define QCA808X_MMD3_DEBUG_1_VALUE 0x920342#define QCA808X_PHY_MMD3_DEBUG_2 0xa10143#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad44#define QCA808X_PHY_MMD3_DEBUG_3 0xa10345#define QCA808X_MMD3_DEBUG_3_VALUE 0x169846#define QCA808X_PHY_MMD3_DEBUG_4 0xa10547#define QCA808X_MMD3_DEBUG_4_VALUE 0x800148#define QCA808X_PHY_MMD3_DEBUG_5 0xa10649#define QCA808X_MMD3_DEBUG_5_VALUE 0x111150#define QCA808X_PHY_MMD3_DEBUG_6 0xa01151#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f8552 53/* master/slave seed config */54#define QCA808X_PHY_DEBUG_LOCAL_SEED 955#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1)56#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2)57#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x3258 59/* Hibernation yields lower power consumpiton in contrast with normal operation mode.60 * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.61 */62#define QCA808X_DBG_AN_TEST 0xb63#define QCA808X_HIBERNATION_EN BIT(15)64 65#define QCA808X_MMD7_LED2_CTRL 0x807466#define QCA808X_MMD7_LED2_FORCE_CTRL 0x807567#define QCA808X_MMD7_LED1_CTRL 0x807668#define QCA808X_MMD7_LED1_FORCE_CTRL 0x807769#define QCA808X_MMD7_LED0_CTRL 0x807870#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))71 72#define QCA808X_MMD7_LED0_FORCE_CTRL 0x807973#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))74 75#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a76/* QSDK sets by default 0x46 to this reg that sets BIT 6 for77 * LED to active high. It's not clear what BIT 3 and BIT 4 does.78 */79#define QCA808X_LED_ACTIVE_HIGH BIT(6)80 81/* QCA808X 1G chip type */82#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d83#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)84 85#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x907286#define QCA8081_PHY_FIFO_RSTN BIT(11)87 88#define QCA8081_PHY_ID 0x004dd10189 90MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");91MODULE_AUTHOR("Matus Ujhelyi");92MODULE_LICENSE("GPL");93 94struct qca808x_priv {95 int led_polarity_mode;96};97 98static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)99{100 int ret;101 102 /* Enable fast retrain */103 ret = genphy_c45_fast_retrain(phydev, true);104 if (ret)105 return ret;106 107 phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,108 QCA808X_TOP_OPTION1_DATA);109 phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,110 QCA808X_MSE_THRESHOLD_20DB_VALUE);111 phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,112 QCA808X_MSE_THRESHOLD_17DB_VALUE);113 phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,114 QCA808X_MSE_THRESHOLD_27DB_VALUE);115 phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,116 QCA808X_MSE_THRESHOLD_28DB_VALUE);117 phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,118 QCA808X_MMD3_DEBUG_1_VALUE);119 phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,120 QCA808X_MMD3_DEBUG_4_VALUE);121 phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,122 QCA808X_MMD3_DEBUG_5_VALUE);123 phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,124 QCA808X_MMD3_DEBUG_3_VALUE);125 phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,126 QCA808X_MMD3_DEBUG_6_VALUE);127 phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,128 QCA808X_MMD3_DEBUG_2_VALUE);129 130 return 0;131}132 133static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)134{135 u16 seed_value;136 137 if (!enable)138 return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,139 QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);140 141 seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);142 return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,143 QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,144 FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |145 QCA808X_MASTER_SLAVE_SEED_ENABLE);146}147 148static bool qca808x_is_prefer_master(struct phy_device *phydev)149{150 return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||151 (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);152}153 154static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)155{156 return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);157}158 159static bool qca808x_is_1g_only(struct phy_device *phydev)160{161 int ret;162 163 ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);164 if (ret < 0)165 return true;166 167 return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);168}169 170static void qca808x_fill_possible_interfaces(struct phy_device *phydev)171{172 unsigned long *possible = phydev->possible_interfaces;173 174 __set_bit(PHY_INTERFACE_MODE_SGMII, possible);175 176 if (!qca808x_is_1g_only(phydev))177 __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible);178}179 180static int qca808x_probe(struct phy_device *phydev)181{182 struct device *dev = &phydev->mdio.dev;183 struct qca808x_priv *priv;184 185 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);186 if (!priv)187 return -ENOMEM;188 189 /* Init LED polarity mode to -1 */190 priv->led_polarity_mode = -1;191 192 phydev->priv = priv;193 194 return 0;195}196 197static int qca808x_config_init(struct phy_device *phydev)198{199 struct qca808x_priv *priv = phydev->priv;200 int ret;201 202 /* Default to LED Active High if active-low not in DT */203 if (priv->led_polarity_mode == -1) {204 ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN,205 QCA808X_MMD7_LED_POLARITY_CTRL,206 QCA808X_LED_ACTIVE_HIGH);207 if (ret)208 return ret;209 }210 211 /* Active adc&vga on 802.3az for the link 1000M and 100M */212 ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,213 QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);214 if (ret)215 return ret;216 217 /* Adjust the threshold on 802.3az for the link 1000M */218 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,219 QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,220 QCA808X_MMD3_AZ_TRAINING_VAL);221 if (ret)222 return ret;223 224 if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {225 /* Config the fast retrain for the link 2500M */226 ret = qca808x_phy_fast_retrain_config(phydev);227 if (ret)228 return ret;229 230 ret = genphy_read_master_slave(phydev);231 if (ret < 0)232 return ret;233 234 if (!qca808x_is_prefer_master(phydev)) {235 /* Enable seed and configure lower ramdom seed to make phy236 * linked as slave mode.237 */238 ret = qca808x_phy_ms_seed_enable(phydev, true);239 if (ret)240 return ret;241 }242 }243 244 qca808x_fill_possible_interfaces(phydev);245 246 /* Configure adc threshold as 100mv for the link 10M */247 return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,248 QCA808X_ADC_THRESHOLD_MASK,249 QCA808X_ADC_THRESHOLD_100MV);250}251 252static int qca808x_read_status(struct phy_device *phydev)253{254 struct at803x_ss_mask ss_mask = { 0 };255 int ret;256 257 ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);258 if (ret < 0)259 return ret;260 261 linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,262 ret & MDIO_AN_10GBT_STAT_LP2_5G);263 264 ret = genphy_read_status(phydev);265 if (ret)266 return ret;267 268 /* qca8081 takes the different bits for speed value from at803x */269 ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;270 ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);271 ret = at803x_read_specific_status(phydev, ss_mask);272 if (ret < 0)273 return ret;274 275 if (phydev->link) {276 if (phydev->speed == SPEED_2500)277 phydev->interface = PHY_INTERFACE_MODE_2500BASEX;278 else279 phydev->interface = PHY_INTERFACE_MODE_SGMII;280 } else {281 /* generate seed as a lower random value to make PHY linked as SLAVE easily,282 * except for master/slave configuration fault detected or the master mode283 * preferred.284 *285 * the reason for not putting this code into the function link_change_notify is286 * the corner case where the link partner is also the qca8081 PHY and the seed287 * value is configured as the same value, the link can't be up and no link change288 * occurs.289 */290 if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {291 if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||292 qca808x_is_prefer_master(phydev)) {293 qca808x_phy_ms_seed_enable(phydev, false);294 } else {295 qca808x_phy_ms_seed_enable(phydev, true);296 }297 }298 }299 300 return 0;301}302 303static int qca808x_soft_reset(struct phy_device *phydev)304{305 int ret;306 307 ret = genphy_soft_reset(phydev);308 if (ret < 0)309 return ret;310 311 if (qca808x_has_fast_retrain_or_slave_seed(phydev))312 ret = qca808x_phy_ms_seed_enable(phydev, true);313 314 return ret;315}316 317static int qca808x_cable_test_start(struct phy_device *phydev)318{319 int ret;320 321 /* perform CDT with the following configs:322 * 1. disable hibernation.323 * 2. force PHY working in MDI mode.324 * 3. for PHY working in 1000BaseT.325 * 4. configure the threshold.326 */327 328 ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);329 if (ret < 0)330 return ret;331 332 ret = at803x_config_mdix(phydev, ETH_TP_MDI);333 if (ret < 0)334 return ret;335 336 /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */337 phydev->duplex = DUPLEX_FULL;338 phydev->speed = SPEED_1000;339 ret = genphy_c45_pma_setup_forced(phydev);340 if (ret < 0)341 return ret;342 343 ret = genphy_setup_forced(phydev);344 if (ret < 0)345 return ret;346 347 /* configure the thresholds for open, short, pair ok test */348 phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);349 phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);350 phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);351 phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);352 phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);353 phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);354 355 return 0;356}357 358static int qca808x_get_features(struct phy_device *phydev)359{360 int ret;361 362 ret = genphy_c45_pma_read_abilities(phydev);363 if (ret)364 return ret;365 366 /* The autoneg ability is not existed in bit3 of MMD7.1,367 * but it is supported by qca808x PHY, so we add it here368 * manually.369 */370 linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);371 372 /* As for the qca8081 1G version chip, the 2500baseT ability is also373 * existed in the bit0 of MMD1.21, we need to remove it manually if374 * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.375 */376 if (qca808x_is_1g_only(phydev))377 linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);378 379 return 0;380}381 382static int qca808x_config_aneg(struct phy_device *phydev)383{384 int phy_ctrl = 0;385 int ret;386 387 ret = at803x_prepare_config_aneg(phydev);388 if (ret)389 return ret;390 391 /* The reg MII_BMCR also needs to be configured for force mode, the392 * genphy_config_aneg is also needed.393 */394 if (phydev->autoneg == AUTONEG_DISABLE)395 genphy_c45_pma_setup_forced(phydev);396 397 if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))398 phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;399 400 ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,401 MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);402 if (ret < 0)403 return ret;404 405 return __genphy_config_aneg(phydev, ret);406}407 408static void qca808x_link_change_notify(struct phy_device *phydev)409{410 /* Assert interface sgmii fifo on link down, deassert it on link up,411 * the interface device address is always phy address added by 1.412 */413 mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,414 MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,415 QCA8081_PHY_FIFO_RSTN,416 phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);417}418 419static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,420 u16 *offload_trigger)421{422 /* Parsing specific to netdev trigger */423 if (test_bit(TRIGGER_NETDEV_TX, &rules))424 *offload_trigger |= QCA808X_LED_TX_BLINK;425 if (test_bit(TRIGGER_NETDEV_RX, &rules))426 *offload_trigger |= QCA808X_LED_RX_BLINK;427 if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))428 *offload_trigger |= QCA808X_LED_SPEED10_ON;429 if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))430 *offload_trigger |= QCA808X_LED_SPEED100_ON;431 if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))432 *offload_trigger |= QCA808X_LED_SPEED1000_ON;433 if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))434 *offload_trigger |= QCA808X_LED_SPEED2500_ON;435 if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))436 *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;437 if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))438 *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;439 440 if (rules && !*offload_trigger)441 return -EOPNOTSUPP;442 443 /* Enable BLINK_CHECK_BYPASS by default to make the LED444 * blink even with duplex or speed mode not enabled.445 */446 *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;447 448 return 0;449}450 451static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)452{453 u16 reg;454 455 if (index > 2)456 return -EINVAL;457 458 reg = QCA808X_MMD7_LED_FORCE_CTRL(index);459 return qca808x_led_reg_hw_control_enable(phydev, reg);460}461 462static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,463 unsigned long rules)464{465 u16 offload_trigger = 0;466 467 if (index > 2)468 return -EINVAL;469 470 return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);471}472 473static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,474 unsigned long rules)475{476 u16 reg, offload_trigger = 0;477 int ret;478 479 if (index > 2)480 return -EINVAL;481 482 reg = QCA808X_MMD7_LED_CTRL(index);483 484 ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);485 if (ret)486 return ret;487 488 ret = qca808x_led_hw_control_enable(phydev, index);489 if (ret)490 return ret;491 492 return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,493 QCA808X_LED_PATTERN_MASK,494 offload_trigger);495}496 497static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)498{499 u16 reg;500 501 if (index > 2)502 return false;503 504 reg = QCA808X_MMD7_LED_FORCE_CTRL(index);505 return qca808x_led_reg_hw_control_status(phydev, reg);506}507 508static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,509 unsigned long *rules)510{511 u16 reg;512 int val;513 514 if (index > 2)515 return -EINVAL;516 517 /* Check if we have hw control enabled */518 if (qca808x_led_hw_control_status(phydev, index))519 return -EINVAL;520 521 reg = QCA808X_MMD7_LED_CTRL(index);522 523 val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);524 if (val & QCA808X_LED_TX_BLINK)525 set_bit(TRIGGER_NETDEV_TX, rules);526 if (val & QCA808X_LED_RX_BLINK)527 set_bit(TRIGGER_NETDEV_RX, rules);528 if (val & QCA808X_LED_SPEED10_ON)529 set_bit(TRIGGER_NETDEV_LINK_10, rules);530 if (val & QCA808X_LED_SPEED100_ON)531 set_bit(TRIGGER_NETDEV_LINK_100, rules);532 if (val & QCA808X_LED_SPEED1000_ON)533 set_bit(TRIGGER_NETDEV_LINK_1000, rules);534 if (val & QCA808X_LED_SPEED2500_ON)535 set_bit(TRIGGER_NETDEV_LINK_2500, rules);536 if (val & QCA808X_LED_HALF_DUPLEX_ON)537 set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);538 if (val & QCA808X_LED_FULL_DUPLEX_ON)539 set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);540 541 return 0;542}543 544static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)545{546 u16 reg;547 548 if (index > 2)549 return -EINVAL;550 551 reg = QCA808X_MMD7_LED_CTRL(index);552 553 return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,554 QCA808X_LED_PATTERN_MASK);555}556 557static int qca808x_led_brightness_set(struct phy_device *phydev,558 u8 index, enum led_brightness value)559{560 u16 reg;561 int ret;562 563 if (index > 2)564 return -EINVAL;565 566 if (!value) {567 ret = qca808x_led_hw_control_reset(phydev, index);568 if (ret)569 return ret;570 }571 572 reg = QCA808X_MMD7_LED_FORCE_CTRL(index);573 return qca808x_led_reg_brightness_set(phydev, reg, value);574}575 576static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,577 unsigned long *delay_on,578 unsigned long *delay_off)579{580 u16 reg;581 582 if (index > 2)583 return -EINVAL;584 585 reg = QCA808X_MMD7_LED_FORCE_CTRL(index);586 return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);587}588 589static int qca808x_led_polarity_set(struct phy_device *phydev, int index,590 unsigned long modes)591{592 struct qca808x_priv *priv = phydev->priv;593 bool active_low = false;594 u32 mode;595 596 for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {597 switch (mode) {598 case PHY_LED_ACTIVE_LOW:599 active_low = true;600 break;601 default:602 return -EINVAL;603 }604 }605 606 /* PHY polarity is global and can't be set per LED.607 * To detect this, check if last requested polarity mode608 * match the new one.609 */610 if (priv->led_polarity_mode >= 0 &&611 priv->led_polarity_mode != active_low) {612 phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");613 return -EINVAL;614 }615 616 /* Save the last PHY polarity mode */617 priv->led_polarity_mode = active_low;618 619 return phy_modify_mmd(phydev, MDIO_MMD_AN,620 QCA808X_MMD7_LED_POLARITY_CTRL,621 QCA808X_LED_ACTIVE_HIGH,622 active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);623}624 625static struct phy_driver qca808x_driver[] = {626{627 /* Qualcomm QCA8081 */628 PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),629 .name = "Qualcomm QCA8081",630 .flags = PHY_POLL_CABLE_TEST,631 .probe = qca808x_probe,632 .config_intr = at803x_config_intr,633 .handle_interrupt = at803x_handle_interrupt,634 .get_tunable = at803x_get_tunable,635 .set_tunable = at803x_set_tunable,636 .set_wol = at803x_set_wol,637 .get_wol = at803x_get_wol,638 .get_features = qca808x_get_features,639 .config_aneg = qca808x_config_aneg,640 .suspend = genphy_suspend,641 .resume = genphy_resume,642 .read_status = qca808x_read_status,643 .config_init = qca808x_config_init,644 .soft_reset = qca808x_soft_reset,645 .cable_test_start = qca808x_cable_test_start,646 .cable_test_get_status = qca808x_cable_test_get_status,647 .link_change_notify = qca808x_link_change_notify,648 .led_brightness_set = qca808x_led_brightness_set,649 .led_blink_set = qca808x_led_blink_set,650 .led_hw_is_supported = qca808x_led_hw_is_supported,651 .led_hw_control_set = qca808x_led_hw_control_set,652 .led_hw_control_get = qca808x_led_hw_control_get,653 .led_polarity_set = qca808x_led_polarity_set,654}, };655 656module_phy_driver(qca808x_driver);657 658static struct mdio_device_id __maybe_unused qca808x_tbl[] = {659 { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },660 { }661};662 663MODULE_DEVICE_TABLE(mdio, qca808x_tbl);664