brintos

brintos / linux-shallow public Read only

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