mediatek: add mt7981 internal gphy cal support

This commit is contained in:
hanwckf 2024-09-21 20:17:53 +08:00
parent 5ddcd81884
commit aa2041c012
9 changed files with 2061 additions and 817 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,452 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/phy.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include "mtk.h"
/* Difference between functions with mtk_tr* and __mtk_tr* prefixes is
* mtk_tr* functions: wrapped by page switching operations
* __mtk_tr* functions: no page switching operations
*/
static void __mtk_tr_access(struct phy_device *phydev, bool read, u8 ch_addr,
u8 node_addr, u8 data_addr)
{
u16 tr_cmd = BIT(15); /* bit 14 & 0 are reserved */
if (read)
tr_cmd |= BIT(13);
tr_cmd |= (((ch_addr & 0x3) << 11) |
((node_addr & 0xf) << 7) |
((data_addr & 0x3f) << 1));
dev_dbg(&phydev->mdio.dev, "tr_cmd: 0x%x\n", tr_cmd);
__phy_write(phydev, 0x10, tr_cmd);
}
static void __mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u16 *tr_high, u16 *tr_low)
{
__mtk_tr_access(phydev, true, ch_addr, node_addr, data_addr);
*tr_low = __phy_read(phydev, 0x11);
*tr_high = __phy_read(phydev, 0x12);
dev_dbg(&phydev->mdio.dev, "tr_high read: 0x%x, tr_low read: 0x%x\n",
*tr_high, *tr_low);
}
u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr)
{
u16 tr_high;
u16 tr_low;
phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
__mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low);
phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
return (tr_high << 16) | tr_low;
}
EXPORT_SYMBOL_GPL(mtk_tr_read);
static void __mtk_tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 tr_data)
{
__phy_write(phydev, 0x11, tr_data & 0xffff);
__phy_write(phydev, 0x12, tr_data >> 16);
dev_dbg(&phydev->mdio.dev, "tr_high write: 0x%x, tr_low write: 0x%x\n",
tr_data >> 16, tr_data & 0xffff);
__mtk_tr_access(phydev, false, ch_addr, node_addr, data_addr);
}
void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 mask, u32 set)
{
u32 tr_data;
u16 tr_high;
u16 tr_low;
__mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low);
tr_data = (tr_high << 16) | tr_low;
tr_data = (tr_data & ~mask) | set;
__mtk_tr_write(phydev, ch_addr, node_addr, data_addr, tr_data);
}
EXPORT_SYMBOL_GPL(__mtk_tr_modify);
void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 mask, u32 set)
{
phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
__mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, mask, set);
phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
}
EXPORT_SYMBOL_GPL(mtk_tr_modify);
void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 set)
{
__mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, 0, set);
}
EXPORT_SYMBOL_GPL(__mtk_tr_set_bits);
void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 clr)
{
__mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, clr, 0);
}
EXPORT_SYMBOL_GPL(__mtk_tr_clr_bits);
int mtk_phy_read_page(struct phy_device *phydev)
{
return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
}
EXPORT_SYMBOL_GPL(mtk_phy_read_page);
int mtk_phy_write_page(struct phy_device *phydev, int page)
{
return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
}
EXPORT_SYMBOL_GPL(mtk_phy_write_page);
/* This function deals with the case that 1G AN starts but isn't completed. We
* set AN_NEW_LP_CNT_LIMIT with different values time after time to let our
* 1G->100Mbps hardware automatic downshift to fit more partner devices.
*/
static int extend_an_new_lp_cnt_limit(struct phy_device *phydev)
{
int mmd_read_ret;
u32 reg_val;
int timeout;
/* According to table 28-9 & Figure 28-18 in IEEE 802.3,
* link_fail_inhibit_timer of 10/100/1000 Mbps devices ranges from 750
* to "1000ms". Once MTK_PHY_FINAL_SPEED_1000 is set, it means that we
* enter "FLP LINK GOOD CHECK" state, link_fail_inhibit_timer starts and
* this PHY's 1G training starts. If 1G training never starts, we do
* nothing but leave.
*/
timeout = read_poll_timeout(mmd_read_ret = phy_read_mmd, reg_val,
(mmd_read_ret < 0) ||
reg_val & MTK_PHY_FINAL_SPEED_1000,
10000, 1000000, false, phydev,
MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC);
if (mmd_read_ret < 0)
return mmd_read_ret;
if (!timeout) {
/* Once we found MTK_PHY_FINAL_SPEED_1000 is set, no matter 1G
* AN is completed or not, we'll set AN_NEW_LP_CNT_LIMIT again
* and again.
*/
mtk_tr_modify(phydev, 0x0, 0xf, 0x3c, AN_NEW_LP_CNT_LIMIT_MASK,
FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK, 0xf));
mdelay(1500);
timeout = read_poll_timeout(mtk_tr_read, reg_val,
(reg_val & AN_STATE_MASK) !=
(AN_STATE_TX_DISABLE <<
AN_STATE_SHIFT),
10000, 1000000, false, phydev,
0x0, 0xf, 0x2);
if (!timeout) {
mdelay(625);
mtk_tr_modify(phydev, 0x0, 0xf, 0x3c,
AN_NEW_LP_CNT_LIMIT_MASK,
FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK,
0x8));
mdelay(500);
mtk_tr_modify(phydev, 0x0, 0xf, 0x3c,
AN_NEW_LP_CNT_LIMIT_MASK,
FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK,
0xf));
} else {
return -ETIMEDOUT;
}
}
return 0;
}
int mtk_gphy_cl22_read_status(struct phy_device *phydev)
{
int ret;
ret = genphy_read_status(phydev);
if (ret)
return ret;
if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) {
ret = phy_read_paged(phydev, MTK_PHY_PAGE_EXTENDED_1,
MTK_PHY_AUX_CTRL_AND_STATUS);
if (ret < 0)
return ret;
/* Once LP_DETECTED is set, it means that"ability_match" in
* IEEE 802.3 Figure 28-18 is set. This happens after we plug in
* cable. Also, LP_DETECTED will be cleared after AN complete.
*/
if (!FIELD_GET(MTK_PHY_LP_DETECTED_MASK, ret))
return 0;
ret = phy_read(phydev, MII_CTRL1000);
if (ret & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) {
ret = extend_an_new_lp_cnt_limit(phydev);
if (ret < 0)
return ret;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(mtk_gphy_cl22_read_status);
#if 0
int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
unsigned long rules,
unsigned long supported_triggers)
{
if (index > 1)
return -EINVAL;
/* All combinations of the supported triggers are allowed */
if (rules & ~supported_triggers)
return -EOPNOTSUPP;
return 0;
}
EXPORT_SYMBOL_GPL(mtk_phy_led_hw_is_supported);
int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index,
unsigned long *rules, unsigned long *led_state,
u16 on_set, u16 rx_blink_set, u16 tx_blink_set)
{
unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK +
(index ? 16 : 0);
unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
int on, blink;
if (index > 1)
return -EINVAL;
on = phy_read_mmd(phydev, MDIO_MMD_VEND2,
index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL);
if (on < 0)
return -EIO;
blink = phy_read_mmd(phydev, MDIO_MMD_VEND2,
index ? MTK_PHY_LED1_BLINK_CTRL :
MTK_PHY_LED0_BLINK_CTRL);
if (blink < 0)
return -EIO;
if ((on & (on_set | MTK_PHY_LED_ON_FDX |
MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) ||
(blink & (rx_blink_set | tx_blink_set)))
set_bit(bit_netdev, led_state);
else
clear_bit(bit_netdev, led_state);
if (on & MTK_PHY_LED_ON_FORCE_ON)
set_bit(bit_on, led_state);
else
clear_bit(bit_on, led_state);
if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK)
set_bit(bit_blink, led_state);
else
clear_bit(bit_blink, led_state);
if (!rules)
return 0;
if (on & on_set)
*rules |= BIT(TRIGGER_NETDEV_LINK);
if (on & MTK_PHY_LED_ON_LINK10)
*rules |= BIT(TRIGGER_NETDEV_LINK_10);
if (on & MTK_PHY_LED_ON_LINK100)
*rules |= BIT(TRIGGER_NETDEV_LINK_100);
if (on & MTK_PHY_LED_ON_LINK1000)
*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
if (on & MTK_PHY_LED_ON_LINK2500)
*rules |= BIT(TRIGGER_NETDEV_LINK_2500);
if (on & MTK_PHY_LED_ON_FDX)
*rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
if (on & MTK_PHY_LED_ON_HDX)
*rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
if (blink & rx_blink_set)
*rules |= BIT(TRIGGER_NETDEV_RX);
if (blink & tx_blink_set)
*rules |= BIT(TRIGGER_NETDEV_TX);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_get);
int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index,
unsigned long rules, unsigned long *led_state,
u16 on_set, u16 rx_blink_set, u16 tx_blink_set)
{
unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
u16 on = 0, blink = 0;
int ret;
if (index > 1)
return -EINVAL;
if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
on |= MTK_PHY_LED_ON_FDX;
if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
on |= MTK_PHY_LED_ON_HDX;
if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK)))
on |= MTK_PHY_LED_ON_LINK10;
if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
on |= MTK_PHY_LED_ON_LINK100;
if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
on |= MTK_PHY_LED_ON_LINK1000;
if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
on |= MTK_PHY_LED_ON_LINK2500;
if (rules & BIT(TRIGGER_NETDEV_RX)) {
if (on & on_set) {
if (on & MTK_PHY_LED_ON_LINK10)
blink |= MTK_PHY_LED_BLINK_10RX;
if (on & MTK_PHY_LED_ON_LINK100)
blink |= MTK_PHY_LED_BLINK_100RX;
if (on & MTK_PHY_LED_ON_LINK1000)
blink |= MTK_PHY_LED_BLINK_1000RX;
if (on & MTK_PHY_LED_ON_LINK2500)
blink |= MTK_PHY_LED_BLINK_2500RX;
} else {
blink |= rx_blink_set;
}
}
if (rules & BIT(TRIGGER_NETDEV_TX)) {
if (on & on_set) {
if (on & MTK_PHY_LED_ON_LINK10)
blink |= MTK_PHY_LED_BLINK_10TX;
if (on & MTK_PHY_LED_ON_LINK100)
blink |= MTK_PHY_LED_BLINK_100TX;
if (on & MTK_PHY_LED_ON_LINK1000)
blink |= MTK_PHY_LED_BLINK_1000TX;
if (on & MTK_PHY_LED_ON_LINK2500)
blink |= MTK_PHY_LED_BLINK_2500TX;
} else {
blink |= tx_blink_set;
}
}
if (blink || on)
set_bit(bit_netdev, led_state);
else
clear_bit(bit_netdev, led_state);
ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL,
MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | on_set,
on);
if (ret)
return ret;
return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
MTK_PHY_LED1_BLINK_CTRL :
MTK_PHY_LED0_BLINK_CTRL, blink);
}
EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_set);
int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on,
unsigned long *delay_off, bool *blinking)
{
if (index > 1)
return -EINVAL;
if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) {
*blinking = true;
*delay_on = 50;
*delay_off = 50;
}
return 0;
}
EXPORT_SYMBOL_GPL(mtk_phy_led_num_dly_cfg);
int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index,
unsigned long *led_state, u16 led_on_mask, bool on)
{
unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
bool changed;
if (on)
changed = !test_and_set_bit(bit_on, led_state);
else
changed = !!test_and_clear_bit(bit_on, led_state);
changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV +
(index ? 16 : 0), led_state);
if (changed)
return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
MTK_PHY_LED1_ON_CTRL :
MTK_PHY_LED0_ON_CTRL,
led_on_mask,
on ? MTK_PHY_LED_ON_FORCE_ON : 0);
else
return 0;
}
EXPORT_SYMBOL_GPL(mtk_phy_hw_led_on_set);
int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index,
unsigned long *led_state, bool blinking)
{
unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK +
(index ? 16 : 0);
bool changed;
if (blinking)
changed = !test_and_set_bit(bit_blink, led_state);
else
changed = !!test_and_clear_bit(bit_blink, led_state);
changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV +
(index ? 16 : 0), led_state);
if (changed)
return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
MTK_PHY_LED1_BLINK_CTRL :
MTK_PHY_LED0_BLINK_CTRL,
blinking ?
MTK_PHY_LED_BLINK_FORCE_BLINK : 0);
else
return 0;
}
EXPORT_SYMBOL_GPL(mtk_phy_hw_led_blink_set);
void mtk_phy_leds_state_init(struct phy_device *phydev)
{
int i;
for (i = 0; i < 2; ++i)
phydev->drv->led_hw_control_get(phydev, i, NULL);
}
EXPORT_SYMBOL_GPL(mtk_phy_leds_state_init);
#endif
MODULE_DESCRIPTION("MediaTek Ethernet PHY driver common");
MODULE_AUTHOR("Sky Huang <SkyLake.Huang@mediatek.com>");
MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Common definition for Mediatek Ethernet PHYs
* Author: SkyLake Huang <SkyLake.Huang@mediatek.com>
* Copyright (c) 2024 MediaTek Inc.
*/
#ifndef _MTK_EPHY_H_
#define _MTK_EPHY_H_
#define MTK_EXT_PAGE_ACCESS 0x1f
#define MTK_PHY_PAGE_STANDARD 0x0000
#define MTK_PHY_PAGE_EXTENDED_1 0x0001
#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14
/* suprv_media_select_RefClk */
#define MTK_PHY_LP_DETECTED_MASK GENMASK(7, 6)
#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4)
#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5
/* Registers on Token Ring debug nodes */
/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x2 */
#define AN_STATE_MASK GENMASK(22, 19)
#define AN_STATE_SHIFT 19
#define AN_STATE_TX_DISABLE 1
/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */
#define AN_NEW_LP_CNT_LIMIT_MASK GENMASK(23, 20)
#define AUTO_NP_10XEN BIT(6)
/* Registers on MDIO_MMD_VEND1 */
#define MTK_PHY_LINK_STATUS_MISC (0xa2)
#define MTK_PHY_FINAL_SPEED_1000 BIT(3)
/* Registers on MDIO_MMD_VEND2 */
#define MTK_PHY_LED0_ON_CTRL 0x24
#define MTK_PHY_LED1_ON_CTRL 0x26
#define MTK_GPHY_LED_ON_MASK GENMASK(6, 0)
#define MTK_2P5GPHY_LED_ON_MASK GENMASK(7, 0)
#define MTK_PHY_LED_ON_LINK1000 BIT(0)
#define MTK_PHY_LED_ON_LINK100 BIT(1)
#define MTK_PHY_LED_ON_LINK10 BIT(2)
#define MTK_PHY_LED_ON_LINKDOWN BIT(3)
#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */
#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */
#define MTK_PHY_LED_ON_FORCE_ON BIT(6)
#define MTK_PHY_LED_ON_LINK2500 BIT(7)
#define MTK_PHY_LED_ON_POLARITY BIT(14)
#define MTK_PHY_LED_ON_ENABLE BIT(15)
#define MTK_PHY_LED0_BLINK_CTRL 0x25
#define MTK_PHY_LED1_BLINK_CTRL 0x27
#define MTK_PHY_LED_BLINK_1000TX BIT(0)
#define MTK_PHY_LED_BLINK_1000RX BIT(1)
#define MTK_PHY_LED_BLINK_100TX BIT(2)
#define MTK_PHY_LED_BLINK_100RX BIT(3)
#define MTK_PHY_LED_BLINK_10TX BIT(4)
#define MTK_PHY_LED_BLINK_10RX BIT(5)
#define MTK_PHY_LED_BLINK_COLLISION BIT(6)
#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7)
#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8)
#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9)
#define MTK_PHY_LED_BLINK_2500TX BIT(10)
#define MTK_PHY_LED_BLINK_2500RX BIT(11)
#define MTK_GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK1000 | \
MTK_PHY_LED_ON_LINK100 | \
MTK_PHY_LED_ON_LINK10)
#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
MTK_PHY_LED_BLINK_100RX | \
MTK_PHY_LED_BLINK_10RX)
#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
MTK_PHY_LED_BLINK_100RX | \
MTK_PHY_LED_BLINK_10RX)
#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \
MTK_GPHY_LED_ON_SET)
#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
MTK_GPHY_LED_RX_BLINK_SET)
#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
MTK_GPHY_LED_TX_BLINK_SET)
#define MTK_PHY_LED_STATE_FORCE_ON 0
#define MTK_PHY_LED_STATE_FORCE_BLINK 1
#define MTK_PHY_LED_STATE_NETDEV 2
u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr);
void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 mask, u32 set);
void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 mask, u32 set);
void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 set);
void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
u8 data_addr, u32 clr);
int mtk_phy_read_page(struct phy_device *phydev);
int mtk_phy_write_page(struct phy_device *phydev, int page);
int mtk_gphy_cl22_read_status(struct phy_device *phydev);
/*int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
unsigned long rules,
unsigned long supported_triggers);
int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index,
unsigned long rules, unsigned long *led_state,
u16 on_set, u16 rx_blink_set, u16 tx_blink_set);
int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index,
unsigned long *rules, unsigned long *led_state,
u16 on_set, u16 rx_blink_set, u16 tx_blink_set);
int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on,
unsigned long *delay_off, bool *blinking);
int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index,
unsigned long *led_state, u16 led_on_mask, bool on);
int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index,
unsigned long *led_state, bool blinking);
void mtk_phy_leds_state_init(struct phy_device *phydev);*/
#endif /* _MTK_EPHY_H_ */

