// SPDX-License-Identifier: GPL-2.0+
/*************************************************
 * FILE NAME:  air_en8811h_api.c
 * PURPOSE:
 *      EN8811H PHY Driver for Linux
 * NOTES:
 *
 *  Copyright (C) 2023 Airoha Technology Corp.
 *************************************************/

/* INCLUDE FILE DECLARATIONS
*/
#include <linux/uaccess.h>
#include <linux/trace_seq.h>
#include <linux/seq_file.h>
#include <linux/u64_stats_sync.h>
#include <linux/dma-mapping.h>
#include <linux/netdevice.h>
#include <linux/ctype.h>
#include <linux/of_mdio.h>
#include <linux/of_address.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/debugfs.h>

#include "air_en8811h.h"
#include "air_en8811h_api.h"

/*
struct air_phy_debug {
	struct dentry *root;
};
struct air_phy_debug air_debug;
*/

static const char * const tx_rx_string[32] = {
	"Tx Reverse, Rx Normal",
	"Tx Normal, Rx Normal",
	"Tx Reverse, Rx Reverse",
	"Tx Normal, Rx Reverse",
};

/* Airoha MII read function */
static int __air_mii_cl22_read(struct mii_bus *ebus,
					int addr, unsigned int phy_register)
{
	int read_data;
#if (KERNEL_VERSION(4, 16, 0) < LINUX_VERSION_CODE)
	read_data = __mdiobus_read(ebus, addr, phy_register);
#else
	read_data = ebus->read(ebus, addr, phy_register);
#endif
	return read_data;
}
/* Airoha MII write function */
static int __air_mii_cl22_write(struct mii_bus *ebus, int addr,
			unsigned int phy_register, unsigned int write_data)
{
	int ret = 0;
#if (KERNEL_VERSION(4, 16, 0) < LINUX_VERSION_CODE)
	ret = __mdiobus_write(ebus, addr, phy_register, write_data);
#else
	ret = ebus->write(ebus, addr, phy_register, write_data);
#endif
	return ret;
}

/* Airoha MII read function */
int air_mii_cl22_read(struct mii_bus *ebus, int addr, unsigned int phy_register)
{
	int read_data;

	mutex_lock(&ebus->mdio_lock);
	read_data = __air_mii_cl22_read(ebus, addr, phy_register);
	mutex_unlock(&ebus->mdio_lock);
	return read_data;
}
/* Airoha MII write function */
int air_mii_cl22_write(struct mii_bus *ebus, int addr,
			unsigned int phy_register, unsigned int write_data)
{
	int ret = 0;

	mutex_lock(&ebus->mdio_lock);
	ret = __air_mii_cl22_write(ebus, addr, phy_register, write_data);
	mutex_unlock(&ebus->mdio_lock);
	return ret;
}

int __air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
{
	int ret = 0;
	int data;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ACC_CTL_REG, devad);
	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ADDR_DATA_REG, reg);
	ret |= __air_mii_cl22_write(mbus, addr,
			MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
	if (ret) {
		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
		return INVALID_DATA;
	}
	data = __air_mii_cl22_read(mbus, addr, MII_MMD_ADDR_DATA_REG);
	return data;
}

int __air_mii_cl45_write(struct phy_device *phydev,
					int devad, u16 reg, u16 write_data)
{
	int ret = 0;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ACC_CTL_REG, devad);
	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ADDR_DATA_REG, reg);
	ret |= __air_mii_cl22_write(mbus, addr,
		MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
	ret |= __air_mii_cl22_write(mbus, addr,
			MII_MMD_ADDR_DATA_REG, write_data);
	if (ret) {
		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
		return ret;
	}
	return 0;
}

int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
{
	int data;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);

	mutex_lock(&mbus->mdio_lock);
	data = __air_mii_cl45_read(phydev, devad, reg);
	mutex_unlock(&mbus->mdio_lock);
	if (data == INVALID_DATA) {
		dev_err(dev, "__airoha_cl45_read fail\n");
		return INVALID_DATA;
	}
	return data;
}

int air_mii_cl45_write(struct phy_device *phydev,
				int devad, u16 reg, u16 write_data)
{
	int ret = 0;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);

	mutex_lock(&mbus->mdio_lock);
	ret |= __air_mii_cl45_write(phydev, devad, reg, write_data);
	mutex_unlock(&mbus->mdio_lock);
	if (ret) {
		dev_err(dev, "__airoha_cl45_write, ret: %d\n", ret);
		return ret;
	}
	return ret;
}

/* EN8811H PBUS read function */
static unsigned int __air_pbus_reg_read(struct phy_device *phydev,
		unsigned int pbus_address)
{
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);
	struct device *dev = phydev_dev(phydev);
	unsigned int pbus_data_low, pbus_data_high;
	unsigned int pbus_data;
	int ret = 0;

	ret |= __air_mii_cl22_write(mbus, (addr + 8),
			0x1F, (pbus_address >> 6));
	pbus_data_low = __air_mii_cl22_read(mbus, (addr + 8),
				((pbus_address >> 2) & 0xf));
	pbus_data_high = __air_mii_cl22_read(mbus, (addr + 8), 0x10);
	pbus_data = (pbus_data_high << 16) + pbus_data_low;
	if (ret) {
		dev_err(dev, "%s: ret: %d\n", __func__, ret);
		return ret;
	}
	return pbus_data;
}

unsigned int air_pbus_reg_read(struct phy_device *phydev,
		unsigned int pbus_address)
{
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	struct device *dev = phydev_dev(phydev);
	int ret = 0;
	unsigned int data;

	mutex_lock(&mbus->mdio_lock);
	data = __air_pbus_reg_read(phydev, pbus_address);
	mutex_unlock(&mbus->mdio_lock);
	if (ret) {
		dev_err(dev, "%s: ret: %d\n", __func__, ret);
		return ret;
	}
	return data;
}

