0
0
mirror of https://git.openwrt.org/openwrt/openwrt.git synced 2024-11-25 14:36:14 +00:00
openwrt/target/linux/mediatek/files/drivers/net/phy/en8801sc.c
Robert Marko 2dba4308d1
mediatek: add Airoha EN8801SC PHY driver
Airoha EN8801SC PHY is a gigabit PHY used on Edgecore EAP111 so, include
the MTK driver with some cleanups.

Unfortunatelly, there is no specification sheet nor datasheet available
in order to demistify the magic PBUS writes and work on upstreaming
this driver.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
2024-04-11 13:10:15 +02:00

1118 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* FILE NAME: en8801sc.c
* PURPOSE:
* EN8801SC phy driver for Linux
* NOTES:
*
*/
/* INCLUDE FILE DECLARATIONS
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include "en8801sc.h"
MODULE_DESCRIPTION("Airoha EN8801S PHY drivers for MediaTek SoC");
MODULE_AUTHOR("Airoha");
MODULE_LICENSE("GPL");
#define airoha_mdio_lock(bus) mutex_lock(&((bus)->mdio_lock))
#define airoha_mdio_unlock(bus) mutex_unlock(&((bus)->mdio_lock))
#define phydev_mdio_bus(_dev) (_dev->mdio.bus)
#define phydev_phy_addr(_dev) (_dev->mdio.addr)
#define phydev_dev(_dev) (&_dev->mdio.dev)
#define phydev_pbus_addr(dev) ((dev)->mdio.addr + 1)
enum {
PHY_STATE_DONE = 0,
PHY_STATE_INIT = 1,
PHY_STATE_PROCESS = 2,
PHY_STATE_FAIL = 3,
};
struct en8801s_priv {
bool first_init;
u16 count;
u16 pro_version;
};
/*
The following led_cfg example is for reference only.
LED5 1000M/LINK/ACT (GPIO5) <-> BASE_T_LED0,
LED6 10/100M/LINK/ACT (GPIO9) <-> BASE_T_LED1,
LED4 100M/LINK/ACT (GPIO8) <-> BASE_T_LED2,
*/
/* User-defined.B */
#define AIR_LED_SUPPORT
#ifdef AIR_LED_SUPPORT
static const struct AIR_BASE_T_LED_CFG_S led_cfg[4] = {
/*
* {LED Enable, GPIO, LED Polarity, LED ON, LED Blink}
*/
/* BASE-T LED0 */
{LED_ENABLE, 5, AIR_ACTIVE_LOW,
BASE_T_LED0_ON_CFG, BASE_T_LED0_BLK_CFG},
/* BASE-T LED1 */
{LED_ENABLE, 9, AIR_ACTIVE_LOW,
BASE_T_LED1_ON_CFG, BASE_T_LED1_BLK_CFG},
/* BASE-T LED2 */
{LED_ENABLE, 8, AIR_ACTIVE_LOW,
BASE_T_LED2_ON_CFG, BASE_T_LED2_BLK_CFG},
/* BASE-T LED3 */
{LED_DISABLE, 1, AIR_ACTIVE_LOW,
BASE_T_LED3_ON_CFG, BASE_T_LED3_BLK_CFG},
};
static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
#endif
/* User-defined.E */
/************************************************************************
* F U N C T I O N S
************************************************************************/
static int en8801s_phase2_init(struct phy_device *phydev);
static int __airoha_cl45_write(struct mii_bus *bus, int port,
u32 devad, u32 reg, u16 val)
{
int ret = 0;
struct device *dev = &bus->dev;
ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad);
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return ret;
}
ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg);
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return ret;
}
ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG,
MMD_OP_MODE_DATA | devad);
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return ret;
}
ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, val);
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return ret;
}
return ret;
}
static int __airoha_cl45_read(struct mii_bus *bus, int port,
u32 devad, u32 reg, u16 *read_data)
{
int ret = 0;
struct device *dev = &bus->dev;
ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad);
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return ret;
}
ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg);
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return ret;
}
ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG,
MMD_OP_MODE_DATA | devad);
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return ret;
}
*read_data = __mdiobus_read(bus, port, MII_MMD_ADDR_DATA_REG);
return ret;
}
static int airoha_cl45_write(struct mii_bus *bus, int port,
u32 devad, u32 reg, u16 val)
{
int ret = 0;
airoha_mdio_lock(bus);
ret = __airoha_cl45_write(bus, port, devad, reg, val);
airoha_mdio_unlock(bus);
return ret;
}
static int airoha_cl45_read(struct mii_bus *bus, int port,
u32 devad, u32 reg, u16 *read_data)
{
int ret = 0;
airoha_mdio_lock(bus);
ret = __airoha_cl45_read(bus, port, devad, reg, read_data);
airoha_mdio_unlock(bus);
return ret;
}
static int __airoha_pbus_write(struct mii_bus *ebus, int pbus_id,
unsigned long pbus_address, unsigned long pbus_data)
{
int ret = 0;
ret = __mdiobus_write(ebus, pbus_id, 0x1F,
(unsigned int)(pbus_address >> 6));
if (ret < 0)
return ret;
ret = __mdiobus_write(ebus, pbus_id,
(unsigned int)((pbus_address >> 2) & 0xf),
(unsigned int)(pbus_data & 0xFFFF));
if (ret < 0)
return ret;
ret = __mdiobus_write(ebus, pbus_id, 0x10,
(unsigned int)(pbus_data >> 16));
if (ret < 0)
return ret;
return ret;
}
static unsigned long __airoha_pbus_read(struct mii_bus *ebus, int pbus_id,
unsigned long pbus_address)
{
unsigned long pbus_data;
unsigned int pbus_data_low, pbus_data_high;
int ret = 0;
struct device *dev = &ebus->dev;
ret = __mdiobus_write(ebus, pbus_id, 0x1F,
(unsigned int)(pbus_address >> 6));
if (ret < 0) {
dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
return INVALID_DATA;
}
pbus_data_low = __mdiobus_read(ebus, pbus_id,
(unsigned int)((pbus_address >> 2) & 0xf));
pbus_data_high = __mdiobus_read(ebus, pbus_id, 0x10);
pbus_data = (pbus_data_high << 16) + pbus_data_low;
return pbus_data;
}
static int airoha_pbus_write(struct mii_bus *ebus, int pbus_id,
unsigned long pbus_address, unsigned long pbus_data)
{
int ret = 0;
airoha_mdio_lock(ebus);
ret = __airoha_pbus_write(ebus, pbus_id, pbus_address, pbus_data);
airoha_mdio_unlock(ebus);
return ret;
}
static unsigned long airoha_pbus_read(struct mii_bus *ebus, int pbus_id,
unsigned long pbus_address)
{
unsigned long pbus_data;
airoha_mdio_lock(ebus);
pbus_data = __airoha_pbus_read(ebus, pbus_id, pbus_address);
airoha_mdio_unlock(ebus);
return pbus_data;
}
/* Airoha Token Ring Write function */
static int airoha_tr_reg_write(struct phy_device *phydev,
unsigned long tr_address, unsigned long tr_data)
{
int ret = 0;
int phy_addr = phydev_phy_addr(phydev);
struct mii_bus *ebus = phydev_mdio_bus(phydev);
airoha_mdio_lock(ebus);
ret = __mdiobus_write(ebus, phy_addr, 0x1F, 0x52b5); /* page select */
ret = __mdiobus_write(ebus, phy_addr, 0x11,
(unsigned int)(tr_data & 0xffff));
ret = __mdiobus_write(ebus, phy_addr, 0x12,
(unsigned int)(tr_data >> 16));
ret = __mdiobus_write(ebus, phy_addr, 0x10,
(unsigned int)(tr_address | TrReg_WR));
ret = __mdiobus_write(ebus, phy_addr, 0x1F, 0x0); /* page resetore */
airoha_mdio_unlock(ebus);
return ret;
}
#ifdef AIR_LED_SUPPORT
static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity,
int polar, u16 on_evt, u16 blk_evt)
{
int ret = 0;
int phy_addr = phydev_phy_addr(phydev);
struct mii_bus *mbus = phydev_mdio_bus(phydev);
if (polar == AIR_ACTIVE_HIGH)
on_evt |= LED_ON_POL;
else
on_evt &= ~LED_ON_POL;
ret = airoha_cl45_write(mbus, phy_addr, 0x1f,
LED_ON_CTRL(entity), on_evt | LED_ON_EN);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1f,
LED_BLK_CTRL(entity), blk_evt);
if (ret < 0)
return ret;
return 0;
}
static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
{
u16 cl45_data;
int err = 0;
int phy_addr = phydev_phy_addr(phydev);
struct mii_bus *mbus = phydev_mdio_bus(phydev);
err = airoha_cl45_read(mbus, phy_addr, 0x1f, LED_BCR, &cl45_data);
if (err < 0)
return err;
switch (mode) {
case AIR_LED_MODE_DISABLE:
cl45_data &= ~LED_BCR_EXT_CTRL;
cl45_data &= ~LED_BCR_MODE_MASK;
cl45_data |= LED_BCR_MODE_DISABLE;
break;
case AIR_LED_MODE_USER_DEFINE:
cl45_data |= LED_BCR_EXT_CTRL;
cl45_data |= LED_BCR_CLK_EN;
break;
default:
return -EINVAL;
}
err = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_BCR, cl45_data);
if (err < 0)
return err;
return 0;
}
static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
{
u16 cl45_data;
int err;
int phy_addr = phydev_phy_addr(phydev);
struct mii_bus *mbus = phydev_mdio_bus(phydev);
err = airoha_cl45_read(mbus, phy_addr, 0x1f,
LED_ON_CTRL(entity), &cl45_data);
if (err < 0)
return err;
if (state == LED_ENABLE)
cl45_data |= LED_ON_EN;
else
cl45_data &= ~LED_ON_EN;
err = airoha_cl45_write(mbus, phy_addr, 0x1f,
LED_ON_CTRL(entity), cl45_data);
if (err < 0)
return err;
return 0;
}
static int en8801s_led_init(struct phy_device *phydev)
{
unsigned long led_gpio = 0, reg_value = 0;
int ret = 0, led_id;
struct mii_bus *mbus = phydev_mdio_bus(phydev);
int gpio_led_rg[3] = {0x1870, 0x1874, 0x1878};
u16 cl45_data = led_dur;
struct device *dev = phydev_dev(phydev);
int phy_addr = phydev_phy_addr(phydev);
int pbus_addr = phydev_pbus_addr(phydev);
ret = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_BLK_DUR, cl45_data);
if (ret < 0)
return ret;
cl45_data >>= 1;
ret = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_ON_DUR, cl45_data);
if (ret < 0)
return ret;
ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
if (ret != 0) {
dev_err(dev, "LED fail to set mode, ret %d !\n", ret);
return ret;
}
for (led_id = 0; led_id < EN8801S_LED_COUNT; led_id++) {
reg_value = 0;
ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en);
if (ret != 0) {
dev_err(dev, "LED fail to set state, ret %d !\n", ret);
return ret;
}
if (led_cfg[led_id].en == LED_ENABLE) {
if ((led_cfg[led_id].gpio < 0)
|| led_cfg[led_id].gpio > 9) {
dev_err(dev, "GPIO%d is out of range!! GPIO number is 0~9.\n",
led_cfg[led_id].gpio);
return -EIO;
}
led_gpio |= BIT(led_cfg[led_id].gpio);
reg_value = airoha_pbus_read(mbus, pbus_addr,
gpio_led_rg[led_cfg[led_id].gpio / 4]);
LED_SET_GPIO_SEL(led_cfg[led_id].gpio,
led_id, reg_value);
dev_dbg(dev, "[Airoha] gpio%d, reg_value 0x%lx\n",
led_cfg[led_id].gpio, reg_value);
ret = airoha_pbus_write(mbus, pbus_addr,
gpio_led_rg[led_cfg[led_id].gpio / 4],
reg_value);
if (ret < 0)
return ret;
ret = airoha_led_set_usr_def(phydev, led_id,
led_cfg[led_id].pol,
led_cfg[led_id].on_cfg,
led_cfg[led_id].blk_cfg);
if (ret != 0) {
dev_err(dev, "LED fail to set usr def, ret %d !\n",
ret);
return ret;
}
}
}
reg_value = (airoha_pbus_read(mbus, pbus_addr, 0x1880) & ~led_gpio);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1880, reg_value);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x186c, led_gpio);
if (ret < 0)
return ret;
dev_info(dev, "LED initialize OK !\n");
return 0;
}
#endif
static int en8801s_phy_process(struct phy_device *phydev)
{
struct mii_bus *mbus = phydev_mdio_bus(phydev);
unsigned long reg_value = 0;
int ret = 0;
int pbus_addr = phydev_pbus_addr(phydev);
reg_value = airoha_pbus_read(mbus, pbus_addr, 0x19e0);
reg_value |= BIT(0);
ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, reg_value);
if (ret < 0)
return ret;
reg_value = airoha_pbus_read(mbus, pbus_addr, 0x19e0);
reg_value &= ~BIT(0);
ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, reg_value);
if (ret < 0)
return ret;
return ret;
}
static int en8801s_phase1_init(struct phy_device *phydev)
{
unsigned long pbus_data;
int pbus_addr = EN8801S_PBUS_DEFAULT_ADDR;
u16 reg_value;
int retry, ret = 0;
struct mii_bus *mbus = phydev_mdio_bus(phydev);
struct device *dev = phydev_dev(phydev);
struct en8801s_priv *priv = phydev->priv;
priv->count = 1;
msleep(1000);
retry = MAX_OUI_CHECK;
while (1) {
pbus_data = airoha_pbus_read(mbus, pbus_addr,
EN8801S_RG_ETHER_PHY_OUI); /* PHY OUI */
if (pbus_data == EN8801S_PBUS_OUI) {
dev_info(dev, "PBUS addr 0x%x: Start initialized.\n",
pbus_addr);
break;
}
pbus_addr = phydev_pbus_addr(phydev);
if (0 == --retry) {
dev_err(dev, "Probe fail !\n");
return 0;
}
}
ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_BUCK_CTL, 0x03);
if (ret < 0)
return ret;
pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_PROD_VER);
priv->pro_version = pbus_data & 0xf;
dev_info(dev, "EN8801S Procduct Version :E%d\n", priv->pro_version);
mdelay(10);
pbus_data = (airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_LTR_CTL)
& 0xfffffffc) | BIT(2);
ret = airoha_pbus_write(mbus, pbus_addr,
EN8801S_RG_LTR_CTL, pbus_data);
if (ret < 0)
return ret;
mdelay(500);
pbus_data = (pbus_data & ~BIT(2)) |
EN8801S_RX_POLARITY_NORMAL |
EN8801S_TX_POLARITY_NORMAL;
ret = airoha_pbus_write(mbus, pbus_addr,
EN8801S_RG_LTR_CTL, pbus_data);
if (ret < 0)
return ret;
mdelay(500);
if (priv->pro_version == 4) {
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1900);
dev_dbg(dev, "Before 0x1900 0x%lx\n", pbus_data);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1900, 0x101009f);
if (ret < 0)
return ret;
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1900);
dev_dbg(dev, "After 0x1900 0x%lx\n", pbus_data);
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19a8);
dev_dbg(dev, "Before 19a8 0x%lx\n", pbus_data);
ret = airoha_pbus_write(mbus, pbus_addr,
0x19a8, pbus_data & ~BIT(16));
if (ret < 0)
return ret;
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19a8);
dev_dbg(dev, "After 19a8 0x%lx\n", pbus_data);
}
pbus_data = airoha_pbus_read(mbus, pbus_addr,
EN8801S_RG_SMI_ADDR); /* SMI ADDR */
pbus_data = (pbus_data & 0xffff0000) |
(unsigned long)(phydev_pbus_addr(phydev) << 8) |
(unsigned long)(phydev_phy_addr(phydev));
dev_info(phydev_dev(phydev), "SMI_ADDR=%lx (renew)\n", pbus_data);
ret = airoha_pbus_write(mbus, pbus_addr,
EN8801S_RG_SMI_ADDR, pbus_data);
mdelay(10);
retry = MAX_RETRY;
while (1) {
mdelay(10);
reg_value = phy_read(phydev, MII_PHYSID2);
if (reg_value == EN8801S_PHY_ID2)
break; /* wait GPHY ready */
retry--;
if (retry == 0) {
dev_err(dev, "Initialize fail !\n");
return 0;
}
}
/* Software Reset PHY */
reg_value = phy_read(phydev, MII_BMCR);
reg_value |= BMCR_RESET;
ret = phy_write(phydev, MII_BMCR, reg_value);
if (ret < 0)
return ret;
retry = MAX_RETRY;
do {
mdelay(10);
reg_value = phy_read(phydev, MII_BMCR);
retry--;
if (retry == 0) {
dev_err(dev, "Reset fail !\n");
return 0;
}
} while (reg_value & BMCR_RESET);
phydev->dev_flags = PHY_STATE_INIT;
dev_info(dev, "Phase1 initialize OK ! (%s)\n", EN8801S_DRIVER_VERSION);
if (priv->pro_version == 4) {
ret = en8801s_phase2_init(phydev);
if (ret != 0) {
dev_info(dev, "en8801_phase2_init failed\n");
phydev->dev_flags = PHY_STATE_FAIL;
return 0;
}
phydev->dev_flags = PHY_STATE_PROCESS;
}
return 0;
}
static int en8801s_phase2_init(struct phy_device *phydev)
{
union gephy_all_REG_LpiReg1Ch GPHY_RG_LPI_1C;
union gephy_all_REG_dev1Eh_reg324h GPHY_RG_1E_324;
union gephy_all_REG_dev1Eh_reg012h GPHY_RG_1E_012;
union gephy_all_REG_dev1Eh_reg017h GPHY_RG_1E_017;
unsigned long pbus_data;
int phy_addr = phydev_phy_addr(phydev);
int pbus_addr = phydev_pbus_addr(phydev);
u16 cl45_value;
int retry, ret = 0;
struct mii_bus *mbus = phydev_mdio_bus(phydev);
struct device *dev = phydev_dev(phydev);
struct en8801s_priv *priv = phydev->priv;
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1690);
pbus_data |= BIT(31);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1690, pbus_data);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD801);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0, 0x9140);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0003);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
if (ret < 0)
return ret;
/* Set FCM control */
ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x004b);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x142c, 0x05050505);
if (ret < 0)
return ret;
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1440);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1440, pbus_data & ~BIT(11));
if (ret < 0)
return ret;
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1408);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1408, pbus_data | BIT(5));
if (ret < 0)
return ret;
/* Set GPHY Perfomance*/
/* Token Ring */
ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_15h, 0x0055A0);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_17h, 0x07FF3F);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_PMA_00h, 0x00001E);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_PMA_01h, 0x6FB90A);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_PMA_17h, 0x060671);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_PMA_18h, 0x0E2F00);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_TR_26h, 0x444444);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_03h, 0x000000);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_06h, 0x2EBAEF);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_08h, 0x00000B);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Ch, 0x00504D);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Dh, 0x02314F);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Fh, 0x003028);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_10h, 0x005010);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_11h, 0x040001);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_13h, 0x018670);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_14h, 0x00024A);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Bh, 0x000072);
if (ret < 0)
return ret;
ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Ch, 0x003210);
if (ret < 0)
return ret;
/* CL22 & CL45 */
ret = phy_write(phydev, 0x1f, 0x03);
if (ret < 0)
return ret;
GPHY_RG_LPI_1C.DATA = phy_read(phydev, RgAddr_LPI_1Ch);
GPHY_RG_LPI_1C.DataBitField.smi_deton_th = 0x0C;
ret = phy_write(phydev, RgAddr_LPI_1Ch, GPHY_RG_LPI_1C.DATA);
if (ret < 0)
return ret;
ret = phy_write(phydev, RgAddr_LPI_1Ch, 0xC92);
if (ret < 0)
return ret;
ret = phy_write(phydev, RgAddr_AUXILIARY_1Dh, 0x1);
if (ret < 0)
return ret;
ret = phy_write(phydev, 0x1f, 0x0);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x120, 0x8014);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x122, 0xffff);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x123, 0xffff);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x144, 0x0200);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x14A, 0xEE20);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x189, 0x0110);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x19B, 0x0111);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x234, 0x0181);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x238, 0x0120);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x239, 0x0117);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x268, 0x07F4);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x2D1, 0x0733);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x323, 0x0011);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324, 0x013F);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x326, 0x0037);
if (ret < 0)
return ret;
ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x324, &cl45_value);
if (ret < 0)
return ret;
GPHY_RG_1E_324.DATA = cl45_value;
GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = 0;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324,
GPHY_RG_1E_324.DATA);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x19E, 0xC2);
if (ret < 0)
return ret;
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x013, 0x0);
if (ret < 0)
return ret;
/* EFUSE */
airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40000040);
retry = MAX_RETRY;
while (retry != 0) {
mdelay(1);
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C08);
if ((pbus_data & BIT(30)) == 0)
break;
retry--;
}
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C38); /* RAW#2 */
ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x12, &cl45_value);
if (ret < 0)
return ret;
GPHY_RG_1E_012.DATA = cl45_value;
GPHY_RG_1E_012.DataBitField.da_tx_i2mpb_a_tbt =
(u16)(pbus_data & 0x03f);
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x12,
GPHY_RG_1E_012.DATA);
if (ret < 0)
return ret;
ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x17, &cl45_value);
if (ret < 0)
return ret;
GPHY_RG_1E_017.DATA = cl45_value;
GPHY_RG_1E_017.DataBitField.da_tx_i2mpb_b_tbt =
(u16)((pbus_data >> 8) & 0x03f);
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x17,
GPHY_RG_1E_017.DATA);
if (ret < 0)
return ret;
airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40400040);
retry = MAX_RETRY;
while (retry != 0) {
mdelay(1);
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C08);
if ((pbus_data & BIT(30)) == 0)
break;
retry--;
}
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C30); /* RAW#16 */
GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off =
(u16)((pbus_data >> 12) & 0x01);
ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324,
GPHY_RG_1E_324.DATA);
if (ret < 0)
return ret;
#ifdef AIR_LED_SUPPORT
ret = en8801s_led_init(phydev);
if (ret != 0)
dev_err(dev, "en8801s_led_init fail (ret:%d) !\n", ret);
#endif
ret = airoha_cl45_read(mbus, phy_addr, MDIO_MMD_AN,
MDIO_AN_EEE_ADV, &cl45_value);
if (ret < 0)
return ret;
if (cl45_value == 0) {
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1960);
if (0xA == ((pbus_data & 0x07c00000) >> 22)) {
pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
if (ret < 0)
return ret;
mdelay(10);
pbus_data = (pbus_data & 0xf83fffff) | (0xE << 22);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
if (ret < 0)
return ret;
mdelay(10);
}
} else {
pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1960);
if (0xE == ((pbus_data & 0x07c00000) >> 22)) {
pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
if (ret < 0)
return ret;
mdelay(10);
pbus_data = (pbus_data & 0xf83fffff) | (0xA << 22);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
if (ret < 0)
return ret;
mdelay(10);
}
}
priv->first_init = false;
dev_info(phydev_dev(phydev), "Phase2 initialize OK !\n");
return 0;
}
static int en8801s_read_status(struct phy_device *phydev)
{
int ret = 0, preSpeed = phydev->speed;
struct mii_bus *mbus = phydev_mdio_bus(phydev);
u32 reg_value;
struct device *dev = phydev_dev(phydev);
int pbus_addr = phydev_pbus_addr(phydev);
struct en8801s_priv *priv = phydev->priv;
ret = genphy_read_status(phydev);
if (phydev->link == LINK_DOWN)
preSpeed = phydev->speed = 0;
if (phydev->dev_flags == PHY_STATE_PROCESS) {
en8801s_phy_process(phydev);
phydev->dev_flags = PHY_STATE_DONE;
}
if (phydev->dev_flags == PHY_STATE_INIT) {
dev_dbg(dev, "phydev->link %d, count %d\n",
phydev->link, priv->count);
if ((phydev->link) || (priv->count == 5)) {
if (priv->pro_version != 4) {
ret = en8801s_phase2_init(phydev);
if (ret != 0) {
dev_info(dev, "en8801_phase2_init failed\n");
phydev->dev_flags = PHY_STATE_FAIL;
return 0;
}
phydev->dev_flags = PHY_STATE_PROCESS;
}
}
priv->count++;
}
if ((preSpeed != phydev->speed) && (phydev->link == LINK_UP)) {
preSpeed = phydev->speed;
if (preSpeed == SPEED_10) {
reg_value = airoha_pbus_read(mbus, pbus_addr, 0x1694);
reg_value |= BIT(31);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1694,
reg_value);
if (ret < 0)
return ret;
phydev->dev_flags = PHY_STATE_PROCESS;
} else {
reg_value = airoha_pbus_read(mbus, pbus_addr, 0x1694);
reg_value &= ~BIT(31);
ret = airoha_pbus_write(mbus, pbus_addr, 0x1694,
reg_value);
if (ret < 0)
return ret;
phydev->dev_flags = PHY_STATE_PROCESS;
}
airoha_pbus_write(mbus, pbus_addr, 0x0600,
0x0c000c00);
if (preSpeed == SPEED_1000) {
dev_dbg(dev, "SPEED_1000\n");
ret = airoha_pbus_write(mbus, pbus_addr, 0x10,
0xD801);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0,
0x9140);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14,
0x0003);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0600,
0x0c000c00);
if (ret < 0)
return ret;
mdelay(2); /* delay 2 ms */
ret = airoha_pbus_write(mbus, pbus_addr, 0x1404,
0x004b);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x140c,
0x0007);
if (ret < 0)
return ret;
} else if (preSpeed == SPEED_100) {
dev_dbg(dev, "SPEED_100\n");
ret = airoha_pbus_write(mbus, pbus_addr, 0x10,
0xD401);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0,
0x9140);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14,
0x0007);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0600,
0x0c11);
if (ret < 0)
return ret;
mdelay(2); /* delay 2 ms */
ret = airoha_pbus_write(mbus, pbus_addr, 0x1404,
0x0027);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x140c,
0x0007);
if (ret < 0)
return ret;
} else if (preSpeed == SPEED_10) {
dev_dbg(dev, "SPEED_10\n");
ret = airoha_pbus_write(mbus, pbus_addr, 0x10,
0xD001);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0,
0x9140);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14,
0x000b);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x0600,
0x0c11);
if (ret < 0)
return ret;
mdelay(2); /* delay 2 ms */
ret = airoha_pbus_write(mbus, pbus_addr, 0x1404,
0x0027);
if (ret < 0)
return ret;
ret = airoha_pbus_write(mbus, pbus_addr, 0x140c,
0x0007);
if (ret < 0)
return ret;
}
}
return ret;
}
static int en8801s_probe(struct phy_device *phydev)
{
struct en8801s_priv *priv;
unsigned long phy_addr = phydev_phy_addr(phydev);
struct mdio_device *mdiodev = &phydev->mdio;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->count = 0;
priv->first_init = true;
if (mdiodev->reset_gpio) {
dev_dbg(phydev_dev(phydev),
"Assert PHY %lx HWRST until phy_init_hw\n",
phy_addr);
phy_device_reset(phydev, 1);
}
phydev->priv = priv;
return 0;
}
static int airoha_mmd_read(struct phy_device *phydev,
int devad, u16 reg)
{
struct mii_bus *mbus = phydev_mdio_bus(phydev);
int phy_addr = phydev_phy_addr(phydev);
int ret = 0;
u16 cl45_value;
ret = __airoha_cl45_read(mbus, phy_addr, devad, reg, &cl45_value);
if (ret < 0)
return ret;
return cl45_value;
}
static int airoha_mmd_write(struct phy_device *phydev,
int devad, u16 reg, u16 val)
{
struct mii_bus *mbus = phydev_mdio_bus(phydev);
int phy_addr = phydev_phy_addr(phydev);
int pbus_addr = phydev_pbus_addr(phydev);
unsigned long pbus_data;
int ret = 0;
if (MDIO_MMD_AN == devad && MDIO_AN_EEE_ADV == reg) {
if (val == 0) {
pbus_data = __airoha_pbus_read(mbus, pbus_addr, 0x1960);
if (0xA == ((pbus_data & 0x07c00000) >> 22)) {
pbus_data = (pbus_data & 0xf83fffff) |
(0xC << 22);
__airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
mdelay(10);
pbus_data = (pbus_data & 0xf83fffff) |
(0xE << 22);
__airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
mdelay(10);
}
} else {
pbus_data = __airoha_pbus_read(mbus, pbus_addr, 0x1960);
if (0xE == ((pbus_data & 0x07c00000) >> 22)) {
pbus_data = (pbus_data & 0xf83fffff) |
(0xC << 22);
__airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
mdelay(10);
pbus_data = (pbus_data & 0xf83fffff) |
(0xA << 22);
__airoha_pbus_write(mbus, pbus_addr, 0x1960,
pbus_data);
mdelay(10);
}
}
}
ret = __airoha_cl45_write(mbus, phy_addr, devad, reg, val);
if (ret < 0)
return ret;
return 0;
}
static struct phy_driver Airoha_driver[] = {
{
.phy_id = EN8801SC_PHY_ID,
.name = "Airoha EN8801SC",
.phy_id_mask = 0x0ffffff0,
.features = PHY_GBIT_FEATURES,
.probe = en8801s_probe,
.config_init = en8801s_phase1_init,
.config_aneg = genphy_config_aneg,
.read_status = en8801s_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
.read_mmd = airoha_mmd_read,
.write_mmd = airoha_mmd_write,
}
};
module_phy_driver(Airoha_driver);
static struct mdio_device_id __maybe_unused Airoha_tbl[] = {
{ EN8801SC_PHY_ID, 0x0ffffff0 },
{ }
};
MODULE_DEVICE_TABLE(mdio, Airoha_tbl);