mirror of
https://github.com/hanwckf/immortalwrt-mt798x.git
synced 2025-01-05 00:53:32 +08:00
mediatek: add mt7981 internal gphy cal support
This commit is contained in:
parent
5ddcd81884
commit
aa2041c012
File diff suppressed because it is too large
Load Diff
452
target/linux/mediatek/files-5.4/drivers/net/phy/mtk-phy-lib.c
Normal file
452
target/linux/mediatek/files-5.4/drivers/net/phy/mtk-phy-lib.c
Normal 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");
|
119
target/linux/mediatek/files-5.4/drivers/net/phy/mtk.h
Normal file
119
target/linux/mediatek/files-5.4/drivers/net/phy/mtk.h
Normal 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_ */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user