Revert "Revert "rockchip: replace local motorcomm driver with upstreamed patches""

Let's backport upstream fixes.

This reverts commit 141446a4d9.

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen 2023-01-15 17:09:53 +08:00
parent 89186b3172
commit ef405c488e
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
9 changed files with 2365 additions and 505 deletions

View File

@ -13,16 +13,10 @@
/ {
model = "FriendlyElec NanoPi R2C";
compatible = "friendlyarm,nanopi-r2c", "rockchip,rk3328";
aliases {
ethernet1 = &yt8521s;
};
};
&gmac2io {
phy-handle = <&yt8521s>;
tx_delay = <0x22>;
rx_delay = <0x12>;
mdio {
/delete-node/ ethernet-phy@1;

View File

@ -11,16 +11,10 @@
/ {
model = "FriendlyElec NanoPi R2C";
compatible = "friendlyarm,nanopi-r2c", "rockchip,rk3328";
aliases {
ethernet1 = &yt8521s;
};
};
&gmac2io {
phy-handle = <&yt8521s>;
tx_delay = <0x22>;
rx_delay = <0x12>;
mdio {
/delete-node/ ethernet-phy@1;

View File

@ -1,420 +0,0 @@
/*
* drivers/net/phy/motorcomm.c
*
* Driver for Motorcomm PHYs
*
* Author: Leilei Zhao <leilei.zhao@motorcomm.com>
*
* Copyright (c) 2019 Motorcomm, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* Support : Motorcomm Phys:
* Giga phys: yt8511, yt8521
* 100/10 Phys : yt8512, yt8512b, yt8510
* Automotive 100Mb Phys : yt8010
* Automotive 100/10 hyper range Phys: yt8510
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/motorcomm_phy.h>
#include <linux/of.h>
#include <linux/clk.h>
static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
{
int ret;
int val;
ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
val = phy_read(phydev, REG_DEBUG_DATA);
return val;
}
static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
{
int ret;
ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
ret = phy_write(phydev, REG_DEBUG_DATA, val);
return ret;
}
static int yt8010_config_aneg(struct phy_device *phydev)
{
phydev->speed = SPEED_100;
return 0;
}
static int yt8511_config_init(struct phy_device *phydev)
{
int ret;
int val;
/* disable auto sleep */
val = ytphy_read_ext(phydev, YT8511_EXTREG_SLEEP_CONTROL1);
if (val < 0)
return val;
val &= (~BIT(YT8511_EN_SLEEP_SW_BIT));
ret = ytphy_write_ext(phydev, YT8511_EXTREG_SLEEP_CONTROL1, val);
if (ret < 0)
return ret;
/* output SyncE clock (125mhz) even link is down */
ret = ytphy_write_ext(phydev, 0xa012, 0xd0);
if (ret < 0)
return ret;
/* enable RXC clock when no wire plug */
val = ytphy_read_ext(phydev, 0xc);
if (val < 0)
return val;
/* ext reg 0xc.b[2:1]
00-----25M from pll;
01---- 25M from xtl;(default)
10-----62.5M from pll;
11----125M from pll(here set to this value)
*/
val |= (3 << 1);
ret = ytphy_write_ext(phydev, 0xc, val);
if (ret < 0)
return ret;
/* LED0: Unused/Off, LED1: Link, LED2: Activity, 8Hz */
ytphy_write_ext(phydev, 0xa00b, 0xe004);
ytphy_write_ext(phydev, 0xa00c, 0);
ytphy_write_ext(phydev, 0xa00d, 0x2600);
ytphy_write_ext(phydev, 0xa00e, 0x0070);
ytphy_write_ext(phydev, 0xa00f, 0x000a);
return 0;
}
static int yt8512_clk_init(struct phy_device *phydev)
{
int ret;
int val;
val = ytphy_read_ext(phydev, YT8512_EXTREG_AFE_PLL);
if (val < 0)
return val;
val |= YT8512_CONFIG_PLL_REFCLK_SEL_EN;
ret = ytphy_write_ext(phydev, YT8512_EXTREG_AFE_PLL, val);
if (ret < 0)
return ret;
val = ytphy_read_ext(phydev, YT8512_EXTREG_EXTEND_COMBO);
if (val < 0)
return val;
val |= YT8512_CONTROL1_RMII_EN;
ret = ytphy_write_ext(phydev, YT8512_EXTREG_EXTEND_COMBO, val);
if (ret < 0)
return ret;
val = phy_read(phydev, MII_BMCR);
if (val < 0)
return val;
val |= YT_SOFTWARE_RESET;
ret = phy_write(phydev, MII_BMCR, val);
return ret;
}
static int yt8512_led_init(struct phy_device *phydev)
{
int ret;
int val;
int mask;
val = ytphy_read_ext(phydev, YT8512_EXTREG_LED0);
if (val < 0)
return val;
val |= YT8512_LED0_ACT_BLK_IND;
mask = YT8512_LED0_DIS_LED_AN_TRY | YT8512_LED0_BT_BLK_EN |
YT8512_LED0_HT_BLK_EN | YT8512_LED0_COL_BLK_EN |
YT8512_LED0_BT_ON_EN;
val &= ~mask;
ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED0, val);
if (ret < 0)
return ret;
val = ytphy_read_ext(phydev, YT8512_EXTREG_LED1);
if (val < 0)
return val;
val |= YT8512_LED1_BT_ON_EN;
mask = YT8512_LED1_TXACT_BLK_EN | YT8512_LED1_RXACT_BLK_EN;
val &= ~mask;
ret = ytphy_write_ext(phydev, YT8512_LED1_BT_ON_EN, val);
return ret;
}
static int yt8512_config_init(struct phy_device *phydev)
{
int ret;
int val;
ret = yt8512_clk_init(phydev);
if (ret < 0)
return ret;
ret = yt8512_led_init(phydev);
/* disable auto sleep */
val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1);
if (val < 0)
return val;
val &= (~BIT(YT8512_EN_SLEEP_SW_BIT));
ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val);
if (ret < 0)
return ret;
return ret;
}
static int yt8512_read_status(struct phy_device *phydev)
{
int ret;
int val;
int speed, speed_mode, duplex;
ret = genphy_update_link(phydev);
if (ret)
return ret;
val = phy_read(phydev, REG_PHY_SPEC_STATUS);
if (val < 0)
return val;
duplex = (val & YT8512_DUPLEX) >> YT8512_DUPLEX_BIT;
speed_mode = (val & YT8512_SPEED_MODE) >> YT8512_SPEED_MODE_BIT;
switch (speed_mode) {
case 0:
speed = SPEED_10;
break;
case 1:
speed = SPEED_100;
break;
case 2:
case 3:
default:
speed = SPEED_UNKNOWN;
break;
}
phydev->speed = speed;
phydev->duplex = duplex;
return 0;
}
static int yt8521_config_init(struct phy_device *phydev)
{
int ret;
int val;
/* disable auto sleep */
val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
if (val < 0)
return val;
val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
if (ret < 0)
return ret;
/* switch to access UTP */
ret = ytphy_write_ext(phydev, 0xa000, 0);
if (ret < 0)
return ret;
/* enable RXC clock when no wire plug */
val = ytphy_read_ext(phydev, 0xc);
if (val < 0)
return val;
val &= ~(1 << 12);
ret = ytphy_write_ext(phydev, 0xc, val);
if (ret < 0)
return ret;
/* output SyncE clock (125mhz) even link is down */
ret = ytphy_write_ext(phydev, 0xa012, 0x38);
if (ret < 0)
return ret;
/* disable rgmii clk 2ns delay */
val = ytphy_read_ext(phydev, 0xa001);
if (val < 0)
return val;
val &= ~(1 << 8);
ret = ytphy_write_ext(phydev, 0xa001, val);
if (ret < 0)
return ret;
/* setup delay */
val = (1 << 10) | (0xf << 4) | 5;
ret = ytphy_write_ext(phydev, 0xa003, val);
if (ret < 0)
return ret;
/* LED0: Unused/Off, LED1: Link, LED2: Activity, 8Hz */
ytphy_write_ext(phydev, 0xa00b, 0xe004);
ytphy_write_ext(phydev, 0xa00c, 0);
ytphy_write_ext(phydev, 0xa00d, 0x2600);
ytphy_write_ext(phydev, 0xa00e, 0x0070);
ytphy_write_ext(phydev, 0xa00f, 0x000a);
return 0;
}
static int yt8521_config_intr(struct phy_device *phydev)
{
int val;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
val = BIT(14) | BIT(13) | BIT(11) | BIT(10);
else
val = 0;
return phy_write(phydev, REG_INT_MASK, val);
}
static irqreturn_t yt8521_ack_interrupt(struct phy_device *phydev)
{
int val;
val = phy_read(phydev, REG_INT_STATUS);
phydev_dbg(phydev, "intr status 0x04%x\n", val);
if (val < 0) {
phy_error(phydev);
return IRQ_NONE;
}
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static struct phy_driver ytphy_drvs[] = {
{
.phy_id = PHY_ID_YT8010,
.name = "YT8010 Automotive Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_BASIC_FEATURES,
.config_aneg = yt8010_config_aneg,
.read_status = genphy_read_status,
}, {
.phy_id = PHY_ID_YT8510,
.name = "YT8510 100/10Mb Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_BASIC_FEATURES,
.read_status = genphy_read_status,
}, {
.phy_id = PHY_ID_YT8511,
.name = "YT8511 Gigabit Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_GBIT_FEATURES,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = PHY_ID_YT8512,
.name = "YT8512 Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_BASIC_FEATURES,
.config_init = yt8512_config_init,
.read_status = yt8512_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = PHY_ID_YT8512B,
.name = "YT8512B Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_BASIC_FEATURES,
.config_init = yt8512_config_init,
.read_status = yt8512_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = PHY_ID_YT8521,
.name = "YT8521 Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
/* PHY_GBIT_FEATURES */
.config_init = yt8521_config_init,
.handle_interrupt = yt8521_ack_interrupt,
.config_intr = yt8521_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
/* same as 8521 */
.phy_id = PHY_ID_YT8531S,
.name = "YT8531S Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
/* PHY_GBIT_FEATURES */
.config_init = yt8521_config_init,
.handle_interrupt = yt8521_ack_interrupt,
.config_intr = yt8521_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
/* same as 8511 */
.phy_id = PHY_ID_YT8531,
.name = "YT8531 Gigabit Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_GBIT_FEATURES,
.config_init = yt8511_config_init,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
};
module_phy_driver(ytphy_drvs);
MODULE_DESCRIPTION("Motorcomm PHY driver");
MODULE_AUTHOR("Leilei Zhao");
MODULE_LICENSE("GPL");
static struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
{ PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK },
{ PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK },
{ PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK },
{ PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK },
{ PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK },
{ PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK },
{ PHY_ID_YT8531S, MOTORCOMM_PHY_ID_8531_MASK },
{ PHY_ID_YT8531, MOTORCOMM_PHY_ID_8531_MASK },
{ }
};
MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);

