brintos

brintos / linux-shallow public Read only

0
0
Text · 23.3 KiB · bd8a51e Raw
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