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