/* EN8811H PBUS write function */
static int __air_pbus_reg_write(struct phy_device *phydev,
		unsigned int pbus_address, unsigned long pbus_data)
{
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);
	struct device *dev = phydev_dev(phydev);
	int ret = 0;

	ret |= __air_mii_cl22_write(mbus, (addr + 8),
			0x1F, (pbus_address >> 6));
	ret |= __air_mii_cl22_write(mbus, (addr + 8),
			((pbus_address >> 2) & 0xf), (pbus_data & 0xFFFF));
	ret |= __air_mii_cl22_write(mbus, (addr + 8),
			0x10, (pbus_data >> 16));
	if (ret) {
		dev_err(dev, "%s: ret: %d\n", __func__, ret);
		return ret;
	}
	return 0;
}

int air_pbus_reg_write(struct phy_device *phydev,
		unsigned int pbus_address, unsigned int pbus_data)
{
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	struct device *dev = phydev_dev(phydev);
	int ret = 0;

	mutex_lock(&mbus->mdio_lock);
	ret |= __air_pbus_reg_write(phydev, pbus_address, pbus_data);
	mutex_unlock(&mbus->mdio_lock);
	if (ret) {
		dev_err(dev, "%s: ret: %d\n", __func__, ret);
		return ret;
	}
	return 0;
}
/* EN8811H BUCK write function */
static int __air_buckpbus_reg_write(struct phy_device *phydev,
			unsigned int pbus_address, unsigned int pbus_data)
{
	int ret = 0;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	/* page 4 */
	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 4);
	ret |= __air_mii_cl22_write(mbus, addr, 0x10, 0);
	ret |= __air_mii_cl22_write(mbus, addr,
			0x11, ((pbus_address >> 16) & 0xffff));
	ret |= __air_mii_cl22_write(mbus, addr,
			0x12, (pbus_address & 0xffff));
	ret |= __air_mii_cl22_write(mbus, addr,
			0x13, ((pbus_data >> 16) & 0xffff));
	ret |= __air_mii_cl22_write(mbus, addr, 0x14, (pbus_data & 0xffff));
	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 0);
	if (ret < 0) {
		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
		return ret;
	}
	return 0;
}

/* EN8811H BUCK read function */
static unsigned int __air_buckpbus_reg_read(struct phy_device *phydev,
			unsigned int pbus_address)
{
	unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
	int ret = 0;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	/* page 4 */
	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 4);
	ret |= __air_mii_cl22_write(mbus, addr, 0x10, 0);
	ret |= __air_mii_cl22_write(mbus, addr,
			0x15, ((pbus_address >> 16) & 0xffff));
	ret |= __air_mii_cl22_write(mbus, addr,
			0x16, (pbus_address & 0xffff));
	if (ret) {
		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
		return PBUS_INVALID_DATA;
	}

	pbus_data_high = __air_mii_cl22_read(mbus, addr, 0x17);
	pbus_data_low = __air_mii_cl22_read(mbus, addr, 0x18);
	pbus_data = (pbus_data_high << 16) + pbus_data_low;
	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 0);
	if (ret) {
		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
		return ret;
	}
	return pbus_data;
}

unsigned int air_buckpbus_reg_read(struct phy_device *phydev,
			unsigned int pbus_address)
{
	unsigned int data;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);

	mutex_lock(&mbus->mdio_lock);
	data = __air_buckpbus_reg_read(phydev, pbus_address);
	mutex_unlock(&mbus->mdio_lock);
	if (data == INVALID_DATA) {
		dev_err(dev, "__air_buckpbus_reg_read fail\n");
		return INVALID_DATA;
	}
	return data;
}

int air_buckpbus_reg_write(struct phy_device *phydev,
		unsigned int pbus_address, unsigned int pbus_data)
{
	int ret = 0;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);

	mutex_lock(&mbus->mdio_lock);
	ret |= __air_buckpbus_reg_write(phydev, pbus_address, pbus_data);
	mutex_unlock(&mbus->mdio_lock);
	if (ret) {
		dev_err(dev, "__air_buckpbus_reg_write, ret: %d\n", ret);
		return ret;
	}
	return ret;
}

int air_surge_5ohm_config(struct phy_device *phydev)
{
	int ret = 0;
	struct device *dev = phydev_dev(phydev);

	ret |= air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
	ret |= air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
	ret |= air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
	ret |= air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x00b0);
	if (ret < 0)
		return ret;
	dev_info(dev, "surge protection mode - 5R\n");
	return ret;
}

#if defined(CONFIG_OF)
int en8811h_of_init(struct phy_device *phydev)
{
	struct device *dev = phydev_dev(phydev);
	struct device_node *of_node = dev->of_node;
	struct en8811h_priv *priv = phydev->priv;
	u32 val = 0;

	dev_info(dev, "%s: start\n", __func__);
	if (of_find_property(of_node, "airoha,polarity", NULL)) {
		if (of_property_read_u32(of_node, "airoha,polarity",
					 &val) != 0) {
			dev_err(dev, "airoha,polarity value is invalid.");
			return -EINVAL;
		}
		if (val < AIR_POL_TX_REV_RX_NOR ||
		    val > AIR_POL_TX_NOR_RX_REV) {
			dev_err(dev,
				   "airoha,polarity value %u out of range.",
				   val);
			return -EINVAL;
		}
		priv->pol = val;
	} else
		priv->pol = AIR_POL_TX_NOR_RX_NOR;

	if (of_find_property(of_node, "airoha,surge", NULL)) {
		if (of_property_read_u32(of_node, "airoha,surge",
					 &val) != 0) {
			dev_err(dev, "airoha,surge value is invalid.");
			return -EINVAL;
		}
		if (val < 0 || val > 1) {
			dev_err(dev,
				   "airoha,surge value %u out of range.",
				   val);
			return -EINVAL;
		}
		if (val)
			priv->surge = 1;
		else
			priv->surge = 0;
	} else
		priv->surge = 0;

	return 0;
}
#else
int en8811h_of_init(struct phy_device *phydev)
{
	return -ESRCH;
}
#endif /* CONFIG_OF */