View File

@ -284,7 +284,7 @@ CONFIG_MD=y
CONFIG_MDIO_BUS=y
CONFIG_MDIO_DEVICE=y
# CONFIG_MEDIATEK_2P5GE_PHY is not set
# CONFIG_MEDIATEK_GE_PHY is not set
CONFIG_MEDIATEK_GE_PHY=y
CONFIG_MEDIATEK_MT6577_AUXADC=y
# CONFIG_MEDIATEK_NETSYS_RX_V2 is not set
CONFIG_MEDIATEK_NETSYS_V2=y

View File

@ -771,34 +771,3 @@ index 0000000..7304278
+MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
+MODULE_AUTHOR("Xu Liang");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 19444cd..34bdd16 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -21,6 +21,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/mod_devicetable.h>
+#include <linux/iopoll.h>
#include <linux/atomic.h>
@@ -711,6 +712,18 @@ static inline int phy_read(struct phy_device *phydev, u32 regnum)
return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum);
}
+#define phy_read_poll_timeout(phydev, regnum, val, cond, sleep_us, \
+ timeout_us, sleep_before_read) \
+({ \
+ int __ret = read_poll_timeout(phy_read, val, (cond) || val < 0, \
+ sleep_us, timeout_us, sleep_before_read, phydev, regnum); \
+ if (val < 0) \
+ __ret = val; \
+ if (__ret) \
+ phydev_err(phydev, "%s failed: %d\n", __func__, __ret); \
+ __ret; \
+})
+
/**
* __phy_read - convenience function for reading a given PHY register
* @phydev: the phy_device struct

View File

@ -0,0 +1,7 @@
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -119,3 +119,4 @@ obj-$(CONFIG_AIR_EN8811H_PHY) += air_e
obj-$(CONFIG_MT753X_GSW) += mtk/mt753x/
obj-$(CONFIG_RTL8367S_GSW) += rtk/
+obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-phy-lib.o

View File

@ -1,5 +1,45 @@
From bc97a676615bd0ec66bb2a2a42c939455bf5bed6 Mon Sep 17 00:00:00 2001
From: Sam Shih <sam.shih@mediatek.com>
Date: Fri, 2 Jun 2023 13:06:27 +0800
Subject: [PATCH]
[networking][999-2700-v5.7-iopoll-introduce-read_poll_timeout-macro.patch]
---
drivers/net/phy/phy_device.c | 16 +++++----------
include/linux/iopoll.h | 40 +++++++++++++++++++++++++++++-------
include/linux/phy.h | 27 ++++++++++++++++++++++++
3 files changed, 65 insertions(+), 18 deletions(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 76a68bb02..0349801df 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1056,18 +1056,12 @@ EXPORT_SYMBOL(phy_disconnect);
static int phy_poll_reset(struct phy_device *phydev)
{
/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
- unsigned int retries = 12;
- int ret;
-
- do {
- msleep(50);
- ret = phy_read(phydev, MII_BMCR);
- if (ret < 0)
- return ret;
- } while (ret & BMCR_RESET && --retries);
- if (ret & BMCR_RESET)
- return -ETIMEDOUT;
+ int ret, val;
+ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+ 50000, 600000, true);
+ if (ret)
+ return ret;
/* Some chips (smsc911x) may still need up to another 1ms after the
* BMCR_RESET bit is cleared before they are usable.
*/
diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h
index 35e15df..2c8860e 100644
index 35e15dfd4..cb20c733b 100644
--- a/include/linux/iopoll.h
+++ b/include/linux/iopoll.h
@@ -14,36 +14,41 @@
@ -51,56 +91,7 @@ index 35e15df..2c8860e 100644
break; \
} \
if (__sleep_us) \
@@ -53,42 +58,87 @@
})
/**
- * readx_poll_timeout_atomic - Periodically poll an address until a condition is met or a timeout occurs
- * @op: accessor function (takes @addr as its only argument)
- * @addr: Address to poll
+ * read_poll_timeout_atomic - Periodically poll an address until a condition is
+ * met or a timeout occurs
+ * @op: accessor function (takes @args as its arguments)
* @val: Variable to read the value into
* @cond: Break condition (usually involving @val)
* @delay_us: Time to udelay between reads in us (0 tight-loops). Should
* be less than ~10us since udelay is used (see
* Documentation/timers/timers-howto.rst).
* @timeout_us: Timeout in us, 0 means never timeout
+ * @delay_before_read: if it is true, delay @delay_us before read.
+ * @args: arguments for @op poll
*
* Returns 0 on success and -ETIMEDOUT upon a timeout. In either
- * case, the last read value at @addr is stored in @val.
+ * case, the last read value at @args is stored in @val.
*
* When available, you'll probably want to use one of the specialized
* macros defined below rather than this macro directly.
*/
-#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \
+#define read_poll_timeout_atomic(op, val, cond, delay_us, timeout_us, \
+ delay_before_read, args...) \
({ \
u64 __timeout_us = (timeout_us); \
unsigned long __delay_us = (delay_us); \
ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
+ if (delay_before_read && __delay_us) \
+ udelay(__delay_us); \
for (;;) { \
- (val) = op(addr); \
+ (val) = op(args); \
if (cond) \
break; \
if (__timeout_us && \
ktime_compare(ktime_get(), __timeout) > 0) { \
- (val) = op(addr); \
+ (val) = op(args); \
break; \
} \
if (__delay_us) \
- udelay(__delay_us); \
+ udelay(__delay_us); \
} \
@@ -52,6 +57,27 @@
(cond) ? 0 : -ETIMEDOUT; \
})
@ -125,25 +116,61 @@ index 35e15df..2c8860e 100644
+#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \
+ read_poll_timeout(op, val, cond, sleep_us, timeout_us, false, addr)
+
+/**
+ * readx_poll_timeout_atomic - Periodically poll an address until a condition is met or a timeout occurs
+ * @op: accessor function (takes @addr as its only argument)
+ * @addr: Address to poll
+ * @val: Variable to read the value into
+ * @cond: Break condition (usually involving @val)
+ * @delay_us: Time to udelay between reads in us (0 tight-loops). Should
+ * be less than ~10us since udelay is used (see
+ * Documentation/timers/timers-howto.rst).
+ * @timeout_us: Timeout in us, 0 means never timeout
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
+ * case, the last read value at @addr is stored in @val.
+ *
+ * When available, you'll probably want to use one of the specialized
+ * macros defined below rather than this macro directly.
+ */
+#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \
+ read_poll_timeout_atomic(op, val, cond, delay_us, timeout_us, false, addr)
/**
* readx_poll_timeout_atomic - Periodically poll an address until a condition is met or a timeout occurs
* @op: accessor function (takes @addr as its only argument)
diff --git a/include/linux/phy.h b/include/linux/phy.h
index a1070d60e..107dcbea4 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -21,6 +21,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/mod_devicetable.h>
+#include <linux/iopoll.h>
#define readb_poll_timeout(addr, val, cond, delay_us, timeout_us) \
readx_poll_timeout(readb, addr, val, cond, delay_us, timeout_us)
#include <linux/atomic.h>
@@ -714,6 +715,19 @@ static inline int phy_read(struct phy_device *phydev, u32 regnum)
return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum);
}
+#define phy_read_poll_timeout(phydev, regnum, val, cond, sleep_us, \
+ timeout_us, sleep_before_read) \
+({ \
+ int __ret = read_poll_timeout(phy_read, val, (cond) || val < 0, \
+ sleep_us, timeout_us, sleep_before_read, phydev, regnum); \
+ if (val < 0) \
+ __ret = val; \
+ if (__ret) \
+ phydev_err(phydev, "%s failed: %d\n", __func__, __ret); \
+ __ret; \
+})
+
+
/**
* __phy_read - convenience function for reading a given PHY register
* @phydev: the phy_device struct
@@ -766,6 +780,19 @@ static inline int __phy_write(struct phy_device *phydev, u32 regnum, u16 val)
*/
int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
+#define phy_read_mmd_poll_timeout(phydev, devaddr, regnum, val, cond, \
+ sleep_us, timeout_us, sleep_before_read) \
+({ \
+ int __ret = read_poll_timeout(phy_read_mmd, val, (cond) || val < 0, \
+ sleep_us, timeout_us, sleep_before_read, \
+ phydev, devaddr, regnum); \
+ if (val < 0) \
+ __ret = val; \
+ if (__ret) \
+ phydev_err(phydev, "%s failed: %d\n", __func__, __ret); \
+ __ret; \
+})
+
/**
* __phy_read_mmd - Convenience function for reading a register
* from an MMD on a given PHY.
--
2.34.1

View File

@ -0,0 +1,311 @@
From 2dca4de7282d3003f3703f707d773f4dbbc0f28e Mon Sep 17 00:00:00 2001
From: Sam Shih <sam.shih@mediatek.com>
Date: Fri, 2 Jun 2023 13:06:27 +0800
Subject: [PATCH]
[networking][999-2701-v5.8-net-phy-add-concept-of-shared-storage-for-PHYs.patch]
---
drivers/net/phy/mdio_bus.c | 1 +
drivers/net/phy/phy_device.c | 138 +++++++++++++++++++++++++++++++++++
include/linux/phy.h | 89 ++++++++++++++++++++++
3 files changed, 228 insertions(+)
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index fdf8221f4..d9f2cee33 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -404,6 +404,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
}
mutex_init(&bus->mdio_lock);
+ mutex_init(&bus->shared_lock);
/* de-assert bus level PHY GPIO reset */
gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 0349801df..99f265a1c 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1447,6 +1447,144 @@ bool phy_driver_is_genphy_10g(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
+/**
+ * phy_package_join - join a common PHY group
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * This joins a PHY group and provides a shared storage for all phydevs in
+ * this group. This is intended to be used for packages which contain
+ * more than one PHY, for example a quad PHY transceiver.
+ *
+ * The addr parameter serves as a cookie which has to have the same value
+ * for all members of one group and as a PHY address to access generic
+ * registers of a PHY package. Usually, one of the PHY addresses of the
+ * different PHYs in the package provides access to these global registers.
+ * The address which is given here, will be used in the phy_package_read()
+ * and phy_package_write() convenience functions. If your PHY doesn't have
+ * global registers you can just pick any of the PHY addresses.
+ *
+ * This will set the shared pointer of the phydev to the shared storage.
+ * If this is the first call for a this cookie the shared storage will be
+ * allocated. If priv_size is non-zero, the given amount of bytes are
+ * allocated for the priv member.
+ *
+ * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
+ * with the same cookie but a different priv_size is an error.
+ */
+int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
+{
+ struct mii_bus *bus = phydev->mdio.bus;
+ struct phy_package_shared *shared;
+ int ret;
+
+ if (addr < 0 || addr >= PHY_MAX_ADDR)
+ return -EINVAL;
+
+ mutex_lock(&bus->shared_lock);
+ shared = bus->shared[addr];
+ if (!shared) {
+ ret = -ENOMEM;
+ shared = kzalloc(sizeof(*shared), GFP_KERNEL);
+ if (!shared)
+ goto err_unlock;
+ if (priv_size) {
+ shared->priv = kzalloc(priv_size, GFP_KERNEL);
+ if (!shared->priv)
+ goto err_free;
+ shared->priv_size = priv_size;
+ }
+ shared->addr = addr;
+ refcount_set(&shared->refcnt, 1);
+ bus->shared[addr] = shared;
+ } else {
+ ret = -EINVAL;
+ if (priv_size && priv_size != shared->priv_size)
+ goto err_unlock;
+ refcount_inc(&shared->refcnt);
+ }
+ mutex_unlock(&bus->shared_lock);
+
+ phydev->shared = shared;
+
+ return 0;
+
+err_free:
+ kfree(shared);
+err_unlock:
+ mutex_unlock(&bus->shared_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_package_join);
+
+/**
+ * phy_package_leave - leave a common PHY group
+ * @phydev: target phy_device struct
+ *
+ * This leaves a PHY group created by phy_package_join(). If this phydev
+ * was the last user of the shared data between the group, this data is
+ * freed. Resets the phydev->shared pointer to NULL.
+ */
+void phy_package_leave(struct phy_device *phydev)
+{
+ struct phy_package_shared *shared = phydev->shared;
+ struct mii_bus *bus = phydev->mdio.bus;
+
+ if (!shared)
+ return;
+
+ if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+ bus->shared[shared->addr] = NULL;
+ mutex_unlock(&bus->shared_lock);
+ kfree(shared->priv);
+ kfree(shared);
+ }
+
+ phydev->shared = NULL;
+}
+EXPORT_SYMBOL_GPL(phy_package_leave);
+
+static void devm_phy_package_leave(struct device *dev, void *res)
+{
+ phy_package_leave(*(struct phy_device **)res);
+}
+
+/**
+ * devm_phy_package_join - resource managed phy_package_join()
+ * @dev: device that is registering this PHY package
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * Managed phy_package_join(). Shared storage fetched by this function,
+ * phy_package_leave() is automatically called on driver detach. See
+ * phy_package_join() for more information.
+ */
+int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+ int addr, size_t priv_size)
+{
+ struct phy_device **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = phy_package_join(phydev, addr, priv_size);
+
+ if (!ret) {
+ *ptr = phydev;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_phy_package_join);
+
/**
* phy_detach - detach a PHY device from its network device
* @phydev: target phy_device struct
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 107dcbea4..d26dba255 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -22,6 +22,7 @@
#include <linux/workqueue.h>
#include <linux/mod_devicetable.h>
#include <linux/iopoll.h>
+#include <linux/refcount.h>
#include <linux/atomic.h>
@@ -211,6 +212,28 @@ struct sfp_bus;
struct sfp_upstream_ops;
struct sk_buff;
+/* Represents a shared structure between different phydev's in the same
+ * package, for example a quad PHY. See phy_package_join() and
+ * phy_package_leave().
+ */
+struct phy_package_shared {
+ int addr;
+ refcount_t refcnt;
+ unsigned long flags;
+ size_t priv_size;
+
+ /* private data pointer */
+ /* note that this pointer is shared between different phydevs and
+ * the user has to take care of appropriate locking. It is allocated
+ * and freed automatically by phy_package_join() and
+ * phy_package_leave().
+ */
+ void *priv;
+};
+
+/* used as bit number in atomic bitops */
+#define PHY_SHARED_F_INIT_DONE 0
+
/*
* The Bus class for PHYs. Devices which provide access to
* PHYs should register using this structure
@@ -258,6 +281,12 @@ struct mii_bus {
int reset_delay_us;
/* RESET GPIO descriptor pointer */
struct gpio_desc *reset_gpiod;
+
+ /* protect access to the shared element */
+ struct mutex shared_lock;
+
+ /* shared state across different PHYs */
+ struct phy_package_shared *shared[PHY_MAX_ADDR];
};
#define to_mii_bus(d) container_of(d, struct mii_bus, dev)
@@ -437,6 +466,10 @@ struct phy_device {
/* For use by PHYs to maintain extra state */
void *priv;
+ /* shared data pointer */
+ /* For use by PHYs inside the same package that need a shared state. */
+ struct phy_package_shared *shared;
+
/* Interrupt and Polling infrastructure */
struct delayed_work state_queue;
@@ -1242,6 +1275,10 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev,
int phy_ethtool_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *cmd);
int phy_ethtool_nway_reset(struct net_device *ndev);
+int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size);
+void phy_package_leave(struct phy_device *phydev);
+int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+ int addr, size_t priv_size);
#if IS_ENABLED(CONFIG_PHYLIB)
int __init mdio_bus_init(void);
@@ -1294,6 +1331,58 @@ static inline int phy_ethtool_get_stats(struct phy_device *phydev,
return 0;
}
+static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
+{
+ struct phy_package_shared *shared = phydev->shared;
+
+ if (!shared)
+ return -EIO;
+
+ return mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
+}
+
+static inline int __phy_package_read(struct phy_device *phydev, u32 regnum)
+{
+ struct phy_package_shared *shared = phydev->shared;
+
+ if (!shared)
+ return -EIO;
+
+ return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
+}
+
+static inline int phy_package_write(struct phy_device *phydev,
+ u32 regnum, u16 val)
+{
+ struct phy_package_shared *shared = phydev->shared;
+
+ if (!shared)
+ return -EIO;
+
+ return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
+}
+
+static inline int __phy_package_write(struct phy_device *phydev,
+ u32 regnum, u16 val)
+{
+ struct phy_package_shared *shared = phydev->shared;
+
+ if (!shared)
+ return -EIO;
+
+ return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
+}
+
+static inline bool phy_package_init_once(struct phy_device *phydev)
+{
+ struct phy_package_shared *shared = phydev->shared;
+
+ if (!shared)
+ return false;
+
+ return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags);
+}
+
extern struct bus_type mdio_bus_type;
struct mdio_board_info {
--
2.34.1

View File

@ -0,0 +1,55 @@
From 3fc10755d5f6a5618519f2b5b3a68febfc5984b0 Mon Sep 17 00:00:00 2001
From: Sam Shih <sam.shih@mediatek.com>
Date: Fri, 2 Jun 2023 13:06:27 +0800
Subject: [PATCH]
[networking][999-2702-v5.9-net-phy-add-support-for-a-common-probe-between-shared-PHYs.patch]
---
include/linux/phy.h | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/include/linux/phy.h b/include/linux/phy.h
index d26dba255..4f2c105f5 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -232,7 +232,8 @@ struct phy_package_shared {
};
/* used as bit number in atomic bitops */
-#define PHY_SHARED_F_INIT_DONE 0
+#define PHY_SHARED_F_INIT_DONE 0
+#define PHY_SHARED_F_PROBE_DONE 1
/*
* The Bus class for PHYs. Devices which provide access to
@@ -1373,14 +1374,25 @@ static inline int __phy_package_write(struct phy_device *phydev,
return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
}
-static inline bool phy_package_init_once(struct phy_device *phydev)
+static inline bool __phy_package_set_once(struct phy_device *phydev,
+ unsigned int b)
{
struct phy_package_shared *shared = phydev->shared;
if (!shared)
return false;
- return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags);
+ return !test_and_set_bit(b, &shared->flags);
+}
+
+static inline bool phy_package_init_once(struct phy_device *phydev)
+{
+ return __phy_package_set_once(phydev, PHY_SHARED_F_INIT_DONE);
+}
+
+static inline bool phy_package_probe_once(struct phy_device *phydev)
+{
+ return __phy_package_set_once(phydev, PHY_SHARED_F_PROBE_DONE);
}
extern struct bus_type mdio_bus_type;
--
2.34.1