mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 02:15:45 +01:00 
			
		
		
		
	When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay Ethernet"' I failed to notice that b4 noticed it was based on next and so took that as the base commit and merged that part of next to master. This reverts commitc8ffd1356d, reversing changes made to2ee6f3a5f7. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			278 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /**
 | |
|  *  Driver for Analog Devices Industrial Ethernet PHYs
 | |
|  *
 | |
|  * Copyright 2019 Analog Devices Inc.
 | |
|  * Copyright 2022 Variscite Ltd.
 | |
|  * Copyright 2022 Josua Mayer <josua@solid-run.com>
 | |
|  */
 | |
| #include <common.h>
 | |
| #include <phy.h>
 | |
| #include <linux/bitops.h>
 | |
| #include <linux/bitfield.h>
 | |
| 
 | |
| #define PHY_ID_ADIN1300				0x0283bc30
 | |
| #define ADIN1300_EXT_REG_PTR			0x10
 | |
| #define ADIN1300_EXT_REG_DATA			0x11
 | |
| 
 | |
| #define ADIN1300_GE_CLK_CFG_REG			0xff1f
 | |
| #define   ADIN1300_GE_CLK_CFG_MASK		GENMASK(5, 0)
 | |
| #define   ADIN1300_GE_CLK_CFG_RCVR_125		BIT(5)
 | |
| #define   ADIN1300_GE_CLK_CFG_FREE_125		BIT(4)
 | |
| #define   ADIN1300_GE_CLK_CFG_REF_EN		BIT(3)
 | |
| #define   ADIN1300_GE_CLK_CFG_HRT_RCVR		BIT(2)
 | |
| #define   ADIN1300_GE_CLK_CFG_HRT_FREE		BIT(1)
 | |
| #define   ADIN1300_GE_CLK_CFG_25		BIT(0)
 | |
| 
 | |
| #define ADIN1300_GE_RGMII_CFG			0xff23
 | |
| #define ADIN1300_GE_RGMII_RX_MSK		GENMASK(8, 6)
 | |
| #define ADIN1300_GE_RGMII_RX_SEL(x)		\
 | |
| 		FIELD_PREP(ADIN1300_GE_RGMII_RX_MSK, x)
 | |
| #define ADIN1300_GE_RGMII_GTX_MSK		GENMASK(5, 3)
 | |
| #define ADIN1300_GE_RGMII_GTX_SEL(x)		\
 | |
| 		FIELD_PREP(ADIN1300_GE_RGMII_GTX_MSK, x)
 | |
| #define ADIN1300_GE_RGMII_RXID_EN		BIT(2)
 | |
| #define ADIN1300_GE_RGMII_TXID_EN		BIT(1)
 | |
| #define ADIN1300_GE_RGMII_EN			BIT(0)
 | |
| 
 | |
| /* RGMII internal delay settings for rx and tx for ADIN1300 */
 | |
| #define ADIN1300_RGMII_1_60_NS			0x0001
 | |
| #define ADIN1300_RGMII_1_80_NS			0x0002
 | |
| #define	ADIN1300_RGMII_2_00_NS			0x0000
 | |
| #define	ADIN1300_RGMII_2_20_NS			0x0006
 | |
| #define	ADIN1300_RGMII_2_40_NS			0x0007
 | |
| 
 | |
| /**
 | |
|  * struct adin_cfg_reg_map - map a config value to aregister value
 | |
|  * @cfg		value in device configuration
 | |
|  * @reg		value in the register
 | |
|  */
 | |
| struct adin_cfg_reg_map {
 | |
| 	int cfg;
 | |
| 	int reg;
 | |
| };
 | |
| 
 | |
| static const struct adin_cfg_reg_map adin_rgmii_delays[] = {
 | |
| 	{ 1600, ADIN1300_RGMII_1_60_NS },
 | |
| 	{ 1800, ADIN1300_RGMII_1_80_NS },
 | |
| 	{ 2000, ADIN1300_RGMII_2_00_NS },
 | |
| 	{ 2200, ADIN1300_RGMII_2_20_NS },
 | |
| 	{ 2400, ADIN1300_RGMII_2_40_NS },
 | |
| 	{ },
 | |
| };
 | |
| 
 | |
| static int adin_lookup_reg_value(const struct adin_cfg_reg_map *tbl, int cfg)
 | |
| {
 | |
| 	size_t i;
 | |
| 
 | |
| 	for (i = 0; tbl[i].cfg; i++) {
 | |
| 		if (tbl[i].cfg == cfg)
 | |
| 			return tbl[i].reg;
 | |
| 	}
 | |
| 
 | |
| 	return -EINVAL;
 | |
| }
 | |