static int air_resolve_an_speed(struct phy_device *phydev)
{
	int lpagb = 0, advgb = 0, common_adv_gb = 0;
	int lpa = 0, adv = 0, common_adv = 0;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	dev_dbg(dev, "AN mode!\n");
	dev_dbg(dev, "SPEED 1000/100!\n");
	lpagb = air_mii_cl22_read(mbus,
				addr, MII_STAT1000);
	if (lpagb < 0)
		return lpagb;
	advgb = air_mii_cl22_read(mbus,
				addr, MII_CTRL1000);
	if (adv < 0)
		return adv;
	common_adv_gb = (lpagb & (advgb << 2));

	lpa = air_mii_cl22_read(mbus, addr, MII_LPA);
	if (lpa < 0)
		return lpa;
	adv = air_mii_cl22_read(mbus,
				addr, MII_ADVERTISE);
	if (adv < 0)
		return adv;
	phydev->pause = GET_BIT(adv, 10);
	phydev->asym_pause = GET_BIT(adv, 11);
	common_adv = (lpa & adv);

	phydev->speed = SPEED_UNKNOWN;
	phydev->duplex = DUPLEX_HALF;
	if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF)) {
		phydev->speed = SPEED_1000;
		if (common_adv_gb & LPA_1000FULL)
			phydev->duplex = DUPLEX_FULL;
	} else if (common_adv & (LPA_100FULL | LPA_100HALF)) {
		phydev->speed = SPEED_100;
		if (common_adv & LPA_100FULL)
			phydev->duplex = DUPLEX_FULL;
	} else {
		if (common_adv & LPA_10FULL)
			phydev->duplex = DUPLEX_FULL;
	}
	return 0;
}

int air_get_autonego(struct phy_device *phydev, int *an)
{
	int reg;
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	reg = air_mii_cl22_read(mbus, addr, MII_BMCR);
	if (reg < 0)
		return -EINVAL;
	if (reg & BMCR_ANENABLE)
		*an = AUTONEG_ENABLE;
	else
		*an = AUTONEG_DISABLE;
	return 0;
}

static int air_read_status(struct phy_device *phydev)
{
	int ret = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0;
	u32 pbus_value = 0;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	phydev->speed = SPEED_UNKNOWN;
	phydev->duplex = DUPLEX_UNKNOWN;
	phydev->pause = 0;
	phydev->asym_pause = 0;
	phydev->link = 0;
	phydev->autoneg = AUTONEG_DISABLE;
	reg = air_mii_cl22_read(mbus, addr, MII_BMSR);
	if (reg < 0) {
		dev_err(dev, "MII_BMSR reg %d!\n", reg);
		return reg;
	}
	reg = air_mii_cl22_read(mbus, addr, MII_BMSR);
	if (reg < 0) {
		dev_err(dev, "MII_BMSR reg %d!\n", reg);
		return reg;
	}
	if (reg & BMSR_LSTATUS) {
		phydev->link = 1;
		ret = air_get_autonego(phydev, &an);
		if (ret < 0)
			return ret;
		phydev->autoneg = an;
		pbus_value = air_buckpbus_reg_read(phydev, 0x109D4);
		if (0x10 & pbus_value) {
			phydev->speed = SPEED_2500;
			phydev->duplex = DUPLEX_FULL;
		} else {
			ret = air_get_autonego(phydev, &an);
			if (phydev->autoneg == AUTONEG_ENABLE) {
				ret = air_resolve_an_speed(phydev);
				if (ret < 0)
					return ret;
			} else {
				dev_dbg(dev, "Force mode!\n");
				bmcr = air_mii_cl22_read(mbus, addr, MII_BMCR);

				if (bmcr < 0)
					return bmcr;

				if (bmcr & BMCR_FULLDPLX)
					phydev->duplex = DUPLEX_FULL;
				else
					phydev->duplex = DUPLEX_HALF;

				if (bmcr & BMCR_SPEED1000)
					phydev->speed = SPEED_1000;
				else if (bmcr & BMCR_SPEED100)
					phydev->speed = SPEED_100;
				else
					phydev->speed = SPEED_UNKNOWN;
			}
		}
	}

	return ret;
}
#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
static void air_polarity_help(void)
{
	pr_notice("\nUsage:\n"
			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
			"echo [tx polarity] [rx polarity] > /[debugfs]/polarity\n"
			"option: tx_normal, tx_reverse, rx_normal, rx_revers\n");
}

static int air_set_polarity(struct phy_device *phydev, int tx_rx)
{
	int ret = 0;
	unsigned int pbus_data = 0;

	pr_debug("\nPolarit %s\n", tx_rx_string[tx_rx]);
	pbus_data = air_buckpbus_reg_read(phydev, 0xca0f8) & ~(BIT(0) | BIT(1));
	pbus_data |= tx_rx;
	ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_data);
	if (ret < 0)
		pr_notice("\n%s:air_buckpbus_reg_write fail\n", __func__);
	pbus_data = air_buckpbus_reg_read(phydev, 0xca0f8);
	pr_notice("\nPolarity %s confirm....(%02lx)\n",
			tx_rx_string[tx_rx], pbus_data & (BIT(0) | BIT(1)));

	return ret;
}

static int air_set_mode(struct phy_device *phydev, int dbg_mode)
{
	int ret = 0, val = 0;
	unsigned int pbus_data = 0;
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	switch (dbg_mode) {
	case AIR_PORT_MODE_FORCE_100:
		pr_notice("\nForce 100M\n");
		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) | BIT(8);
		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) & ~BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl45_read(phydev, 0x7, 0x20) & ~BIT(7);
		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
		if (unlikely(ret < 0))
			break;
		break;
	case AIR_PORT_MODE_FORCE_1000:
		pr_notice("\nForce 1000M\n");
		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) & ~BIT(8);
		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) | BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl45_read(phydev, 0x7, 0x20) & ~BIT(7);
		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
		if (unlikely(ret < 0))
			break;
		break;
	case AIR_PORT_MODE_FORCE_2500:
		pr_notice("\nForce 2500M\n");
		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) & ~BIT(8);
		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) & ~BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl45_read(phydev, 0x7, 0x20) | BIT(7);
		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
		if (unlikely(ret < 0))
			break;
		break;
	case AIR_PORT_MODE_AUTONEGO:
		pr_notice("\nAutonego mode\n");
		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) | BIT(8);
		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) | BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl45_read(phydev, 0x7, 0x20) | BIT(7);
		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
		if (unlikely(ret < 0))
			break;
		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
		if (unlikely(ret < 0))
			break;
		break;
	case AIR_PORT_MODE_POWER_DOWN:
		pr_notice("\nPower Down\n");
		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(11);
		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
		if (unlikely(ret < 0))
			break;
		break;
	case AIR_PORT_MODE_POWER_UP:
		pr_notice("\nPower Up\n");
		val = air_mii_cl22_read(mbus, addr, MII_BMCR) & ~BIT(11);
		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
		if (unlikely(ret < 0))
			break;
		break;
	case AIR_PORT_MODE_SSC_DISABLE:
		pr_notice("\nSSC Disabled\n");
		pbus_data = air_buckpbus_reg_read(phydev, 0xca000);
		pbus_data &= ~BIT(21);
		ret = air_buckpbus_reg_write(phydev, 0xca000, pbus_data);
		if (unlikely(ret < 0))
			break;
		break;
	case AIR_PORT_MODE_SSC_ENABLE:
		pr_notice("\nSSC Enabled\n");
		pbus_data = air_buckpbus_reg_read(phydev, 0xca000);
		pbus_data |= BIT(21);
		ret = air_buckpbus_reg_write(phydev, 0xca000, pbus_data);
		if (unlikely(ret < 0))
			break;
		break;
	default:
		pr_notice("\nWrong Port mode\n");
		break;
	}
	return ret;
}