View File

@ -1,73 +0,0 @@
/*
* include/linux/motorcomm_phy.h
*
* Motorcomm PHY IDs
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef _MOTORCOMM_PHY_H
#define _MOTORCOMM_PHY_H
#define MOTORCOMM_PHY_ID_MASK 0x00000fff
#define MOTORCOMM_PHY_ID_8531_MASK 0xffffffff
#define PHY_ID_YT8010 0x00000309
#define PHY_ID_YT8510 0x00000109
#define PHY_ID_YT8511 0x0000010a
#define PHY_ID_YT8512 0x00000118
#define PHY_ID_YT8512B 0x00000128
#define PHY_ID_YT8521 0x0000011a
#define PHY_ID_YT8531S 0x4f51e91a
#define PHY_ID_YT8531 0x4f51e91b
#define REG_PHY_SPEC_STATUS 0x11
#define REG_INT_MASK 0x12
#define REG_INT_STATUS 0x13
#define REG_DEBUG_ADDR_OFFSET 0x1e
#define REG_DEBUG_DATA 0x1f
#define YT8511_EXTREG_SLEEP_CONTROL1 0x27
#define YT8511_EN_SLEEP_SW_BIT 15
#define YT8512_EXTREG_AFE_PLL 0x50
#define YT8512_EXTREG_EXTEND_COMBO 0x4000
#define YT8512_EXTREG_LED0 0x40c0
#define YT8512_EXTREG_LED1 0x40c3
#define YT8512_EXTREG_SLEEP_CONTROL1 0x2027
#define YT_SOFTWARE_RESET 0x8000
#define YT8512_CONFIG_PLL_REFCLK_SEL_EN 0x0040
#define YT8512_CONTROL1_RMII_EN 0x0001
#define YT8512_LED0_ACT_BLK_IND 0x1000
#define YT8512_LED0_DIS_LED_AN_TRY 0x0001
#define YT8512_LED0_BT_BLK_EN 0x0002
#define YT8512_LED0_HT_BLK_EN 0x0004
#define YT8512_LED0_COL_BLK_EN 0x0008
#define YT8512_LED0_BT_ON_EN 0x0010
#define YT8512_LED1_BT_ON_EN 0x0010
#define YT8512_LED1_TXACT_BLK_EN 0x0100
#define YT8512_LED1_RXACT_BLK_EN 0x0200
#define YT8512_SPEED_MODE 0xc000
#define YT8512_DUPLEX 0x2000
#define YT8512_SPEED_MODE_BIT 14
#define YT8512_DUPLEX_BIT 13
#define YT8512_EN_SLEEP_SW_BIT 15
#define YT8521_EXTREG_SLEEP_CONTROL1 0x27
#define YT8521_EN_SLEEP_SW_BIT 15
#define YT8521_SPEED_MODE 0xc000
#define YT8521_DUPLEX 0x2000
#define YT8521_SPEED_MODE_BIT 14
#define YT8521_DUPLEX_BIT 13
#define YT8521_LINK_STATUS_BIT 10
#endif /* _MOTORCOMM_PHY_H */