| 
 | |
| static u32 adin_get_reg_value(struct phy_device *phydev,
 | |
| 			      const char *prop_name,
 | |
| 			      const struct adin_cfg_reg_map *tbl,
 | |
| 			      u32 dflt)
 | |
| {
 | |
| 	u32 val;
 | |
| 	int rc;
 | |
| 
 | |
| 	ofnode node = phy_get_ofnode(phydev);
 | |
| 	if (!ofnode_valid(node)) {
 | |
| 		printf("%s: failed to get node\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (ofnode_read_u32(node, prop_name, &val)) {
 | |
| 		printf("%s: failed to find %s, using default %d\n",
 | |
| 		       __func__, prop_name, dflt);
 | |
| 		return dflt;
 | |
| 	}
 | |
| 
 | |
| 	debug("%s: %s = '%d'\n", __func__, prop_name, val);
 | |
| 
 | |
| 	rc = adin_lookup_reg_value(tbl, val);
 | |
| 	if (rc < 0) {
 | |
| 		printf("%s: Unsupported value %u for %s using default (%u)\n",
 | |
| 		      __func__, val, prop_name, dflt);
 | |
| 		return dflt;
 | |
| 	}
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * adin_get_phy_mode_override - Get phy-mode override for adin PHY
 | |
|  *
 | |
|  * The function gets phy-mode string from property 'adi,phy-mode-override'
 | |
|  * and return its index in phy_interface_strings table, or -1 in error case.
 | |
|  */
 | |
| phy_interface_t adin_get_phy_mode_override(struct phy_device *phydev)
 | |
| {
 | |
| 	ofnode node = phy_get_ofnode(phydev);
 | |
| 	const char *phy_mode_override;
 | |
| 	const char *prop_phy_mode_override = "adi,phy-mode-override";
 | |
| 	int i;
 | |
| 
 | |
| 	phy_mode_override = ofnode_read_string(node, prop_phy_mode_override);
 | |
| 	if (!phy_mode_override)
 | |
| 		return PHY_INTERFACE_MODE_NA;
 | |
| 
 | |
| 	debug("%s: %s = '%s'\n",
 | |
| 	      __func__, prop_phy_mode_override, phy_mode_override);
 | |
| 
 | |
| 	for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++)
 | |
| 		if (!strcmp(phy_mode_override, phy_interface_strings[i]))
 | |
| 			return (phy_interface_t) i;
 | |
| 
 | |
| 	printf("%s: Invalid PHY interface '%s'\n", __func__, phy_mode_override);
 | |
| 
 | |
| 	return PHY_INTERFACE_MODE_NA;
 | |
| }
 | |
| 
 | |
| static u16 adin_ext_read(struct phy_device *phydev, const u32 regnum)
 | |
| {
 | |
| 	u16 val;
 | |
| 
 | |
| 	phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_PTR, regnum);
 | |
| 	val = phy_read(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_DATA);
 | |
| 
 | |
| 	debug("%s: adin@0x%x 0x%x=0x%x\n", __func__, phydev->addr, regnum, val);
 | |
| 
 | |
| 	return val;
 | |
| }
 | |
| 
 | |
| static int adin_ext_write(struct phy_device *phydev, const u32 regnum, const u16 val)
 | |
| {
 | |
| 	debug("%s: adin@0x%x 0x%x=0x%x\n", __func__, phydev->addr, regnum, val);
 | |
| 
 | |
| 	phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_PTR, regnum);
 | |
| 
 | |
| 	return phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_DATA, val);
 | |
| }
 | |
| 
 | |
| static int adin_extread(struct phy_device *phydev, int addr, int devaddr,
 | |
| 			int regnum)
 | |
| {
 | |
| 	return adin_ext_read(phydev, regnum);
 | |
| }
 | |
| 
 | |
| static int adin_extwrite(struct phy_device *phydev, int addr,
 | |
| 			 int devaddr, int regnum, u16 val)
 | |
| {
 | |
| 	return adin_ext_write(phydev, regnum, val);
 | |
| }
 | |
| 
 | |
| static int adin_config_clk_out(struct phy_device *phydev)
 | |