static int airphy_info_show(struct seq_file *seq, void *v)
{
	struct phy_device *phydev = seq->private;
	struct en8811h_priv *priv = phydev->priv;
	unsigned int val = 0;

	seq_puts(seq, "<<AIR EN8811H Driver info>>\n");
	seq_printf(seq, "| Driver Version       : %s\n",
							EN8811H_DRIVER_VERSION);
	val = air_buckpbus_reg_read(phydev, 0xcf914);
	seq_printf(seq, "| Boot mode            : %s\n",
		((val & BIT(24)) >> 24) ? "Flash" : "Download Code");
	seq_printf(seq, "| EthMD32.dm.bin  CRC32: %08x\n",
				priv->dm_crc32);
	seq_printf(seq, "| EthMD32.DSP.bin CRC32: %08x\n",
				priv->dsp_crc32);
	val = air_buckpbus_reg_read(phydev, 0x3b3c);
	seq_printf(seq, "| MD32 FW Version      : %08x\n", val);
	val = air_mii_cl45_read(phydev, 0x1e, 0x8009);
	seq_printf(seq, "| MD32 FW Status       : %08x\n",
				air_mii_cl45_read(phydev, 0x1e, 0x8009));
	val = (air_buckpbus_reg_read(phydev, 0xca0f8) & 0x3);
	seq_printf(seq, "| Tx, Rx Polarity      : %s(%02d)\n",
						tx_rx_string[val], val);
	seq_puts(seq, "\n");

	return 0;
}

static int airphy_info_open(struct inode *inode, struct file *file)
{
	return single_open(file, airphy_info_show, inode->i_private);
}

static int airphy_fcm_counter_show(struct phy_device *phydev,
				struct seq_file *seq)
{
	int ret = 0;
	u32 pkt_cnt = 0;

	seq_puts(seq, "|\t<<FCM Counter>>\n");
	seq_puts(seq, "| Rx from Line side_S      :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0090);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| Rx from Line side_T      :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0094);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| Tx to System side_S      :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe009c);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| Tx to System side_T      :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe00A0);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| Rx from System side_S    :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0078);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| Rx from System side_T    :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe007C);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| Tx to Line side_S        :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0084);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| Tx to Line side_T        :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0088);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	ret = air_buckpbus_reg_write(phydev, 0xe0074, 0x3);
	if (ret < 0)
		return ret;
	return 0;
}

static int airphy_ss_counter_show(struct phy_device *phydev,
				struct seq_file *seq)
{
	int ret = 0;
	u32 pkt_cnt = 0;

	seq_puts(seq, "|\t<<SS Counter>>\n");
	ret = air_buckpbus_reg_write(phydev, 0xC602C, 0x3);
	if (ret < 0)
		return ret;
	seq_puts(seq, "| TX Start                 :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60B0);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| TX Terminal              :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60B4);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| RX Start                 :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60BC);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	seq_puts(seq, "| RX Terminal              :");
	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60C0);
	seq_printf(seq, "%010u |\n", pkt_cnt);
	ret = air_buckpbus_reg_write(phydev, 0xC602C, 0x4);
	if (ret < 0)
		return ret;
	return 0;
}

static int airphy_counter_show(struct seq_file *seq, void *v)
{
	struct phy_device *phydev = seq->private;
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int ret = 0, addr = phydev_addr(phydev);
	u32 pkt_cnt = 0;

	ret = air_read_status(phydev);
	if (ret < 0)
		return ret;
	seq_puts(seq, "==========AIR PHY COUNTER==========\n");
	if (phydev->link) {
		ret = airphy_ss_counter_show(phydev, seq);
		if (ret < 0)
			return ret;
	}
	ret = airphy_fcm_counter_show(phydev, seq);
	if (ret < 0)
		return ret;
	if (phydev->link) {
		seq_puts(seq, "|\t<<MAC Counter>>\n");
		seq_puts(seq, "| Tx Error from System side:");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x131000);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx Error to System side  :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x132000);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Tx from System side      :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x131004);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx to System Side        :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x132004);
		seq_printf(seq, "%010u |\n", pkt_cnt);
	}
	if (phydev->link && phydev->speed == SPEED_2500) {
		seq_puts(seq, "|\t<<LS Counter>>\n");
		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x10);
		if (ret < 0)
			return ret;
		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x0);
		if (ret < 0)
			return ret;
		seq_puts(seq, "|\tBefore EF\n");
		seq_puts(seq, "| Tx to Line side_S        :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x3071c);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Tx to Line side_T        :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30720);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Tx_ENC                   :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30724);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx from Line side_S      :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x3072c);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx from Line side_T      :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30730);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx_DEC                   :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30728);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "|\tAfter EF\n");
		seq_puts(seq, "| Tx to Line side_S        :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30734);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Tx to Line side_T        :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30738);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx from Line side_S      :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30764);
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx from Line side_T      :");
		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30768);
		seq_printf(seq, "%010u |\n\n", pkt_cnt);
		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x13);
		if (ret < 0)
			return ret;
		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x3);
		if (ret < 0)
			return ret;
		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x10);
		if (ret < 0)
			return ret;
		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x0);
		if (ret < 0)
			return ret;
	}
	if (phydev->link && ((phydev->speed != SPEED_2500))) {
		seq_puts(seq, "|\t<<LS Counter>>\n");
		ret = air_mii_cl22_write(mbus, addr, 0x1f, 1);
		if (ret < 0)
			return ret;
		seq_puts(seq, "| Rx from Line side        :");
		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x12) & 0x7fff;
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Rx Error from Line side  :");
		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x17) & 0xff;
		seq_printf(seq, "%010u |\n", pkt_cnt);
		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0);
		if (ret < 0)
			return ret;
		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0x52B5);
		if (ret < 0)
			return ret;
		ret = air_mii_cl22_write(mbus, addr, 0x10, 0xBF92);
		if (ret < 0)
			return ret;
		seq_puts(seq, "| Tx to Line side          :");
		pkt_cnt = (air_mii_cl22_read(mbus, addr, 0x11) & 0x7ffe) >> 1;
		seq_printf(seq, "%010u |\n", pkt_cnt);
		seq_puts(seq, "| Tx Error to Line side    :");
		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x12);
		pkt_cnt &= 0x7f;
		seq_printf(seq, "%010u |\n\n", pkt_cnt);
		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0);
		if (ret < 0)
			return ret;
	}
	return ret;
}

