mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	net: phy: marvell: support downshift as PHY tunable
So far downshift is implemented for one small use case only and can't be controlled from userspace. So let's implement this feature properly as a PHY tunable so that it can be controlled via ethtool. More Marvell PHY's may support downshift, but I restricted it for now to the ones where I have the datasheet. Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									a8fad5459d
								
							
						
					
					
						commit
						a3bdfce7bf
					
				| @ -54,11 +54,15 @@ | |||||||
| #define MII_M1011_PHY_SCR			0x10 | #define MII_M1011_PHY_SCR			0x10 | ||||||
| #define MII_M1011_PHY_SCR_DOWNSHIFT_EN		BIT(11) | #define MII_M1011_PHY_SCR_DOWNSHIFT_EN		BIT(11) | ||||||
| #define MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT	12 | #define MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT	12 | ||||||
| #define MII_M1011_PHY_SRC_DOWNSHIFT_MASK	0x7800 | #define MII_M1011_PHY_SRC_DOWNSHIFT_MASK	GENMASK(14, 12) | ||||||
|  | #define MII_M1011_PHY_SCR_DOWNSHIFT_MAX		8 | ||||||
| #define MII_M1011_PHY_SCR_MDI			(0x0 << 5) | #define MII_M1011_PHY_SCR_MDI			(0x0 << 5) | ||||||
| #define MII_M1011_PHY_SCR_MDI_X			(0x1 << 5) | #define MII_M1011_PHY_SCR_MDI_X			(0x1 << 5) | ||||||
| #define MII_M1011_PHY_SCR_AUTO_CROSS		(0x3 << 5) | #define MII_M1011_PHY_SCR_AUTO_CROSS		(0x3 << 5) | ||||||
| 
 | 
 | ||||||
|  | #define MII_M1011_PHY_SSR			0x11 | ||||||
|  | #define MII_M1011_PHY_SSR_DOWNSHIFT		BIT(5) | ||||||
|  | 
 | ||||||
