mirror of
https://github.com/hanwckf/immortalwrt-mt798x.git
synced 2025-01-07 01:53:34 +08:00
mediatek: update gpy211 driver
This commit is contained in:
parent
701919f949
commit
572c2ee8a2
@ -1,31 +1,107 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
struct t_phydev {
|
||||
// struct timer_list timer;
|
||||
struct phy_device *phydev;
|
||||
struct delayed_work dw;
|
||||
};
|
||||
|
||||
#define PHY_MIISTAT 0x18
|
||||
|
||||
#define PHY_MIISTAT_SPD_MASK GENMASK(2, 0)
|
||||
#define PHY_MIISTAT_DPX BIT(3)
|
||||
#define PHY_MIISTAT_LS BIT(10)
|
||||
|
||||
#define PHY_MIISTAT_SPD_10 0
|
||||
#define PHY_MIISTAT_SPD_100 1
|
||||
#define PHY_MIISTAT_SPD_1000 2
|
||||
#define PHY_MIISTAT_SPD_2500 4
|
||||
|
||||
void gpy211_status_timer(struct work_struct *t);
|
||||
|
||||
// Starder Magament Registers
|
||||
#define MDIO_MMD_STD 0x0
|
||||
#define VSPEC1_NBT_DS_CTRL 0xA
|
||||
#define DOWNSHIFT_THR_MASK GENMASK(6, 2)
|
||||
#define DOWNSHIFT_EN BIT(1)
|
||||
|
||||
#define DEFAULT_INTEL_GPY211_PHYID1_VALUE 0x67c9
|
||||
|
||||
#define MAXLINEAR_MAX_LED_INDEX 4
|
||||
|
||||
static int gpy211_phy_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int sgmii_reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, 8);
|
||||
|
||||
if (sgmii_reg != 0x24e2){
|
||||
phy_write_mmd(phydev, MDIO_MMD_VEND1, 8, 0x24e2);
|
||||
}
|
||||
phy_write_mmd(phydev, MDIO_MMD_VEND1, 1, 0x03f0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_RETRY_TIMES 80
|
||||
#define RETRY_INTERVAL 10 // unit is ms
|
||||
int gpy211_phy_probe(struct phy_device *phydev)
|
||||
{
|
||||
int sgmii_reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, 8);
|
||||
struct device_node *of_node = phydev->mdio.dev.of_node;
|
||||
u32 reg_value[MAXLINEAR_MAX_LED_INDEX] = {0};
|
||||
int ret;
|
||||
int i=0;
|
||||
u32 phyid1;
|
||||
int buf = 0;
|
||||
|
||||
/*
|
||||
* After reset signal to GPY211B1VC(SSTEP SLN8A), the chip may take 600 ms to bootup complete.
|
||||
* driver can successfully read/write the register after bootup complete.
|
||||
* If phy is ready, the STD_PHYID1(Register 0.2) should be 0x67c9.
|
||||
*/
|
||||
i = MAX_RETRY_TIMES;
|
||||
while (i) {
|
||||
phyid1 = phy_read_mmd(phydev, MDIO_MMD_STD, MDIO_DEVID1);
|
||||
if ( phyid1 == DEFAULT_INTEL_GPY211_PHYID1_VALUE )
|
||||
break;
|
||||
|
||||
msleep(RETRY_INTERVAL);
|
||||
i--;
|
||||
}
|
||||
if (!i) {
|
||||
phydev_err(phydev, "phy is not ready over %d ms!\n", (MAX_RETRY_TIMES-i)*10);
|
||||
}else {
|
||||
phydev_info(phydev, "driver wait %d ms for phy ready!\n", (MAX_RETRY_TIMES-i)*10);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(of_node, "maxlinear,led-reg", reg_value, MAXLINEAR_MAX_LED_INDEX);
|
||||
|
||||
if (ret < 0) {
|
||||
phydev_info(phydev, "not config \"maxlinear,led-reg\" parameter\n");
|
||||
} else {
|
||||
for(i=0;i<MAXLINEAR_MAX_LED_INDEX;i++) {
|
||||
phydev_dbg(phydev, "led-reg %d is %x.\n", i, reg_value[i]);
|
||||
phy_write_mmd(phydev, MDIO_MMD_VEND1, i+1, reg_value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* enable 2.5G SGMII rate adaption */
|
||||
phy_write_mmd(phydev, MDIO_MMD_VEND1, 8, 0x24e2);
|
||||
|
||||
buf = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_NBT_DS_CTRL);
|
||||
//enable downshift and set training counter threshold to 3
|
||||
phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_NBT_DS_CTRL, buf | FIELD_PREP(DOWNSHIFT_THR_MASK, 0x3) | DOWNSHIFT_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpy211_get_features(struct phy_device *phydev)
|
||||
{
|
||||
struct t_phydev *t_phy;
|
||||
t_phy = kzalloc(sizeof(*t_phy), GFP_KERNEL);
|
||||
t_phy->phydev = phydev;
|
||||
|
||||
int ret;
|
||||
|
||||
ret = genphy_read_abilities(phydev);
|
||||
@ -33,6 +109,7 @@ static int gpy211_get_features(struct phy_device *phydev)
|
||||
return ret;
|
||||
|
||||
/* GPY211 with rate adaption supports 100M/1G/2.5G speed. */
|
||||
|
||||
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
|
||||
phydev->supported);
|
||||
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
|
||||
@ -40,9 +117,76 @@ static int gpy211_get_features(struct phy_device *phydev)
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
|
||||
phydev->supported);
|
||||
|
||||
//set timer to query status
|
||||
// timer_setup(&t_phy->timer, gpy211_status_timer, 0);
|
||||
// mod_timer(&t_phy->timer, jiffies + 20*HZ);
|
||||
INIT_DELAYED_WORK(&t_phy->dw, gpy211_status_timer);
|
||||
schedule_delayed_work(&t_phy->dw, msecs_to_jiffies(2000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpy_read_status(struct phy_device *phydev)
|
||||
{
|
||||
int old_link = phydev->link;
|
||||
const char *speed;
|
||||
const char *duplex;
|
||||
|
||||
phydev_dbg(phydev, "### line[%d] addr[%d] link[%d] phyid[0x%x]\n", __LINE__, phydev->mdio.addr, phydev->link, phydev->phy_id);
|
||||
struct device_node *of_node = phydev->mdio.dev.of_node;
|
||||
|
||||
// int ret = phydev->mdio.bus->read(phydev->mdio.bus, phydev->mdio.addr, PHY_MIISTAT);
|
||||
int ret = phy_read_mmd(phydev, MDIO_MMD_STD, PHY_MIISTAT);
|
||||
|
||||
if(ret)
|
||||
{
|
||||
phydev_dbg(phydev, "### line[%d] addr[%d] val=[0x%x] link[%d] phyid[0x%x]\n", __LINE__, phydev->mdio.addr, ret, phydev->link, phydev->phy_id);
|
||||
phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
|
||||
phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
|
||||
duplex = (phydev->duplex == DUPLEX_FULL) ? "F" : "H";
|
||||
switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
|
||||
case PHY_MIISTAT_SPD_10:
|
||||
phydev->speed = SPEED_10;
|
||||
speed = "10";
|
||||
break;
|
||||
case PHY_MIISTAT_SPD_100:
|
||||
phydev->speed = SPEED_100;
|
||||
speed = "100";
|
||||
break;
|
||||
case PHY_MIISTAT_SPD_1000:
|
||||
phydev->speed = SPEED_1000;
|
||||
speed = "1000";
|
||||
break;
|
||||
case PHY_MIISTAT_SPD_2500:
|
||||
phydev->speed = SPEED_2500;
|
||||
speed = "2500";
|
||||
break;
|
||||
}
|
||||
|
||||
phydev_dbg(phydev, "### line[%d] addr[%d] old[%d] newlink[%d] \n", __LINE__, phydev->mdio.addr, old_link, phydev->link);
|
||||
|
||||
if(old_link != phydev->link)
|
||||
{
|
||||
if(phydev->link)
|
||||
phydev_info(phydev, "###phy_addr[%d] link up speed[%s]\n", phydev->mdio.addr, speed);
|
||||
else
|
||||
phydev_info(phydev, "###phy_addr[%d] link down \n", phydev->mdio.addr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpy211_status_timer(struct work_struct *t)
|
||||
{
|
||||
struct t_phydev *t_phy = container_of(t, struct t_phydev, dw.work);
|
||||
gpy_read_status(t_phy->phydev);
|
||||
|
||||
//trigger timer again
|
||||
// mod_timer(&t_phy->timer, jiffies + HZ);
|
||||
schedule_delayed_work(&t_phy->dw, msecs_to_jiffies(2000));
|
||||
}
|
||||
|
||||
static struct phy_driver gpy211_phy_driver[] = {
|
||||
{
|
||||
PHY_ID_MATCH_MODEL(0x67c9de0a),
|
||||
@ -50,6 +194,7 @@ static struct phy_driver gpy211_phy_driver[] = {
|
||||
.config_init = gpy211_phy_config_init,
|
||||
.probe = gpy211_phy_probe,
|
||||
.get_features = gpy211_get_features,
|
||||
.read_status = gpy_read_status,
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user