static int airphy_counter_open(struct inode *inode, struct file *file)
{
	return single_open(file, airphy_counter_show, inode->i_private);
}

static ssize_t airphy_polarity_write(struct file *file, const char __user *ptr,
					size_t len, loff_t *off)
{
	struct phy_device *phydev = file->private_data;
	char buf[32], param1[32], param2[32];
	int count = len, ret = 0, tx_rx = 0;

	memset(buf, 0, 32);
	memset(param1, 0, 32);
	memset(param2, 0, 32);

	if (count > sizeof(buf) - 1)
		return -EINVAL;
	if (copy_from_user(buf, ptr, len))
		return -EFAULT;
	if (sscanf(buf, "%s %s", param1, param2) == -1)
		return -EFAULT;

	if (!strncmp("tx_normal", param1, strlen("tx_normal"))) {
		if (!strncmp("rx_normal", param2, strlen("rx_normal")))
			tx_rx = AIR_POL_TX_NOR_RX_NOR;
		else if (!strncmp("rx_reverse", param2, strlen("rx_reverse")))
			tx_rx = AIR_POL_TX_NOR_RX_REV;
		else {
			pr_notice("\nRx param is not correct.\n");
			return -EINVAL;
		}
	} else if (!strncmp("tx_reverse", param1, strlen("tx_reverse"))) {
		if (!strncmp("rx_normal", param2, strlen("rx_normal")))
			tx_rx = AIR_POL_TX_REV_RX_NOR;
		else if (!strncmp("rx_reverse", param2, strlen("rx_reverse")))
			tx_rx = AIR_POL_TX_REV_RX_REV;
		else {
			pr_notice("\nRx param is not correct.\n");
			return -EINVAL;
		}
	} else {
		air_polarity_help();
		return count;
	}
	pr_notice("\nSet Polarity %s\n", tx_rx_string[tx_rx]);
	ret = air_set_polarity(phydev, tx_rx);
	if (ret < 0)
		return ret;
	return count;
}
static void airphy_port_mode_help(void)
{
	pr_notice("\nUsage:\n"
			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
			"echo [mode] [para] > /[debugfs]/port_mode\n"
			"echo re-an > /[debugfs]/port_mode\n"
			"echo auto > /[debugfs]/port_mode\n"
			"echo 2500 > /[debugfs]/port_mode\n"
			"echo 1000 > /[debugfs]/port_mode\n"
			"echo 100 > /[debugfs]/port_mode\n"
			"echo ssc ena/dis > /[debugfs]/port_mode\n"
			"echo power up/down >  /[debugfs]/port_mode\n");
}

static ssize_t airphy_port_mode(struct file *file, const char __user *ptr,
					size_t len, loff_t *off)
{
	struct phy_device *phydev = file->private_data;
	char buf[32], cmd[32], param[32];
	int count = len, ret = 0;
	int num = 0, val = 0;

	memset(buf, 0, 32);
	memset(cmd, 0, 32);
	memset(param, 0, 32);

	if (count > sizeof(buf) - 1)
		return -EINVAL;
	if (copy_from_user(buf, ptr, len))
		return -EFAULT;

	num = sscanf(buf, "%s %s", cmd, param);
	if (num < 1 || num > 3)
		return -EFAULT;

	if (!strncmp("auto", cmd, strlen("auto")))
		ret = air_set_mode(phydev, AIR_PORT_MODE_AUTONEGO);
	else if (!strncmp("2500", cmd, strlen("2500")))
		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_2500);
	else if (!strncmp("1000", cmd, strlen("1000")))
		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_1000);
	else if (!strncmp("100", cmd, strlen("100")))
		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_100);
	else if (!strncmp("re-an", cmd, strlen("re-an"))) {
		val = phy_read(phydev, MII_BMCR) | BIT(9);
		ret = phy_write(phydev, MII_BMCR, val);
	} else if (!strncmp("power", cmd, strlen("power"))) {
		if (!strncmp("down", param, strlen("down")))
			ret = air_set_mode(phydev, AIR_PORT_MODE_POWER_DOWN);
		else if (!strncmp("up", param, strlen("up")))
			ret = air_set_mode(phydev, AIR_PORT_MODE_POWER_UP);
	} else if (!strncmp("ssc", cmd, strlen("ssc"))) {
		if (!strncmp("dis", param, strlen("dis")))
			ret = air_set_mode(phydev, AIR_PORT_MODE_SSC_DISABLE);
		else if (!strncmp("ena", param, strlen("ena")))
			ret = air_set_mode(phydev, AIR_PORT_MODE_SSC_ENABLE);
	} else if (!strncmp("help", cmd, strlen("help"))) {
		airphy_port_mode_help();
	}

	if (ret < 0)
		return ret;

	return count;
}

static void airphy_debugfs_buckpbus_help(void)
{
	pr_notice("\nUsage:\n"
			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
			"Read:\n"
			"echo r [buckpbus_register] > /[debugfs]/buckpbus_op\n"
			"Write:\n"
			"echo w [buckpbus_register] [value] > /[debugfs]/buckpbus_op\n");
}