| #define MII_M1111_PHY_LED_CONTROL	0x18 | #define MII_M1111_PHY_LED_CONTROL	0x18 | ||||||
| #define MII_M1111_PHY_LED_DIRECT	0x4100 | #define MII_M1111_PHY_LED_DIRECT	0x4100 | ||||||
| #define MII_M1111_PHY_LED_COMBINE	0x411c | #define MII_M1111_PHY_LED_COMBINE	0x411c | ||||||
| @ -833,6 +837,79 @@ static int m88e1111_config_init(struct phy_device *phydev) | |||||||
| 	return genphy_soft_reset(phydev); | 	return genphy_soft_reset(phydev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int m88e1111_get_downshift(struct phy_device *phydev, u8 *data) | ||||||
|  | { | ||||||
|  | 	int val, cnt, enable; | ||||||
|  | 
 | ||||||
|  | 	val = phy_read(phydev, MII_M1011_PHY_SCR); | ||||||
|  | 	if (val < 0) | ||||||
|  | 		return val; | ||||||
|  | 
 | ||||||
|  | 	enable = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_EN, val); | ||||||
|  | 	cnt = FIELD_GET(MII_M1011_PHY_SRC_DOWNSHIFT_MASK, val) + 1; | ||||||
|  | 
 | ||||||
|  | 	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int m88e1111_set_downshift(struct phy_device *phydev, u8 cnt) | ||||||
|  | { | ||||||
|  | 	int val; | ||||||
|  | 
 | ||||||
|  | 	if (cnt > MII_M1011_PHY_SCR_DOWNSHIFT_MAX) | ||||||
|  | 		return -E2BIG; | ||||||
|  | 
 | ||||||
|  | 	if (!cnt) | ||||||
|  | 		return phy_clear_bits(phydev, MII_M1011_PHY_SCR, | ||||||
|  | 				      MII_M1011_PHY_SCR_DOWNSHIFT_EN); | ||||||
|  | 
 | ||||||
|  | 	val = MII_M1011_PHY_SCR_DOWNSHIFT_EN; | ||||||
|  | 	val |= FIELD_PREP(MII_M1011_PHY_SRC_DOWNSHIFT_MASK, cnt - 1); | ||||||
|  | 
 | ||||||
|  | 	return phy_modify(phydev, MII_M1011_PHY_SCR, | ||||||
|  | 			  MII_M1011_PHY_SCR_DOWNSHIFT_EN | | ||||||
|  | 			  MII_M1011_PHY_SRC_DOWNSHIFT_MASK, | ||||||
|  | 			  val); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int m88e1111_get_tunable(struct phy_device *phydev, | ||||||
|  | 				struct ethtool_tunable *tuna, void *data) | ||||||
|  | { | ||||||
|  | 	switch (tuna->id) { | ||||||
|  | 	case ETHTOOL_PHY_DOWNSHIFT: | ||||||
|  | 		return m88e1111_get_downshift(phydev, data); | ||||||
|  | 	default: | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int m88e1111_set_tunable(struct phy_device *phydev, | ||||||
|  | 				struct ethtool_tunable *tuna, const void *data) | ||||||
|  | { | ||||||
|  | 	switch (tuna->id) { | ||||||
|  | 	case ETHTOOL_PHY_DOWNSHIFT: | ||||||
|  | 		return m88e1111_set_downshift(phydev, *(const u8 *)data); | ||||||
|  | 	default: | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void m88e1111_link_change_notify(struct phy_device *phydev) | ||||||
|  | { | ||||||
|  | 	int status; | ||||||
|  | 
 | ||||||
|  | 	if (phydev->state != PHY_RUNNING) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	/* we may be on fiber page currently */ | ||||||
|  | 	status = phy_read_paged(phydev, MII_MARVELL_COPPER_PAGE, | ||||||
|  | 				MII_M1011_PHY_SSR); | ||||||
|  | 
 | ||||||
|  | 	if (status > 0 && status & MII_M1011_PHY_SSR_DOWNSHIFT) | ||||||
|  | 		phydev_warn(phydev, "Downshift occurred! Cabling may be defective.\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int m88e1318_config_init(struct phy_device *phydev) | static int m88e1318_config_init(struct phy_device *phydev) | ||||||
| { | { | ||||||
| 	if (phy_interrupt_is_valid(phydev)) { | 	if (phy_interrupt_is_valid(phydev)) { | ||||||
| @ -1117,6 +1194,8 @@ static int m88e1540_get_tunable(struct phy_device *phydev, | |||||||
| 	switch (tuna->id) { | 	switch (tuna->id) { | ||||||
| 	case ETHTOOL_PHY_FAST_LINK_DOWN: | 	case ETHTOOL_PHY_FAST_LINK_DOWN: | ||||||
| 		return m88e1540_get_fld(phydev, data); | 		return m88e1540_get_fld(phydev, data); | ||||||
|  | 	case ETHTOOL_PHY_DOWNSHIFT: | ||||||
|  | 		return m88e1111_get_downshift(phydev, data); | ||||||
| 	default: | 	default: | ||||||
| 		return -EOPNOTSUPP; | 		return -EOPNOTSUPP; | ||||||
| 	} | 	} | ||||||
| @ -1128,6 +1207,8 @@ static int m88e1540_set_tunable(struct phy_device *phydev, | |||||||
| 	switch (tuna->id) { | 	switch (tuna->id) { | ||||||
| 	case ETHTOOL_PHY_FAST_LINK_DOWN: | 	case ETHTOOL_PHY_FAST_LINK_DOWN: | ||||||
| 		return m88e1540_set_fld(phydev, data); | 		return m88e1540_set_fld(phydev, data); | ||||||
|  | 	case ETHTOOL_PHY_DOWNSHIFT: | ||||||
|  | 		return m88e1111_set_downshift(phydev, *(const u8 *)data); | ||||||
| 	default: | 	default: | ||||||
| 		return -EOPNOTSUPP; | 		return -EOPNOTSUPP; | ||||||
| 	} | 	} | ||||||
| @ -2220,6 +2301,9 @@ static struct phy_driver marvell_drivers[] = { | |||||||
| 		.get_sset_count = marvell_get_sset_count, | 		.get_sset_count = marvell_get_sset_count, | ||||||
| 		.get_strings = marvell_get_strings, | 		.get_strings = marvell_get_strings, | ||||||
| 		.get_stats = marvell_get_stats, | 		.get_stats = marvell_get_stats, | ||||||
|  | 		.get_tunable = m88e1111_get_tunable, | ||||||
|  | 		.set_tunable = m88e1111_set_tunable, | ||||||
|  | 		.link_change_notify = m88e1111_link_change_notify, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		.phy_id = MARVELL_PHY_ID_88E1318S, | 		.phy_id = MARVELL_PHY_ID_88E1318S, | ||||||
| @ -2359,6 +2443,7 @@ static struct phy_driver marvell_drivers[] = { | |||||||
| 		.get_stats = marvell_get_stats, | 		.get_stats = marvell_get_stats, | ||||||
| 		.get_tunable = m88e1540_get_tunable, | 		.get_tunable = m88e1540_get_tunable, | ||||||
| 		.set_tunable = m88e1540_set_tunable, | 		.set_tunable = m88e1540_set_tunable, | ||||||
|  | 		.link_change_notify = m88e1111_link_change_notify, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		.phy_id = MARVELL_PHY_ID_88E1545, | 		.phy_id = MARVELL_PHY_ID_88E1545, | ||||||
| @ -2421,6 +2506,7 @@ static struct phy_driver marvell_drivers[] = { | |||||||
| 		.get_stats = marvell_get_stats, | 		.get_stats = marvell_get_stats, | ||||||
| 		.get_tunable = m88e1540_get_tunable, | 		.get_tunable = m88e1540_get_tunable, | ||||||
| 		.set_tunable = m88e1540_set_tunable, | 		.set_tunable = m88e1540_set_tunable, | ||||||
|  | 		.link_change_notify = m88e1111_link_change_notify, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Heiner Kallweit
						Heiner Kallweit