View File

@ -0,0 +1,55 @@
From bdb6cfe7512f7a214815a3092f0be50963dcacbc Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Sat, 18 Jun 2022 11:28:32 +0100
Subject: [PATCH] net: mii: add mii_bmcr_encode_fixed()
Add a function to encode a fixed speed/duplex to a BMCR value.
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/linux/mii.h | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
--- a/include/linux/mii.h
+++ b/include/linux/mii.h
@@ -595,4 +595,39 @@ static inline u8 mii_resolve_flowctrl_fd
return cap;
}
+/**
+ * mii_bmcr_encode_fixed - encode fixed speed/duplex settings to a BMCR value
+ * @speed: a SPEED_* value
+ * @duplex: a DUPLEX_* value
+ *
+ * Encode the speed and duplex to a BMCR value. 2500, 1000, 100 and 10 Mbps are
+ * supported. 2500Mbps is encoded to 1000Mbps. Other speeds are encoded as 10
+ * Mbps. Unknown duplex values are encoded to half-duplex.
+ */
+static inline u16 mii_bmcr_encode_fixed(int speed, int duplex)
+{
+ u16 bmcr;
+
+ switch (speed) {
+ case SPEED_2500:
+ case SPEED_1000:
+ bmcr = BMCR_SPEED1000;
+ break;
+
+ case SPEED_100:
+ bmcr = BMCR_SPEED100;
+ break;
+
+ case SPEED_10:
+ default:
+ bmcr = BMCR_SPEED10;
+ break;
+ }
+
+ if (duplex == DUPLEX_FULL)
+ bmcr |= BMCR_FULLDPLX;
+
+ return bmcr;
+}
+
#endif /* __LINUX_MII_H__ */