static ssize_t airphy_debugfs_buckpbus(struct file *file,
					const char __user *buffer, size_t count,
					loff_t *data)
{
	struct phy_device *phydev = file->private_data;
	char buf[64];
	int ret = 0;
	unsigned int reg, val;

	memset(buf, 0, 64);
	if (count > sizeof(buf) - 1)
		return -EINVAL;
	if (copy_from_user(buf, buffer, count))
		return -EFAULT;

	if (buf[0] == 'w') {
		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
			return -EFAULT;

		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
			phydev_addr(phydev), reg, val);
		ret = air_buckpbus_reg_write(phydev, reg, val);
		if (ret < 0) {
			pr_notice("\nbuckpbus_reg_write fail\n");
			return -EIO;
		}
		val = air_buckpbus_reg_read(phydev, reg);
		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
			phydev_addr(phydev), reg, val);
	} else if (buf[0] == 'r') {
		if (sscanf(buf, "r %x", &reg) == -1)
			return -EFAULT;

		val = air_buckpbus_reg_read(phydev, reg);
		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
		phydev_addr(phydev), reg, val);
	} else
		airphy_debugfs_buckpbus_help();

	return count;
}

static ssize_t airphy_debugfs_pbus(struct file *file,
					const char __user *buffer, size_t count,
					loff_t *data)
{
	struct phy_device *phydev = file->private_data;
	char buf[64];
	int ret = 0;
	unsigned int reg, val;

	memset(buf, 0, 64);
	if (copy_from_user(buf, buffer, count))
		return -EFAULT;

	if (buf[0] == 'w') {
		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
			return -EFAULT;

		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
			phydev_addr(phydev), reg, val);
		ret = air_pbus_reg_write(phydev, reg, val);
		if (ret < 0) {
			pr_notice("\npbus_reg_write fail\n");
			return -EIO;
		}
		val = air_pbus_reg_read(phydev, reg);
		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
			phydev_addr(phydev), reg, val);
	} else if (buf[0] == 'r') {
		if (sscanf(buf, "r %x", &reg) == -1)
			return -EFAULT;

		val = air_pbus_reg_read(phydev, reg);
		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
		phydev_addr(phydev), reg, val);
	} else
		airphy_debugfs_buckpbus_help();

	return count;
}

static int airphy_link_status(struct seq_file *seq, void *v)
{
	int ret = 0;
	struct phy_device *phydev = seq->private;

	ret = air_read_status(phydev);
	if (ret < 0)
		return ret;

	seq_printf(seq, "%s Information:\n", dev_name(phydev_dev(phydev)));
	seq_printf(seq, "\tPHYAD: %02d\n", phydev_addr(phydev));
	seq_printf(seq, "\tLink Status: %s\n", phydev->link ? "UP" : "DOWN");
	if (phydev->link) {
		seq_printf(seq, "\tAuto-Nego: %s\n",
				phydev->autoneg ? "on" : "off");
		seq_puts(seq, "\tSpeed: ");
		if (phydev->speed == SPEED_UNKNOWN)
			seq_printf(seq, "Unknown! (%i)\n", phydev->speed);
		else
			seq_printf(seq, "%uMb/s\n", phydev->speed);

		seq_printf(seq, "\tDuplex: %s\n",
				phydev->duplex ? "Full" : "Half");
		seq_puts(seq, "\n");
	}

	return ret;
}

static int airphy_link_status_open(struct inode *inode, struct file *file)
{
	return single_open(file, airphy_link_status, inode->i_private);
}

