842 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2023 Sartura Ltd.4 *5 * Author: Robert Marko <robert.marko@sartura.hr>6 * Christian Marangi <ansuelsmth@gmail.com>7 *8 * Qualcomm QCA8072 and QCA8075 PHY driver9 */10 11#include <linux/module.h>12#include <linux/of.h>13#include <linux/phy.h>14#include <linux/bitfield.h>15#include <linux/gpio/driver.h>16#include <linux/sfp.h>17 18#include "qcom.h"19 20#define QCA807X_CHIP_CONFIGURATION 0x1f21#define QCA807X_BT_BX_REG_SEL BIT(15)22#define QCA807X_BT_BX_REG_SEL_FIBER 023#define QCA807X_BT_BX_REG_SEL_COPPER 124#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0)25#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 426#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 327#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 028 29#define QCA807X_MEDIA_SELECT_STATUS 0x1a30#define QCA807X_MEDIA_DETECTED_COPPER BIT(5)31#define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4)32#define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3)33 34#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e35#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0)36 37#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a38#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)39/* List of tweaks enabled by this bit:40 * - With both FULL amplitude and FULL bias current: bias current41 * is set to half.42 * - With only DSP amplitude: bias current is set to half and43 * is set to 1/4 with cable < 10m.44 * - With DSP bias current (included both DSP amplitude and45 * DSP bias current): bias current is half the detected current46 * with cable < 10m.47 */48#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2)49#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1)50#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0)51 52#define QCA807X_MMD7_LED_100N_1 0x807453#define QCA807X_MMD7_LED_100N_2 0x807554#define QCA807X_MMD7_LED_1000N_1 0x807655#define QCA807X_MMD7_LED_1000N_2 0x807756 57#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2))58#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2))59 60/* LED hw control pattern for fiber port */61#define QCA807X_LED_FIBER_PATTERN_MASK GENMASK(11, 1)62#define QCA807X_LED_FIBER_TXACT_BLK_EN BIT(10)63#define QCA807X_LED_FIBER_RXACT_BLK_EN BIT(9)64#define QCA807X_LED_FIBER_FDX_ON_EN BIT(6)65#define QCA807X_LED_FIBER_HDX_ON_EN BIT(5)66#define QCA807X_LED_FIBER_1000BX_ON_EN BIT(2)67#define QCA807X_LED_FIBER_100FX_ON_EN BIT(1)68 69/* Some device repurpose the LED as GPIO out */70#define QCA807X_GPIO_FORCE_EN QCA808X_LED_FORCE_EN71#define QCA807X_GPIO_FORCE_MODE_MASK QCA808X_LED_FORCE_MODE_MASK72 73#define QCA807X_FUNCTION_CONTROL 0x1074#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)75#define QCA807X_FC_MDI_CROSSOVER_AUTO 376#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 177#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 078 79/* PQSGMII Analog PHY specific */80#define PQSGMII_CTRL_REG 0x081#define PQSGMII_ANALOG_SW_RESET BIT(6)82#define PQSGMII_DRIVE_CONTROL_1 0xb83#define PQSGMII_TX_DRIVER_MASK GENMASK(7, 4)84#define PQSGMII_TX_DRIVER_140MV 0x085#define PQSGMII_TX_DRIVER_160MV 0x186#define PQSGMII_TX_DRIVER_180MV 0x287#define PQSGMII_TX_DRIVER_200MV 0x388#define PQSGMII_TX_DRIVER_220MV 0x489#define PQSGMII_TX_DRIVER_240MV 0x590#define PQSGMII_TX_DRIVER_260MV 0x691#define PQSGMII_TX_DRIVER_280MV 0x792#define PQSGMII_TX_DRIVER_300MV 0x893#define PQSGMII_TX_DRIVER_320MV 0x994#define PQSGMII_TX_DRIVER_400MV 0xa95#define PQSGMII_TX_DRIVER_500MV 0xb96#define PQSGMII_TX_DRIVER_600MV 0xc97#define PQSGMII_MODE_CTRL 0x6d98#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0)99#define PQSGMII_MMD3_SERDES_CONTROL 0x805a100 101#define PHY_ID_QCA8072 0x004dd0b2102#define PHY_ID_QCA8075 0x004dd0b1103 104#define QCA807X_COMBO_ADDR_OFFSET 4105#define QCA807X_PQSGMII_ADDR_OFFSET 5106#define SERDES_RESET_SLEEP 100107 108enum qca807x_global_phy {109 QCA807X_COMBO_ADDR = 4,110 QCA807X_PQSGMII_ADDR = 5,111};112 113struct qca807x_shared_priv {114 unsigned int package_mode;115 u32 tx_drive_strength;116};117 118struct qca807x_gpio_priv {119 struct phy_device *phy;120};121 122struct qca807x_priv {123 bool dac_full_amplitude;124 bool dac_full_bias_current;125 bool dac_disable_bias_current_tweak;126};127 128static int qca807x_cable_test_start(struct phy_device *phydev)129{130 /* we do all the (time consuming) work later */131 return 0;132}133 134static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,135 u16 *offload_trigger)136{137 /* Parsing specific to netdev trigger */138 switch (phydev->port) {139 case PORT_TP:140 if (test_bit(TRIGGER_NETDEV_TX, &rules))141 *offload_trigger |= QCA808X_LED_TX_BLINK;142 if (test_bit(TRIGGER_NETDEV_RX, &rules))143 *offload_trigger |= QCA808X_LED_RX_BLINK;144 if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))145 *offload_trigger |= QCA808X_LED_SPEED10_ON;146 if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))147 *offload_trigger |= QCA808X_LED_SPEED100_ON;148 if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))149 *offload_trigger |= QCA808X_LED_SPEED1000_ON;150 if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))151 *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;152 if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))153 *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;154 break;155 case PORT_FIBRE:156 if (test_bit(TRIGGER_NETDEV_TX, &rules))157 *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN;158 if (test_bit(TRIGGER_NETDEV_RX, &rules))159 *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN;160 if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))161 *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN;162 if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))163 *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN;164 if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))165 *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN;166 if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))167 *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN;168 break;169 default:170 return -EOPNOTSUPP;171 }172 173 if (rules && !*offload_trigger)174 return -EOPNOTSUPP;175 176 return 0;177}178 179static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index)180{181 u16 reg;182 183 if (index > 1)184 return -EINVAL;185 186 reg = QCA807X_MMD7_LED_FORCE_CTRL(index);187 return qca808x_led_reg_hw_control_enable(phydev, reg);188}189 190static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index,191 unsigned long rules)192{193 u16 offload_trigger = 0;194 195 if (index > 1)196 return -EINVAL;197 198 return qca807x_led_parse_netdev(phydev, rules, &offload_trigger);199}200 201static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index,202 unsigned long rules)203{204 u16 reg, mask, offload_trigger = 0;205 int ret;206 207 if (index > 1)208 return -EINVAL;209 210 ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger);211 if (ret)212 return ret;213 214 ret = qca807x_led_hw_control_enable(phydev, index);215 if (ret)216 return ret;217 218 switch (phydev->port) {219 case PORT_TP:220 reg = QCA807X_MMD7_LED_CTRL(index);221 mask = QCA808X_LED_PATTERN_MASK;222 break;223 case PORT_FIBRE:224 /* HW control pattern bits are in LED FORCE reg */225 reg = QCA807X_MMD7_LED_FORCE_CTRL(index);226 mask = QCA807X_LED_FIBER_PATTERN_MASK;227 break;228 default:229 return -EINVAL;230 }231 232 return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask,233 offload_trigger);234}235 236static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index)237{238 u16 reg;239 240 if (index > 1)241 return false;242 243 reg = QCA807X_MMD7_LED_FORCE_CTRL(index);244 return qca808x_led_reg_hw_control_status(phydev, reg);245}246 247static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index,248 unsigned long *rules)249{250 u16 reg;251 int val;252 253 if (index > 1)254 return -EINVAL;255 256 /* Check if we have hw control enabled */257 if (qca807x_led_hw_control_status(phydev, index))258 return -EINVAL;259 260 /* Parsing specific to netdev trigger */261 switch (phydev->port) {262 case PORT_TP:263 reg = QCA807X_MMD7_LED_CTRL(index);264 val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);265 if (val & QCA808X_LED_TX_BLINK)266 set_bit(TRIGGER_NETDEV_TX, rules);267 if (val & QCA808X_LED_RX_BLINK)268 set_bit(TRIGGER_NETDEV_RX, rules);269 if (val & QCA808X_LED_SPEED10_ON)270 set_bit(TRIGGER_NETDEV_LINK_10, rules);271 if (val & QCA808X_LED_SPEED100_ON)272 set_bit(TRIGGER_NETDEV_LINK_100, rules);273 if (val & QCA808X_LED_SPEED1000_ON)274 set_bit(TRIGGER_NETDEV_LINK_1000, rules);275 if (val & QCA808X_LED_HALF_DUPLEX_ON)276 set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);277 if (val & QCA808X_LED_FULL_DUPLEX_ON)278 set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);279 break;280 case PORT_FIBRE:281 /* HW control pattern bits are in LED FORCE reg */282 reg = QCA807X_MMD7_LED_FORCE_CTRL(index);283 val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);284 if (val & QCA807X_LED_FIBER_TXACT_BLK_EN)285 set_bit(TRIGGER_NETDEV_TX, rules);286 if (val & QCA807X_LED_FIBER_RXACT_BLK_EN)287 set_bit(TRIGGER_NETDEV_RX, rules);288 if (val & QCA807X_LED_FIBER_100FX_ON_EN)289 set_bit(TRIGGER_NETDEV_LINK_100, rules);290 if (val & QCA807X_LED_FIBER_1000BX_ON_EN)291 set_bit(TRIGGER_NETDEV_LINK_1000, rules);292 if (val & QCA807X_LED_FIBER_HDX_ON_EN)293 set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);294 if (val & QCA807X_LED_FIBER_FDX_ON_EN)295 set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);296 break;297 default:298 return -EINVAL;299 }300 301 return 0;302}303 304static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index)305{306 u16 reg, mask;307 308 if (index > 1)309 return -EINVAL;310 311 switch (phydev->port) {312 case PORT_TP:313 reg = QCA807X_MMD7_LED_CTRL(index);314 mask = QCA808X_LED_PATTERN_MASK;315 break;316 case PORT_FIBRE:317 /* HW control pattern bits are in LED FORCE reg */318 reg = QCA807X_MMD7_LED_FORCE_CTRL(index);319 mask = QCA807X_LED_FIBER_PATTERN_MASK;320 break;321 default:322 return -EINVAL;323 }324 325 return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask);326}327 328static int qca807x_led_brightness_set(struct phy_device *phydev,329 u8 index, enum led_brightness value)330{331 u16 reg;332 int ret;333 334 if (index > 1)335 return -EINVAL;336 337 /* If we are setting off the LED reset any hw control rule */338 if (!value) {339 ret = qca807x_led_hw_control_reset(phydev, index);340 if (ret)341 return ret;342 }343 344 reg = QCA807X_MMD7_LED_FORCE_CTRL(index);345 return qca808x_led_reg_brightness_set(phydev, reg, value);346}347 348static int qca807x_led_blink_set(struct phy_device *phydev, u8 index,349 unsigned long *delay_on,350 unsigned long *delay_off)351{352 u16 reg;353 354 if (index > 1)355 return -EINVAL;356 357 reg = QCA807X_MMD7_LED_FORCE_CTRL(index);358 return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);359}360 361#ifdef CONFIG_GPIOLIB362static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)363{364 return GPIO_LINE_DIRECTION_OUT;365}366 367static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)368{369 struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);370 u16 reg;371 int val;372 373 reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);374 val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);375 376 return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);377}378 379static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)380{381 struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);382 u16 reg;383 int val;384 385 reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);386 387 val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);388 val &= ~QCA807X_GPIO_FORCE_MODE_MASK;389 val |= QCA807X_GPIO_FORCE_EN;390 val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);391 392 phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);393}394 395static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)396{397 qca807x_gpio_set(gc, offset, value);398 399 return 0;400}401 402static int qca807x_gpio(struct phy_device *phydev)403{404 struct device *dev = &phydev->mdio.dev;405 struct qca807x_gpio_priv *priv;406 struct gpio_chip *gc;407 408 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);409 if (!priv)410 return -ENOMEM;411 412 priv->phy = phydev;413 414 gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);415 if (!gc)416 return -ENOMEM;417 418 gc->label = dev_name(dev);419 gc->base = -1;420 gc->ngpio = 2;421 gc->parent = dev;422 gc->owner = THIS_MODULE;423 gc->can_sleep = true;424 gc->get_direction = qca807x_gpio_get_direction;425 gc->direction_output = qca807x_gpio_dir_out;426 gc->get = qca807x_gpio_get;427 gc->set = qca807x_gpio_set;428 429 return devm_gpiochip_add_data(dev, gc, priv);430}431#endif432 433static int qca807x_read_fiber_status(struct phy_device *phydev)434{435 bool changed;436 int ss, err;437 438 err = genphy_c37_read_status(phydev, &changed);439 if (err || !changed)440 return err;441 442 /* Read the QCA807x PHY-Specific Status register fiber page,443 * which indicates the speed and duplex that the PHY is actually444 * using, irrespective of whether we are in autoneg mode or not.445 */446 ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);447 if (ss < 0)448 return ss;449 450 phydev->speed = SPEED_UNKNOWN;451 phydev->duplex = DUPLEX_UNKNOWN;452 if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {453 switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) {454 case AT803X_SS_SPEED_100:455 phydev->speed = SPEED_100;456 break;457 case AT803X_SS_SPEED_1000:458 phydev->speed = SPEED_1000;459 break;460 }461 462 if (ss & AT803X_SS_DUPLEX)463 phydev->duplex = DUPLEX_FULL;464 else465 phydev->duplex = DUPLEX_HALF;466 }467 468 return 0;469}470 471static int qca807x_read_status(struct phy_device *phydev)472{473 if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {474 switch (phydev->port) {475 case PORT_FIBRE:476 return qca807x_read_fiber_status(phydev);477 case PORT_TP:478 return at803x_read_status(phydev);479 default:480 return -EINVAL;481 }482 }483 484 return at803x_read_status(phydev);485}486 487static int qca807x_phy_package_probe_once(struct phy_device *phydev)488{489 struct phy_package_shared *shared = phydev->shared;490 struct qca807x_shared_priv *priv = shared->priv;491 unsigned int tx_drive_strength;492 const char *package_mode_name;493 494 /* Default to 600mw if not defined */495 if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt",496 &tx_drive_strength))497 tx_drive_strength = 600;498 499 switch (tx_drive_strength) {500 case 140:501 priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV;502 break;503 case 160:504 priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV;505 break;506 case 180:507 priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV;508 break;509 case 200:510 priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV;511 break;512 case 220:513 priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV;514 break;515 case 240:516 priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV;517 break;518 case 260:519 priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV;520 break;521 case 280:522 priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV;523 break;524 case 300:525 priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV;526 break;527 case 320:528 priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV;529 break;530 case 400:531 priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV;532 break;533 case 500:534 priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV;535 break;536 case 600:537 priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV;538 break;539 default:540 return -EINVAL;541 }542 543 priv->package_mode = PHY_INTERFACE_MODE_NA;544 if (!of_property_read_string(shared->np, "qcom,package-mode",545 &package_mode_name)) {546 if (!strcasecmp(package_mode_name,547 phy_modes(PHY_INTERFACE_MODE_PSGMII)))548 priv->package_mode = PHY_INTERFACE_MODE_PSGMII;549 else if (!strcasecmp(package_mode_name,550 phy_modes(PHY_INTERFACE_MODE_QSGMII)))551 priv->package_mode = PHY_INTERFACE_MODE_QSGMII;552 else553 return -EINVAL;554 }555 556 return 0;557}558 559static int qca807x_phy_package_config_init_once(struct phy_device *phydev)560{561 struct phy_package_shared *shared = phydev->shared;562 struct qca807x_shared_priv *priv = shared->priv;563 int val, ret;564 565 /* Make sure PHY follow PHY package mode if enforced */566 if (priv->package_mode != PHY_INTERFACE_MODE_NA &&567 phydev->interface != priv->package_mode)568 return -EINVAL;569 570 phy_lock_mdio_bus(phydev);571 572 /* Set correct PHY package mode */573 val = __phy_package_read(phydev, QCA807X_COMBO_ADDR,574 QCA807X_CHIP_CONFIGURATION);575 val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK;576 /* package_mode can be QSGMII or PSGMII and we validate577 * this in probe_once.578 * With package_mode to NA, we default to PSGMII.579 */580 switch (priv->package_mode) {581 case PHY_INTERFACE_MODE_QSGMII:582 val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII;583 break;584 case PHY_INTERFACE_MODE_PSGMII:585 default:586 val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER;587 }588 ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR,589 QCA807X_CHIP_CONFIGURATION, val);590 if (ret)591 goto exit;592 593 /* After mode change Serdes reset is required */594 val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,595 PQSGMII_CTRL_REG);596 val &= ~PQSGMII_ANALOG_SW_RESET;597 ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,598 PQSGMII_CTRL_REG, val);599 if (ret)600 goto exit;601 602 msleep(SERDES_RESET_SLEEP);603 604 val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,605 PQSGMII_CTRL_REG);606 val |= PQSGMII_ANALOG_SW_RESET;607 ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,608 PQSGMII_CTRL_REG, val);609 if (ret)610 goto exit;611 612 /* Workaround to enable AZ transmitting ability */613 val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR,614 MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL);615 val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK;616 ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR,617 MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val);618 if (ret)619 goto exit;620 621 /* Set PQSGMII TX AMP strength */622 val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,623 PQSGMII_DRIVE_CONTROL_1);624 val &= ~PQSGMII_TX_DRIVER_MASK;625 val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength);626 ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,627 PQSGMII_DRIVE_CONTROL_1, val);628 if (ret)629 goto exit;630 631 /* Prevent PSGMII going into hibernation via PSGMII self test */632 val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR,633 MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL);634 val &= ~BIT(1);635 ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR,636 MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val);637 638exit:639 phy_unlock_mdio_bus(phydev);640 641 return ret;642}643 644static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)645{646 struct phy_device *phydev = upstream;647 __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };648 phy_interface_t iface;649 int ret;650 DECLARE_PHY_INTERFACE_MASK(interfaces);651 652 sfp_parse_support(phydev->sfp_bus, id, support, interfaces);653 iface = sfp_select_interface(phydev->sfp_bus, support);654 655 dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));656 657 switch (iface) {658 case PHY_INTERFACE_MODE_1000BASEX:659 case PHY_INTERFACE_MODE_100BASEX:660 /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */661 ret = phy_modify(phydev,662 QCA807X_CHIP_CONFIGURATION,663 QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,664 QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);665 /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */666 ret = phy_set_bits_mmd(phydev,667 MDIO_MMD_AN,668 QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,669 QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);670 /* Select fiber page */671 ret = phy_clear_bits(phydev,672 QCA807X_CHIP_CONFIGURATION,673 QCA807X_BT_BX_REG_SEL);674 675 phydev->port = PORT_FIBRE;676 break;677 default:678 dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");679 return -EINVAL;680 }681 682 return ret;683}684 685static void qca807x_sfp_remove(void *upstream)686{687 struct phy_device *phydev = upstream;688 689 /* Select copper page */690 phy_set_bits(phydev,691 QCA807X_CHIP_CONFIGURATION,692 QCA807X_BT_BX_REG_SEL);693 694 phydev->port = PORT_TP;695}696 697static const struct sfp_upstream_ops qca807x_sfp_ops = {698 .attach = phy_sfp_attach,699 .detach = phy_sfp_detach,700 .module_insert = qca807x_sfp_insert,701 .module_remove = qca807x_sfp_remove,702 .connect_phy = phy_sfp_connect_phy,703 .disconnect_phy = phy_sfp_disconnect_phy,704};705 706static int qca807x_probe(struct phy_device *phydev)707{708 struct device_node *node = phydev->mdio.dev.of_node;709 struct qca807x_shared_priv *shared_priv;710 struct device *dev = &phydev->mdio.dev;711 struct phy_package_shared *shared;712 struct qca807x_priv *priv;713 int ret;714 715 ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));716 if (ret)717 return ret;718 719 if (phy_package_probe_once(phydev)) {720 ret = qca807x_phy_package_probe_once(phydev);721 if (ret)722 return ret;723 }724 725 shared = phydev->shared;726 shared_priv = shared->priv;727 728 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);729 if (!priv)730 return -ENOMEM;731 732 priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude");733 priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current");734 priv->dac_disable_bias_current_tweak = of_property_read_bool(node,735 "qcom,dac-disable-bias-current-tweak");736 737#if IS_ENABLED(CONFIG_GPIOLIB)738 /* Do not register a GPIO controller unless flagged for it */739 if (of_property_read_bool(node, "gpio-controller")) {740 ret = qca807x_gpio(phydev);741 if (ret)742 return ret;743 }744#endif745 746 /* Attach SFP bus on combo port*/747 if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {748 ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);749 if (ret)750 return ret;751 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);752 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);753 }754 755 phydev->priv = priv;756 757 return 0;758}759 760static int qca807x_config_init(struct phy_device *phydev)761{762 struct qca807x_priv *priv = phydev->priv;763 u16 control_dac;764 int ret;765 766 if (phy_package_init_once(phydev)) {767 ret = qca807x_phy_package_config_init_once(phydev);768 if (ret)769 return ret;770 }771 772 control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,773 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);774 control_dac &= ~QCA807X_CONTROL_DAC_MASK;775 if (!priv->dac_full_amplitude)776 control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE;777 if (!priv->dac_full_amplitude)778 control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT;779 if (!priv->dac_disable_bias_current_tweak)780 control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK;781 return phy_write_mmd(phydev, MDIO_MMD_AN,782 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH,783 control_dac);784}785 786static struct phy_driver qca807x_drivers[] = {787 {788 PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),789 .name = "Qualcomm QCA8072",790 .flags = PHY_POLL_CABLE_TEST,791 /* PHY_GBIT_FEATURES */792 .probe = qca807x_probe,793 .config_init = qca807x_config_init,794 .read_status = qca807x_read_status,795 .config_intr = at803x_config_intr,796 .handle_interrupt = at803x_handle_interrupt,797 .soft_reset = genphy_soft_reset,798 .get_tunable = at803x_get_tunable,799 .set_tunable = at803x_set_tunable,800 .resume = genphy_resume,801 .suspend = genphy_suspend,802 .cable_test_start = qca807x_cable_test_start,803 .cable_test_get_status = qca808x_cable_test_get_status,804 },805 {806 PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),807 .name = "Qualcomm QCA8075",808 .flags = PHY_POLL_CABLE_TEST,809 /* PHY_GBIT_FEATURES */810 .probe = qca807x_probe,811 .config_init = qca807x_config_init,812 .read_status = qca807x_read_status,813 .config_intr = at803x_config_intr,814 .handle_interrupt = at803x_handle_interrupt,815 .soft_reset = genphy_soft_reset,816 .get_tunable = at803x_get_tunable,817 .set_tunable = at803x_set_tunable,818 .resume = genphy_resume,819 .suspend = genphy_suspend,820 .cable_test_start = qca807x_cable_test_start,821 .cable_test_get_status = qca808x_cable_test_get_status,822 .led_brightness_set = qca807x_led_brightness_set,823 .led_blink_set = qca807x_led_blink_set,824 .led_hw_is_supported = qca807x_led_hw_is_supported,825 .led_hw_control_set = qca807x_led_hw_control_set,826 .led_hw_control_get = qca807x_led_hw_control_get,827 },828};829module_phy_driver(qca807x_drivers);830 831static struct mdio_device_id __maybe_unused qca807x_tbl[] = {832 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) },833 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) },834 { }835};836 837MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");838MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");839MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");840MODULE_DEVICE_TABLE(mdio, qca807x_tbl);841MODULE_LICENSE("GPL");842