View File

@ -0,0 +1,49 @@
From 4e0243e7128c9b25ea2739136076a95d6adaba5e Mon Sep 17 00:00:00 2001
From: Frank <Frank.Sae@motor-comm.com>
Date: Fri, 4 Nov 2022 16:44:41 +0800
Subject: [PATCH] net: phy: fix yt8521 duplicated argument to & or |
cocci warnings: (new ones prefixed by >>)
>> drivers/net/phy/motorcomm.c:1122:8-35: duplicated argument to & or |
drivers/net/phy/motorcomm.c:1126:8-35: duplicated argument to & or |
drivers/net/phy/motorcomm.c:1130:8-34: duplicated argument to & or |
drivers/net/phy/motorcomm.c:1134:8-34: duplicated argument to & or |
The second YT8521_RC1R_GE_TX_DELAY_xx should be YT8521_RC1R_FE_TX_DELAY_xx.
Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy")
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Julia Lawall <julia.lawall@lip6.fr>
Signed-off-by: Frank <Frank.Sae@motor-comm.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
drivers/net/phy/motorcomm.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1119,19 +1119,19 @@ static int yt8521_config_init(struct phy
switch (phydev->interface) {
case PHY_INTERFACE_MODE_RGMII:
- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
val |= YT8521_RC1R_RX_DELAY_DIS;
break;
case PHY_INTERFACE_MODE_RGMII_RXID:
- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
val |= YT8521_RC1R_RX_DELAY_EN;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
val |= YT8521_RC1R_RX_DELAY_DIS;
break;
case PHY_INTERFACE_MODE_RGMII_ID:
- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
val |= YT8521_RC1R_RX_DELAY_EN;
break;
case PHY_INTERFACE_MODE_SGMII:

View File

@ -0,0 +1,140 @@
From 813abcd98fb1b2cccf850cdfa092a4bfc50b2363 Mon Sep 17 00:00:00 2001
From: Frank <Frank.Sae@motor-comm.com>
Date: Tue, 22 Nov 2022 16:42:32 +0800
Subject: [PATCH] net: phy: add Motorcomm YT8531S phy id.
We added patch for motorcomm.c to support YT8531S. This patch has
been tested on AM335x platform which has one YT8531S interface
card and passed all test cases.
The tested cases indluding: YT8531S UTP function with support of
10M/100M/1000M; YT8531S Fiber function with support of 100M/1000M;
and YT8531S Combo function that supports auto detection of media type.
Since most functions of YT8531S are similar to YT8521 and we reuse some
codes for YT8521 in the patch file.
Signed-off-by: Frank <Frank.Sae@motor-comm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
drivers/net/phy/Kconfig | 2 +-
drivers/net/phy/motorcomm.c | 52 +++++++++++++++++++++++++++++++++----
2 files changed, 48 insertions(+), 6 deletions(-)
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -319,7 +319,7 @@ config MOTORCOMM_PHY
tristate "Motorcomm PHYs"
help
Enables support for Motorcomm network PHYs.
- Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs.
+ Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
config NATIONAL_PHY
tristate "National Semiconductor PHYs"
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Motorcomm 8511/8521 PHY driver.
+ * Motorcomm 8511/8521/8531S PHY driver.
*
* Author: Peter Geis <pgwipeout@gmail.com>
* Author: Frank <Frank.Sae@motor-comm.com>
@@ -12,9 +12,10 @@
#include <linux/phy.h>
#define PHY_ID_YT8511 0x0000010a
-#define PHY_ID_YT8521 0x0000011A
+#define PHY_ID_YT8521 0x0000011A
+#define PHY_ID_YT8531S 0x4F51E91A
-/* YT8521 Register Overview
+/* YT8521/YT8531S Register Overview
* UTP Register space | FIBER Register space
* ------------------------------------------------------------
* | UTP MII | FIBER MII |
@@ -147,7 +148,7 @@
#define YT8521_LINK_TIMER_CFG2_REG 0xA5
#define YT8521_LTCR_EN_AUTOSEN BIT(15)
-/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers
+/* 0xA000, 0xA001, 0xA003, 0xA006 ~ 0xA00A and 0xA012 are common ext registers
* of yt8521 phy. There is no need to switch reg space when operating these
* registers.
*/
@@ -221,6 +222,9 @@
*/
#define YTPHY_WCR_TYPE_PULSE BIT(0)
+#define YT8531S_SYNCE_CFG_REG 0xA012
+#define YT8531S_SCR_SYNCE_ENABLE BIT(6)
+
/* Extended Register end */
struct yt8521_priv {
@@ -648,6 +652,26 @@ static int yt8521_probe(struct phy_devic
}
/**
+ * yt8531s_probe() - read chip config then set suitable polling_mode
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8531s_probe(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Disable SyncE clock output by default */
+ ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
+ YT8531S_SCR_SYNCE_ENABLE, 0);
+ if (ret < 0)
+ return ret;
+
+ /* same as yt8521_probe */
+ return yt8521_probe(phydev);
+}
+
+/**
* ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
* @phydev: a pointer to a &struct phy_device
*
@@ -1750,11 +1774,28 @@ static struct phy_driver motorcomm_phy_d
.suspend = yt8521_suspend,
.resume = yt8521_resume,
},
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
+ .name = "YT8531S Gigabit Ethernet",
+ .get_features = yt8521_get_features,
+ .probe = yt8531s_probe,
+ .read_page = yt8521_read_page,
+ .write_page = yt8521_write_page,
+ .get_wol = ytphy_get_wol,
+ .set_wol = ytphy_set_wol,
+ .config_aneg = yt8521_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .config_init = yt8521_config_init,
+ .read_status = yt8521_read_status,
+ .soft_reset = yt8521_soft_reset,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+ },
};
module_phy_driver(motorcomm_phy_drvs);
-MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver");
+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
MODULE_AUTHOR("Peter Geis");
MODULE_AUTHOR("Frank");
MODULE_LICENSE("GPL");
@@ -1762,6 +1803,7 @@ MODULE_LICENSE("GPL");
static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
{ /* sentinal */ }
};