static int dbg_regs_show(struct seq_file *seq, void *v)
{
	struct phy_device *phydev = seq->private;
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	seq_puts(seq, "\t<<DEBUG REG DUMP>>\n");
	seq_printf(seq, "| RG_MII_BMCR           : 0x%08x |\n",
		   air_mii_cl22_read(mbus, addr, MII_BMCR));
	seq_printf(seq, "| RG_MII_BMSR           : 0x%08x |\n",
		   air_mii_cl22_read(mbus, addr, MII_BMSR));
	seq_printf(seq, "| RG_MII_ADVERTISE      : 0x%08x |\n",
		   air_mii_cl22_read(mbus, addr, MII_ADVERTISE));
	seq_printf(seq, "| RG_MII_LPA            : 0x%08x |\n",
		   air_mii_cl22_read(mbus, addr, MII_LPA));
	seq_printf(seq, "| RG_MII_CTRL1000       : 0x%08x |\n",
		   air_mii_cl22_read(mbus, addr, MII_CTRL1000));
	seq_printf(seq, "| RG_MII_STAT1000       : 0x%08x |\n",
		   air_mii_cl22_read(mbus, addr, MII_STAT1000));
	seq_printf(seq, "| RG_MII_REF_CLK        : 0x%08x |\n",
		   air_mii_cl22_read(mbus, addr, 0x1d));
	seq_printf(seq, "| RG_HW_STRAP1          : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xcf910));
	seq_printf(seq, "| RG_HW_STRAP2          : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xcf914));
	seq_printf(seq, "| RG_SYS_LINK_MODE      : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xe0004));
	seq_printf(seq, "| RG_FCM_CTRL           : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xe000C));
	seq_printf(seq, "| RG_SS_PAUSE_TIME      : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xe0020));
	seq_printf(seq, "| RG_MIN_IPG_NUM        : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xe002C));
	seq_printf(seq, "| RG_CTROL_0            : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xc0000));
	seq_printf(seq, "| RG_LINK_STATUS        : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xc0b04));
	seq_printf(seq, "| RG_LINK_PARTNER_AN    : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0xc0014));
	seq_printf(seq, "| RG_FN_PWR_CTRL_STATUS : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0x1020c));
	seq_printf(seq, "| RG_WHILE_LOOP_COUNT   : 0x%08x |\n",
		   air_buckpbus_reg_read(phydev, 0x3A48));

	return 0;
}

static int airphy_dbg_regs_show_open(struct inode *inode, struct file *file)
{
	return single_open(file, dbg_regs_show, inode->i_private);
}


static int airphy_temp_show(struct seq_file *seq, void *v)
{
	struct phy_device *phydev = seq->private;
	int ret = 0;
	u32 pbus_value = 0;

	seq_puts(seq, "<<AIR EN8811H Temp>>\n");
	ret |= air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1100);
	ret |= air_mii_cl45_write(phydev, 0x1e, 0x800f, 0xe5);
	if (ret < 0) {
		pr_notice("\nmii_cl45_write fail\n");
		return -EIO;
	}
	pbus_value = air_buckpbus_reg_read(phydev, 0x3B38);
	seq_printf(seq, "| Temperature  : %dC |\n",
						pbus_value);
	seq_puts(seq, "\n");

	return 0;
}

static int airphy_temp_show_open(struct inode *inode, struct file *file)
{
	return single_open(file, airphy_temp_show, inode->i_private);
}


static unsigned int air_read_lp_speed(struct phy_device *phydev)
{
	int val = 0, an = AUTONEG_DISABLE;
	unsigned int ret = 0;
	int count = 15, i, lpa, lpagb;
	struct air_lp_speed *m;
	struct device *dev = phydev_dev(phydev);
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);

	val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
	ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
	if (unlikely(ret < 0))
		return ret;
	msleep(1500);
	do {
		msleep(100);
		ret = air_mii_cl45_read(phydev, MDIO_MMD_AN, 0x21);
		ret &= BIT(5);
		if (ret)
			break;
		count--;
	} while (count);

	count = 10;
	do {
		msleep(500);
		val = air_mii_cl22_read(mbus, addr, MII_BMSR);
		if (val < 0) {
			dev_err(dev, "MII_BMSR reg 0x%x!\n", val);
			return val;
		}
		val = air_mii_cl22_read(mbus, addr, MII_BMSR);
		if (val < 0) {
			dev_err(dev, "MII_BMSR reg 0x%x!\n", val);
			return val;
		}
		dev_dbg(dev, "val 0x%x\n", val);
		if (val & BMSR_LSTATUS) {
			val = air_mii_cl22_read(mbus, addr, MII_LPA);
			if (val < 0)
				return val;
			lpa = (val & (BIT(5) | BIT(6) | BIT(7) | BIT(8))) >> 5;
			val = air_mii_cl22_read(mbus, addr, MII_STAT1000);
			if (val < 0)
				return val;
			lpagb = GET_BIT(val, 11) << 4;
			ret |= (lpagb | lpa);
			return ret;
		}
	} while (count--);

	return 0;
}

static int airphy_lp_speed(struct seq_file *seq, void *v)
{
	unsigned int ret = 0, val = 0, did1 = 0, i;
	struct phy_device *phydev = seq->private;
	static const struct {
		unsigned int bit_index;
		const char *name;
	} mode_defs[] = {
		{ AIR_LINK_MODE_10baseT_Half_BIT,
		"10baseT/Half" },
		{ AIR_LINK_MODE_10baseT_Full_BIT,
		"10baseT/Full" },
		{ AIR_LINK_MODE_100baseT_Half_BIT,
		"100baseT/Half" },
		{ AIR_LINK_MODE_100baseT_Full_BIT,
		"100baseT/Full" },
		{ AIR_LINK_MODE_1000baseT_Full_BIT,
		"1000baseT/Full" },
		{ AIR_LINK_MODE_2500baseT_Full_BIT,
		"2500baseT/Full" }
	};

	seq_printf(seq, "%s Link Partner Ability:\n",
			dev_name(phydev_dev(phydev)));
	ret = air_read_lp_speed(phydev);
	if (val < 0)
		return val;
	for (i = 0; i < ARRAY_SIZE(mode_defs); i++) {
		if (ret & BIT(mode_defs[i].bit_index)) {
			seq_printf(seq, "\t\t\t %s\n",
						mode_defs[i].name);
			did1++;
		}
	}
	if (did1 == 0)
		seq_puts(seq, "\t\t\t Not reported\n");

	return 0;
}

static int airphy_lp_speed_open(struct inode *inode, struct file *file)
{
	return single_open(file, airphy_lp_speed, inode->i_private);
}

static void airphy_debugfs_mii_cl22_help(void)
{
	pr_notice("\nUsage:\n"
			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
			"Read:\n"
			"echo r [phy_register] > /[debugfs]/mii_mgr_op\n"
			"Write:\n"
			"echo w [phy_register] [value] > /[debugfs]/mii_mgr_op\n");
}


static ssize_t airphy_debugfs_cl22(struct file *file,
					const char __user *buffer, size_t count,
					loff_t *data)
{
	struct phy_device *phydev = file->private_data;
	struct mii_bus *mbus = phydev_mdio_bus(phydev);
	int addr = phydev_addr(phydev);
	char buf[64];
	int ret = 0;
	unsigned int reg, val;

	memset(buf, 0, 64);
	if (count > sizeof(buf) - 1)
		return -EINVAL;
	if (copy_from_user(buf, buffer, count))
		return -EFAULT;

	if (buf[0] == 'w') {
		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
			return -EFAULT;

		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
			phydev_addr(phydev), reg, val);
		ret = air_mii_cl22_write(mbus, addr, reg, val);
		if (ret < 0) {
			pr_notice("\nmii_cl22_write fail\n");
			return -EIO;
		}
		val = air_mii_cl22_read(mbus, addr, reg);
		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
			phydev_addr(phydev), reg, val);
	} else if (buf[0] == 'r') {
		if (sscanf(buf, "r %x", &reg) == -1)
			return -EFAULT;

		val = air_mii_cl22_read(mbus, addr, reg);
		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
		phydev_addr(phydev), reg, val);
	} else
		airphy_debugfs_mii_cl22_help();

	return count;
}

static void airphy_debugfs_mii_cl45_help(void)
{
	pr_notice("\nUsage:\n"
			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
			"Read:\n"
			"echo r [device number] [phy_register] > /[debugfs]/mii_mgr_cl45_op\n"
			"Write:\n"
			"echo w [device number] [phy_register] [value] > /[debugfs]/mii_mgr_cl45_op\n");
}


static ssize_t airphy_debugfs_cl45(struct file *file,
					const char __user *buffer, size_t count,
					loff_t *data)
{
	struct phy_device *phydev = file->private_data;
	char buf[64];
	int ret = 0;
	unsigned int reg, val, devnum;

	memset(buf, 0, 64);
	if (count > sizeof(buf) - 1)
		return -EINVAL;
	if (copy_from_user(buf, buffer, count))
		return -EFAULT;

	if (buf[0] == 'w') {
		if (sscanf(buf, "w %x %x %x", &devnum, &reg, &val) == -1)
			return -EFAULT;

		pr_notice("\nphy=%d, devnum=0x%x, reg=0x%x, val=0x%x\n",
			phydev_addr(phydev), devnum, reg, val);
		ret = air_mii_cl45_write(phydev, devnum, reg, val);
		if (ret < 0) {
			pr_notice("\nmii_cl45_write fail\n");
			return -EIO;
		}
		val = air_mii_cl45_read(phydev, devnum, reg);
		pr_notice("\nphy=%d, devnum=0x%x, reg=0x%x, val=0x%x confirm..\n",
			phydev_addr(phydev), devnum, reg, val);
	} else if (buf[0] == 'r') {
		if (sscanf(buf, "r %x %x", &devnum, &reg) == -1)
			return -EFAULT;

		val = air_mii_cl45_read(phydev, devnum, reg);
		pr_notice("\nphy=%d, devnum=0x%x, reg=0x%x, val=0x%x\n",
		phydev_addr(phydev), devnum, reg, val);
	} else
		airphy_debugfs_mii_cl45_help();

	return count;
}

static const struct file_operations airphy_lp_speed_fops = {
	.owner = THIS_MODULE,
	.open = airphy_lp_speed_open,
	.read = seq_read,
	.llseek = noop_llseek,
	.release = single_release,
};

static const struct file_operations airphy_info_fops = {
	.owner = THIS_MODULE,
	.open = airphy_info_open,
	.read = seq_read,
	.llseek = noop_llseek,
	.release = single_release,
};

static const struct file_operations airphy_counter_fops = {
	.owner = THIS_MODULE,
	.open = airphy_counter_open,
	.read = seq_read,
	.llseek = noop_llseek,
	.release = single_release,
};

static const struct file_operations airphy_debugfs_buckpbus_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.write = airphy_debugfs_buckpbus,
	.llseek = noop_llseek,
};

static const struct file_operations airphy_debugfs_pbus_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.write = airphy_debugfs_pbus,
	.llseek = noop_llseek,
};

static const struct file_operations airphy_port_mode_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.write = airphy_port_mode,
	.llseek = noop_llseek,
};

static const struct file_operations airphy_polarity_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.write = airphy_polarity_write,
	.llseek = noop_llseek,
};

static const struct file_operations airphy_link_status_fops = {
	.owner = THIS_MODULE,
	.open = airphy_link_status_open,
	.read = seq_read,
	.llseek = noop_llseek,
	.release = single_release,
};

static const struct file_operations airphy_dbg_reg_show_fops = {
	.owner = THIS_MODULE,
	.open = airphy_dbg_regs_show_open,
	.read = seq_read,
	.llseek = noop_llseek,
	.release = single_release,
};

static const struct file_operations airphy_temp_fops = {
	.owner = THIS_MODULE,
	.open = airphy_temp_show_open,
	.read = seq_read,
	.llseek = noop_llseek,
	.release = single_release,
};

static const struct file_operations airphy_debugfs_cl22_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.write = airphy_debugfs_cl22,
	.llseek = noop_llseek,
};