| {
 | |
| 	ofnode node = phy_get_ofnode(phydev);
 | |
| 	const char *val = NULL;
 | |
| 	u8 sel = 0;
 | |
| 
 | |
| 	val = ofnode_read_string(node, "adi,phy-output-clock");
 | |
| 	if (!val) {
 | |
| 		/* property not present, do not enable GP_CLK pin */
 | |
| 	} else if (strcmp(val, "25mhz-reference") == 0) {
 | |
| 		sel |= ADIN1300_GE_CLK_CFG_25;
 | |
| 	} else if (strcmp(val, "125mhz-free-running") == 0) {
 | |
| 		sel |= ADIN1300_GE_CLK_CFG_FREE_125;
 | |
| 	} else if (strcmp(val, "adaptive-free-running") == 0) {
 | |
| 		sel |= ADIN1300_GE_CLK_CFG_HRT_FREE;
 | |
| 	} else {
 | |
| 		pr_err("%s: invalid adi,phy-output-clock\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (ofnode_read_bool(node, "adi,phy-output-reference-clock"))
 | |
| 		sel |= ADIN1300_GE_CLK_CFG_REF_EN;
 | |
| 
 | |
| 	return adin_ext_write(phydev, ADIN1300_GE_CLK_CFG_REG,
 | |
| 			      ADIN1300_GE_CLK_CFG_MASK & sel);
 | |
| }
 | |
| 
 | |
| static int adin_config_rgmii_mode(struct phy_device *phydev)
 | |
| {
 | |
| 	u16 reg_val;
 | |
| 	u32 val;
 | |
| 	phy_interface_t phy_mode_override = adin_get_phy_mode_override(phydev);
 | |
| 
 | |
| 	if (phy_mode_override != PHY_INTERFACE_MODE_NA) {
 | |
| 		phydev->interface = phy_mode_override;
 | |
| 	}
 | |
| 
 | |
| 	reg_val = adin_ext_read(phydev, ADIN1300_GE_RGMII_CFG);
 | |
| 
 | |
| 	if (!phy_interface_is_rgmii(phydev)) {
 | |
| 		/* Disable RGMII */
 | |
| 		reg_val &= ~ADIN1300_GE_RGMII_EN;
 | |
| 		return adin_ext_write(phydev, ADIN1300_GE_RGMII_CFG, reg_val);
 | |
| 	}
 | |
| 
 | |
| 	/* Enable RGMII */
 | |
| 	reg_val |= ADIN1300_GE_RGMII_EN;
 | |
| 
 | |
| 	/* Enable / Disable RGMII RX Delay */
 | |
| 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
 | |
| 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
 | |
| 		reg_val |= ADIN1300_GE_RGMII_RXID_EN;
 | |
| 
 | |
| 		val = adin_get_reg_value(phydev, "adi,rx-internal-delay-ps",
 | |
| 					 adin_rgmii_delays,
 | |
| 					 ADIN1300_RGMII_2_00_NS);
 | |
| 		reg_val &= ~ADIN1300_GE_RGMII_RX_MSK;
 | |
| 		reg_val |= ADIN1300_GE_RGMII_RX_SEL(val);
 | |
| 	} else {
 | |
| 		reg_val &= ~ADIN1300_GE_RGMII_RXID_EN;
 | |
| 	}
 | |
| 
 | |
| 	/* Enable / Disable RGMII RX Delay */
 | |
| 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
 | |
| 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 | |
| 		reg_val |= ADIN1300_GE_RGMII_TXID_EN;
 | |
| 
 | |
| 		val = adin_get_reg_value(phydev, "adi,tx-internal-delay-ps",
 | |
| 					 adin_rgmii_delays,
 | |
| 					 ADIN1300_RGMII_2_00_NS);
 | |
| 		reg_val &= ~ADIN1300_GE_RGMII_GTX_MSK;
 | |
| 		reg_val |= ADIN1300_GE_RGMII_GTX_SEL(val);
 | |
| 	} else {
 | |
| 		reg_val &= ~ADIN1300_GE_RGMII_TXID_EN;
 | |
| 	}
 | |
| 
 | |
| 	return adin_ext_write(phydev, ADIN1300_GE_RGMII_CFG, reg_val);
 | |
| }
 | |
| 
 | |
| static int adin1300_config(struct phy_device *phydev)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	printf("ADIN1300 PHY detected at addr %d\n", phydev->addr);
 | |
| 
 | |
| 	ret = adin_config_clk_out(phydev);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = adin_config_rgmii_mode(phydev);
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	return genphy_config(phydev);
 | |
| }
 | |
| 
 | |
| U_BOOT_PHY_DRIVER(ADIN1300) = {
 | |
| 	.name = "ADIN1300",
 | |
| 	.uid = PHY_ID_ADIN1300,
 | |
| 	.mask = 0xffffffff,
 | |
| 	.features = PHY_GBIT_FEATURES,
 | |
| 	.config = adin1300_config,
 | |
| 	.startup = genphy_startup,
 | |
| 	.shutdown = genphy_shutdown,
 | |
| 	.readext = adin_extread,
 | |
| 	.writeext = adin_extwrite,
 | |
| };
 |