 c06fb25d1f
			
		
	
	c06fb25d1f
	
	
		
			
	
		
	
	
		
			Some checks failed
		
		
	
	Build Kernel / Build all affected Kernels (push) Has been cancelled
				
			Build all core packages / Build all core packages for selected target (push) Has been cancelled
				
			Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
				
			Build Toolchains / Build Toolchains for each target (push) Has been cancelled
				
			Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
				
			Coverity scan build / Coverity x86/64 build (push) Has been cancelled
				
			
		
			
				
	
	
		
			1118 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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);
 |