diff -drupN a/drivers/net/phy/sunxi-ephy.c b/drivers/net/phy/sunxi-ephy.c --- a/drivers/net/phy/sunxi-ephy.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/net/phy/sunxi-ephy.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,394 @@ +/* + * Copyright © 2015-2016, Shuge + * Author: Sugar + * + * This file is provided under a dual BSD/GPL license. When using or + * redistributing this file, you may do so under either license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define EXTEPHY_CTRL0 0x0014 +#define EXTEPHY_CTRL1 0x0016 + +#define EPHY_CTRL 0x6000 +#define EPHY_SID 0x8004 + +#define WAIT_MAX_COUNT 1000 + +/* Register bits */ +#define EXTEPHY_CTRL0_RESET_INVALID (0x1 << 0) +#define EXTEPHY_CTRL0_SYSCLK_GATING (0x1 << 1) +#define EXTEPHY_CTRL1_IO_EN_BITS (0xf) +#define EPHY_CTRL_DEFAULT (0x5) + +atomic_t ephy_en; + +struct ephy_res { + struct device_driver *plat_drv; + struct device_driver *phy_drv; + struct phy_device *phydev; + struct acx00 *acx; +}; + +static struct ephy_res ephy_priv; + +int ephy_is_enable(void) +{ + return atomic_read(&ephy_en); +} +EXPORT_SYMBOL_GPL(ephy_is_enable); + +/** + * @name ephy_read_sid + * @brief read ephy sid from efuse + * @param[IN] none + * @param[OUT] p_ephy_cali: ephy calibration value + * @return return 0 if success,-value if fail + */ +static s32 ephy_read_sid(u16 *p_ephy_cali) +{ + s32 ret = 0; + u8 buf[6]; + + if (!p_ephy_cali) { + pr_info("%s's pointer type args are NULL!\n", __func__); + return -1; + } + ret = sunxi_efuse_readn(EFUSE_OEM_NAME, buf, 6); + if (ret != 0) { + pr_info("sunxi_efuse_readn failed:%d\n", ret); + return ret; + } + *p_ephy_cali = buf[0] + (buf[1] << 8); + + return ret; +} + +#if 0 +static int ephy_reset(struct phy_device *phydev) +{ + int bmcr; + + /* Software Reset PHY */ + bmcr = phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + + bmcr |= BMCR_RESET; + bmcr = phy_write(phydev, MII_BMCR, bmcr); + if (bmcr < 0) + return bmcr; + + do { + bmcr = phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + } while (bmcr & BMCR_RESET); + + return 0; +} +#endif + +static void disable_intelligent_ieee(struct phy_device *phydev) +{ + unsigned int value; + + phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ + value = phy_read(phydev, 0x17); /* read address 0 0x17 register */ + value &= ~(1 << 3); /* reg 0x17 bit 3, set 0 to disable IEEE */ + phy_write(phydev, 0x17, value); + phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ +} + +static void disable_802_3az_ieee(struct phy_device *phydev) +{ + unsigned int value; + + phy_write(phydev, 0xd, 0x7); + phy_write(phydev, 0xe, 0x3c); + phy_write(phydev, 0xd, 0x1 << 14 | 0x7); + value = phy_read(phydev, 0xe); + value &= ~(0x1 << 1); + phy_write(phydev, 0xd, 0x7); + phy_write(phydev, 0xe, 0x3c); + phy_write(phydev, 0xd, 0x1 << 14 | 0x7); + phy_write(phydev, 0xe, value); + + phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ + phy_write(phydev, 0x18, 0x0000); +} + +static int ephy_config_init(struct phy_device *phydev) +{ + int value; + + /* Iint ephy */ + phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ + phy_write(phydev, 0x12, 0x4824); /* Disable APS */ + + phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ + phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ + + phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ + phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */ + phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ + phy_write(phydev, 0x15, 0x1530); + + phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 6 */ + phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ +#if 0 + /* Disable Auto Power Saving mode */ + phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ + value = phy_read(phydev, 0x17); + value &= ~BIT(13); + phy_write(phydev, 0x17, value); +#endif + disable_intelligent_ieee(phydev); /* Disable Intelligent IEEE */ + disable_802_3az_ieee(phydev); /* Disable 802.3az IEEE */ + phy_write(phydev, 0x1f, 0x0000); /* Switch to Page 0 */ + +#ifdef CONFIG_MFD_ACX00 + value = acx00_reg_read(ephy_priv.acx, EPHY_CTRL); + if (phydev->interface == PHY_INTERFACE_MODE_RMII) + value |= (1 << 11); + else + value &= (~(1 << 11)); + acx00_reg_write(ephy_priv.acx, EPHY_CTRL, value | (1 << 11)); +#endif + +#if defined(CONFIG_ARCH_SUN50IW6) + value = phy_read(phydev, 0x13); + value |= 1 << 12; + phy_write(phydev, 0x13, value); +#endif + + return 0; +} + +static int ephy_probe(struct phy_device *phydev) +{ + struct phy_driver *drv; + + if (!phydev) + return -ENODEV; + + drv = phydev->drv; + ephy_priv.phy_drv = &drv->mdiodrv.driver; + ephy_priv.phydev = phydev; + return 0; +} + +#if 0 +static int ephy_ack_interrupt(struct phy_device *phydev) +{ + int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); + + if (err < 0) + return err; + + return 0; +} +#endif + +static void sunxi_ephy_enable(struct ephy_res *priv) +{ +#ifdef CONFIG_MFD_ACX00 + int value; + unsigned int i = 0; +#if defined(CONFIG_ARCH_SUN50IW6) + u16 ephy_cali = 0; +#endif + + if (!acx00_enable()) { + for (i = 0; i < WAIT_MAX_COUNT; i++) { + msleep(10); + if (acx00_enable()) + break; + } + if (i == WAIT_MAX_COUNT) { + pr_err("acx00 is no enable, and sunxi_ephy_enable is fail\n"); + return; + } + } + + value = acx00_reg_read(priv->acx, EXTEPHY_CTRL0); + value |= 0x03; + acx00_reg_write(priv->acx, EXTEPHY_CTRL0, value); + value = acx00_reg_read(priv->acx, EXTEPHY_CTRL1); + value |= 0x0f; + acx00_reg_write(priv->acx, EXTEPHY_CTRL1, value); + acx00_reg_write(priv->acx, EPHY_CTRL, 0x06); + + /*for ephy */ + value = acx00_reg_read(priv->acx, EPHY_CTRL); + value &= ~(0xf << 12); + +#if defined(CONFIG_ARCH_SUN50IW6) + ephy_read_sid(&ephy_cali); + value |= (0x0F & (0x03 + ephy_cali)) << 12; +#else + value |= (0x0F & (0x03 + acx00_reg_read(priv->acx, EPHY_SID))) << 12; +#endif + + acx00_reg_write(priv->acx, EPHY_CTRL, value); + + atomic_set(&ephy_en, 1); +#endif +} + +static struct phy_driver ephy_driver = { + .phy_id = 0x00441400, + .name = "ephy", + .phy_id_mask = 0x0ffffff0, + .features = PHY_BASIC_FEATURES | SUPPORTED_Pause | + SUPPORTED_Asym_Pause, +#if 0 + .flags = PHY_HAS_INTERRUPT, + .ack_interrupt = ephy_ack_interrupt, +#endif + .config_init = &ephy_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + .probe = ephy_probe, +}; + +static const struct platform_device_id sunxi_ephy_id[] = { + { "acx-ephy", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, sunxi_ephy_id); + +static int ephy_plat_probe(struct platform_device *pdev) +{ + struct acx00 *ax = dev_get_drvdata(pdev->dev.parent); + + if (!ax) + return -ENODEV; + + ephy_priv.acx = ax; + platform_set_drvdata(pdev, &ephy_priv); + ephy_priv.plat_drv = pdev->dev.driver; + + atomic_set(&ephy_en, 0); + + sunxi_ephy_enable(&ephy_priv); + + return 0; +} + +static int ephy_plat_remove(struct platform_device *pdev) +{ + return 0; +} + +static int sunxi_phy_suspend(struct device *dev) +{ + int value; + struct ephy_res *priv = &ephy_priv; + + atomic_set(&ephy_en, 0); + + /* reset regs values to the original state */ + value = acx00_reg_read(priv->acx, EXTEPHY_CTRL0); + value &= ~(EXTEPHY_CTRL0_RESET_INVALID | EXTEPHY_CTRL0_SYSCLK_GATING); + acx00_reg_write(priv->acx, EXTEPHY_CTRL0, value); + value = acx00_reg_read(priv->acx, EXTEPHY_CTRL1); + value &= ~(EXTEPHY_CTRL1_IO_EN_BITS); + acx00_reg_write(priv->acx, EXTEPHY_CTRL1, value); + value = acx00_reg_read(priv->acx, EPHY_CTRL); + value = EPHY_CTRL_DEFAULT; /* default value due to spec */ + acx00_reg_write(priv->acx, EPHY_CTRL, value); + + return 0; +} + +static int sunxi_phy_resume(struct device *dev) +{ + sunxi_ephy_enable(&ephy_priv); + + return 0; +} + +/* Suspend hook structures */ +static const struct dev_pm_ops sunxi_phy_pm_ops = { + .suspend = sunxi_phy_suspend, + .resume = sunxi_phy_resume, +}; + +static struct platform_driver ephy_plat_driver = { + .driver = { + .name = "acx-ephy", + .owner = THIS_MODULE, + .pm = &sunxi_phy_pm_ops, + }, + .probe = ephy_plat_probe, + .remove = ephy_plat_remove, + .id_table = sunxi_ephy_id, +}; + +static int ephy_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&ephy_plat_driver); + if (ret) + return -EINVAL; + + ret = phy_driver_register(&ephy_driver, THIS_MODULE); + if (ret) + platform_driver_unregister(&ephy_plat_driver); + + return ret; +} + +static void ephy_exit(void) +{ + if (ephy_priv.plat_drv) + platform_driver_unregister(&ephy_plat_driver); + + phy_driver_unregister(&ephy_driver); +} + +module_init(ephy_init); +module_exit(ephy_exit); + +static struct mdio_device_id __maybe_unused ephy_tbl[] = { + { 0x00441400, 0x0ffffff0 }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, ephy_tbl); + +MODULE_DESCRIPTION("Allwinner EPHY drivers"); +MODULE_AUTHOR("Sugar "); +MODULE_LICENSE("GPL");