View File

@ -0,0 +1,397 @@
From: Frank <Frank.Sae@motor-comm.com>
To: Peter Geis <pgwipeout@gmail.com>, Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: yinghong.zhang@motor-comm.com, fei.zhang@motor-comm.com,
hua.sun@motor-comm.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Frank <Frank.Sae@motor-comm.com>
Subject: [PATCH net-next v2] net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy
Date: Fri, 2 Dec 2022 15:36:48 +0800 [thread overview]
Message-ID: <20221202073648.3182-1-Frank.Sae@motor-comm.com> (raw)
Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have verified
the patch on AM335x platform which has one YT8531 interface
card and passed all test cases.
The tested cases indluding: YT8531 UTP function with support of 10M/100M/1000M
and wol(based on magic packet).
Signed-off-by: Frank <Frank.Sae@motor-comm.com>
---
Hi Russell, Andrew
Thanks and based on your comments we added lock accordingly in the v2 patch.
v2:
- Fixed yt8531_set_wol's lock issue.
- Added ASSERT_MDIO(phydev) to check mdio lock.
Thanks and BR,
Frank
drivers/net/phy/Kconfig | 2 +-
drivers/net/phy/motorcomm.c | 194 +++++++++++++++++++++++++++++++++++-
2 files changed, 190 insertions(+), 6 deletions(-)
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -319,7 +319,7 @@ config MOTORCOMM_PHY
tristate "Motorcomm PHYs"
help
Enables support for Motorcomm network PHYs.
- Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
+ Currently supports the YT8511, YT8521, YT8531, YT8531S Gigabit Ethernet PHYs.
config NATIONAL_PHY
tristate "National Semiconductor PHYs"
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Motorcomm 8511/8521/8531S PHY driver.
+ * Motorcomm 8511/8521/8531/8531S PHY driver.
*
* Author: Peter Geis <pgwipeout@gmail.com>
* Author: Frank <Frank.Sae@motor-comm.com>
@@ -12,8 +12,9 @@
#include <linux/phy.h>
#define PHY_ID_YT8511 0x0000010a
-#define PHY_ID_YT8521 0x0000011A
-#define PHY_ID_YT8531S 0x4F51E91A
+#define PHY_ID_YT8521 0x0000011a
+#define PHY_ID_YT8531 0x4f51e91b
+#define PHY_ID_YT8531S 0x4f51e91a
/* YT8521/YT8531S Register Overview
* UTP Register space | FIBER Register space
@@ -225,6 +226,9 @@
#define YT8531S_SYNCE_CFG_REG 0xA012
#define YT8531S_SCR_SYNCE_ENABLE BIT(6)
+#define YT8531_SYNCE_CFG_REG 0xA012
+#define YT8531_SCR_SYNCE_ENABLE BIT(6)
+
/* Extended Register end */
struct yt8521_priv {
@@ -245,6 +249,15 @@ struct yt8521_priv {
u8 reg_page;
};
+static bool mdio_is_locked(struct phy_device *phydev)
+{
+ return mutex_is_locked(&phydev->mdio.bus->mdio_lock);
+}
+
+#define ASSERT_MDIO(phydev) \
+ WARN_ONCE(!mdio_is_locked(phydev), \
+ "MDIO: assertion failed at %s (%d)\n", __FILE__, __LINE__)
+
/**
* ytphy_read_ext() - read a PHY's extended register
* @phydev: a pointer to a &struct phy_device
@@ -258,6 +271,8 @@ static int ytphy_read_ext(struct phy_dev
{
int ret;
+ ASSERT_MDIO(phydev);
+
ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
if (ret < 0)
return ret;
@@ -297,6 +312,8 @@ static int ytphy_write_ext(struct phy_de
{
int ret;
+ ASSERT_MDIO(phydev);
+
ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
if (ret < 0)
return ret;
@@ -342,6 +359,8 @@ static int ytphy_modify_ext(struct phy_d
{
int ret;
+ ASSERT_MDIO(phydev);
+
ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
if (ret < 0)
return ret;
@@ -479,6 +498,76 @@ err_restore_page:
return phy_restore_page(phydev, old_page, ret);
}
+/**
+ * yt8531_set_wol() - turn wake-on-lan on or off
+ * @phydev: a pointer to a &struct phy_device
+ * @wol: a pointer to a &struct ethtool_wolinfo
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8531_set_wol(struct phy_device *phydev,
+ struct ethtool_wolinfo *wol)
+{
+ struct net_device *p_attached_dev;
+ const u16 mac_addr_reg[] = {
+ YTPHY_WOL_MACADDR2_REG,
+ YTPHY_WOL_MACADDR1_REG,
+ YTPHY_WOL_MACADDR0_REG,
+ };
+ const u8 *mac_addr;
+ u16 mask;
+ u16 val;
+ int ret;
+ u8 i;
+
+ if (wol->wolopts & WAKE_MAGIC) {
+ p_attached_dev = phydev->attached_dev;
+ if (!p_attached_dev)
+ return -ENODEV;
+
+ mac_addr = (const u8 *)p_attached_dev->dev_addr;
+ if (!is_valid_ether_addr(mac_addr))
+ return -EINVAL;
+
+ /* Store the device address for the magic packet */
+ for (i = 0; i < 3; i++) {
+ ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
+ ((mac_addr[i * 2] << 8)) |
+ (mac_addr[i * 2 + 1]));
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Enable WOL feature */
+ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
+ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
+ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
+ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
+ mask, val);
+ if (ret < 0)
+ return ret;
+
+ /* Enable WOL interrupt */
+ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
+ YTPHY_IER_WOL);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* Disable WOL feature */
+ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
+ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
+ mask, 0);
+
+ /* Disable WOL interrupt */
+ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
+ YTPHY_IER_WOL, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int yt8511_read_page(struct phy_device *phydev)
{
return __phy_read(phydev, YT8511_PAGE_SELECT);
@@ -652,6 +741,19 @@ static int yt8521_probe(struct phy_devic
}
/**
+ * yt8531_probe() - Now only disable SyncE clock output
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8531_probe(struct phy_device *phydev)
+{
+ /* Disable SyncE clock output by default */
+ return ytphy_modify_ext_with_lock(phydev, YT8531_SYNCE_CFG_REG,
+ YT8531_SCR_SYNCE_ENABLE, 0);
+}
+
+/**
* yt8531s_probe() - read chip config then set suitable polling_mode
* @phydev: a pointer to a &struct phy_device
*
@@ -683,6 +785,8 @@ static int ytphy_utp_read_lpa(struct phy
{
int lpa, lpagb;
+ ASSERT_MDIO(phydev);
+
if (phydev->autoneg == AUTONEG_ENABLE) {
if (!phydev->autoneg_complete) {
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
@@ -744,6 +848,8 @@ static int yt8521_adjust_status(struct p
int err;
int lpa;
+ ASSERT_MDIO(phydev);
+
if (is_utp)
duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET;
else
@@ -1193,6 +1299,59 @@ err_restore_page:
}
/**
+ * yt8531_config_init() - called to initialize the PHY
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8531_config_init(struct phy_device *phydev)
+{
+ int ret;
+ u16 val;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+ val |= YT8521_RC1R_RX_DELAY_DIS;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+ val |= YT8521_RC1R_RX_DELAY_EN;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+ val |= YT8521_RC1R_RX_DELAY_DIS;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+ val |= YT8521_RC1R_RX_DELAY_EN;
+ break;
+ default: /* do not support other modes */
+ return -EOPNOTSUPP;
+ }
+
+ /* set rgmii delay mode */
+ ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
+ (YT8521_RC1R_RX_DELAY_MASK |
+ YT8521_RC1R_FE_TX_DELAY_MASK |
+ YT8521_RC1R_GE_TX_DELAY_MASK),
+ val);
+ if (ret < 0)
+ return ret;
+
+ /* disable auto sleep */
+ ret = ytphy_modify_ext_with_lock(phydev,
+ YT8521_EXTREG_SLEEP_CONTROL1_REG,
+ YT8521_ESC1R_SLEEP_SW, 0);
+ if (ret < 0)
+ return ret;
+
+ /* enable RXC clock when no wire plug */
+ return ytphy_modify_ext_with_lock(phydev, YT8521_CLOCK_GATING_REG,
+ YT8521_CGR_RX_CLK_EN, 0);
+}
+
+/**
* yt8521_prepare_fiber_features() - A small helper function that setup
* fiber's features.
* @phydev: a pointer to a &struct phy_device
@@ -1220,6 +1379,8 @@ static int yt8521_fiber_setup_forced(str
u16 val;
int ret;
+ ASSERT_MDIO(phydev);
+
if (phydev->speed == SPEED_1000)
val = YTPHY_MCR_FIBER_1000BX;
else if (phydev->speed == SPEED_100)
@@ -1259,6 +1420,8 @@ static int ytphy_check_and_restart_aneg(
{
int ret;
+ ASSERT_MDIO(phydev);
+
if (!restart) {
/* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
@@ -1295,6 +1458,8 @@ static int yt8521_fiber_config_aneg(stru
int bmcr;
u16 adv;
+ ASSERT_MDIO(phydev);
+
if (phydev->autoneg != AUTONEG_ENABLE)
return yt8521_fiber_setup_forced(phydev);
@@ -1352,6 +1517,8 @@ static int ytphy_setup_master_slave(stru
{
u16 ctl = 0;
+ ASSERT_MDIO(phydev);
+
if (!phydev->is_gigabit_capable)
return 0;
@@ -1397,6 +1564,8 @@ static int ytphy_utp_config_advert(struc
int err, bmsr, changed = 0;
u32 adv;
+ ASSERT_MDIO(phydev);
+
/* Only allow advertising what this PHY supports */
linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported);
@@ -1454,6 +1623,8 @@ static int ytphy_utp_config_aneg(struct
int err;
u16 ctl;
+ ASSERT_MDIO(phydev);
+
err = ytphy_setup_master_slave(phydev);
if (err < 0)
return err;
@@ -1655,6 +1826,8 @@ static int ytphy_utp_read_abilities(stru
{
int val;
+ ASSERT_MDIO(phydev);
+
linkmode_set_bit_array(phy_basic_ports_array,
ARRAY_SIZE(phy_basic_ports_array),
phydev->supported);
@@ -1775,6 +1948,16 @@ static struct phy_driver motorcomm_phy_d
.resume = yt8521_resume,
},
{
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
+ .name = "YT8531 Gigabit Ethernet",
+ .probe = yt8531_probe,
+ .config_init = yt8531_config_init,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .get_wol = ytphy_get_wol,
+ .set_wol = yt8531_set_wol,
+ },
+ {
PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
.name = "YT8531S Gigabit Ethernet",
.get_features = yt8521_get_features,
@@ -1795,7 +1978,7 @@ static struct phy_driver motorcomm_phy_d
module_phy_driver(motorcomm_phy_drvs);
-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
MODULE_AUTHOR("Peter Geis");
MODULE_AUTHOR("Frank");
MODULE_LICENSE("GPL");
@@ -1803,8 +1986,9 @@ MODULE_LICENSE("GPL");
static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
- { /* sentinal */ }
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);