static const struct file_operations airphy_debugfs_cl45_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.write = airphy_debugfs_cl45,
	.llseek = noop_llseek,
};

int airphy_debugfs_init(struct phy_device *phydev)
{
	int ret = 0;
	struct en8811h_priv *priv = phydev->priv;
	struct dentry *dir = priv->debugfs_root;

	dev_dbg(phydev_dev(phydev), "%s: start\n", __func__);
	dir = debugfs_create_dir(dev_name(phydev_dev(phydev)), NULL);
	if (!dir) {
		dev_err(phydev_dev(phydev), "%s:err at %d\n",
					 __func__, __LINE__);
		ret = -ENOMEM;
	}
	debugfs_create_file(DEBUGFS_DRIVER_INFO, S_IFREG | 0444,
					dir, phydev,
					&airphy_info_fops);
	debugfs_create_file(DEBUGFS_COUNTER, S_IFREG | 0444,
					dir, phydev,
					&airphy_counter_fops);
	debugfs_create_file(DEBUGFS_BUCKPBUS_OP, S_IFREG | 0200,
					dir, phydev,
					&airphy_debugfs_buckpbus_fops);
	debugfs_create_file(DEBUGFS_PBUS_OP, S_IFREG | 0200,
					dir, phydev,
					&airphy_debugfs_pbus_fops);
	debugfs_create_file(DEBUGFS_PORT_MODE, S_IFREG | 0200,
					dir, phydev,
					&airphy_port_mode_fops);
	debugfs_create_file(DEBUGFS_POLARITY, S_IFREG | 0200,
					dir, phydev,
					&airphy_polarity_fops);
	debugfs_create_file(DEBUGFS_LINK_STATUS, S_IFREG | 0444,
					dir, phydev,
					&airphy_link_status_fops);
	debugfs_create_file(DEBUGFS_DBG_REG_SHOW, S_IFREG | 0444,
					dir, phydev,
					&airphy_dbg_reg_show_fops);
	debugfs_create_file(DEBUGFS_TEMPERATURE, S_IFREG | 0444,
					dir, phydev,
					&airphy_temp_fops);
	debugfs_create_file(DEBUGFS_LP_SPEED, S_IFREG | 0444,
					dir, phydev,
					&airphy_lp_speed_fops);
	debugfs_create_file(DEBUGFS_MII_CL22_OP, S_IFREG | 0200,
					dir, phydev,
					&airphy_debugfs_cl22_fops);
	debugfs_create_file(DEBUGFS_MII_CL45_OP, S_IFREG | 0200,
					dir, phydev,
					&airphy_debugfs_cl45_fops);

	priv->debugfs_root = dir;
	return ret;
}

void airphy_debugfs_remove(struct phy_device *phydev)
{
	struct en8811h_priv *priv = phydev->priv;

	debugfs_remove_recursive(priv->debugfs_root);
	priv->debugfs_root = NULL;
}
#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
