1
0
mirror of https://github.com/libretro/Lakka-LibreELEC.git synced 2024-11-28 02:44:37 +00:00
Lakka-LibreELEC/projects/Amlogic/devices/AMLGX/patches/linux/amlogic-0109-WIP-net-phy-add-support-for-Maxio-MAE0621A.patch
2023-03-14 11:10:30 +00:00

364 lines
10 KiB
Diff

From dbcc56c1a71911850208663846c93f80307eb455 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Sun, 12 Feb 2023 10:45:54 +0000
Subject: [PATCH 109/120] WIP: net: phy: add support for Maxio MAE0621A
Maxio MAE0621A is the external PHY found in some Amlogic set-top box
devices based on the S905D chipset.
Signed-off-by: Zhao Yang <yang_zhao@maxio-tech.com>
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 7 +
drivers/net/phy/Kconfig | 5 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/maxio.c | 262 ++++++++++++++++++
drivers/net/phy/phy_device.c | 9 +
5 files changed, 284 insertions(+)
create mode 100644 drivers/net/phy/maxio.c
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 84e1740b12f1..ed48b963653e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -7477,6 +7477,8 @@ static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)
netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
}
+#define MAXIO_PHY_MAE0621A_ID 0x7b744411
+
/**
* stmmac_reset_queues_param - reset queue parameters
* @priv: device pointer
@@ -7553,6 +7555,11 @@ int stmmac_resume(struct device *dev)
stmmac_free_tx_skbufs(priv);
stmmac_clear_descriptors(priv, &priv->dma_conf);
+ if (ndev->phydev->drv->config_init) {
+ if (ndev->phydev->phy_id == MAXIO_PHY_MAE0621A_ID)
+ ndev->phydev->drv->config_init(ndev->phydev);
+ }
+
stmmac_hw_setup(ndev, false);
stmmac_init_coalesce(priv);
stmmac_set_rx_mode(ndev);
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index c57a0262fb64..9262cf995743 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -287,6 +287,11 @@ config AT803X_PHY
Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
QCA8337(Internal qca8k PHY) model
+config MAXIO_PHY
+ tristate "MAXIO PHYs"
+ help
+ Supports the Maxio MAExxxx PHYs.
+
config QSEMI_PHY
tristate "Quality Semiconductor PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index f7138d3c896b..11fbac4e4b6d 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_LXT_PHY) += lxt.o
obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o
+obj-$(CONFIG_MAXIO_PHY) += maxio.o
obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o
obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o
obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o
diff --git a/drivers/net/phy/maxio.c b/drivers/net/phy/maxio.c
new file mode 100644
index 000000000000..8d4db24b7ed1
--- /dev/null
+++ b/drivers/net/phy/maxio.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ^^ LICENSE TO BE CONFIRMED ^^
+ *
+ * Driver for Maxio PHYs
+ *
+ * Copyright (c) 2004 maxio technology, Inc.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+
+#define MAXIO_PAGE_SELECT 0x1f
+#define MAXIO_MAE0621A_INER 0x12
+#define MAXIO_MAE0621A_INER_LINK_STATUS BIT(4)
+#define MAXIO_MAE0621A_INSR 0x1d
+#define MAXIO_MAE0621A_TX_DELAY (BIT(6) | BIT(7))
+#define MAXIO_MAE0621A_RX_DELAY (BIT(4) | BIT(5))
+#define MAXIO_MAE0621A_CLK_MODE_REG 0x02
+#define MAXIO_MAE0621A_WORK_STATUS_REG 0x1d
+
+int maxio_read_paged(struct phy_device *phydev, int page, u32 regnum)
+{
+ int ret = 0, oldpage;
+
+ oldpage = phy_read(phydev, MAXIO_PAGE_SELECT);
+ if (oldpage >= 0) {
+ phy_write(phydev, MAXIO_PAGE_SELECT, page);
+ ret = phy_read(phydev, regnum);
+ }
+ phy_write(phydev, MAXIO_PAGE_SELECT, oldpage);
+
+ return ret;
+}
+
+int maxio_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
+{
+ int ret = 0, oldpage;
+
+ oldpage = phy_read(phydev, MAXIO_PAGE_SELECT);
+ if (oldpage >= 0) {
+ phy_write(phydev, MAXIO_PAGE_SELECT, page);
+ ret = phy_write(phydev, regnum, val);
+ }
+ phy_write(phydev, MAXIO_PAGE_SELECT, oldpage);
+
+ return ret;
+}
+
+static int maxio_mae0621a_clk_init(struct phy_device *phydev)
+{
+ u32 workmode, clkmode, oldpage;
+
+ oldpage = phy_read(phydev, MAXIO_PAGE_SELECT);
+ if (oldpage == 0xFFFF)
+ oldpage = phy_read(phydev, MAXIO_PAGE_SELECT);
+
+ /* soft reset */
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0x0);
+ phy_write(phydev, MII_BMCR, BMCR_RESET | phy_read(phydev, MII_BMCR));
+
+ /* get workmode */
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0xa43);
+ workmode = phy_read(phydev, MAXIO_MAE0621A_WORK_STATUS_REG);
+
+ /* get clkmode */
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0xd92);
+ clkmode = phy_read(phydev, MAXIO_MAE0621A_CLK_MODE_REG);
+
+ /* abnormal */
+ if (0 == (workmode & BIT(5))) {
+ if (0 == (clkmode & BIT(8))) {
+ /* oscillator */
+ phy_write(phydev, 0x02, clkmode | BIT(8));
+ pr_debug("maxio: mae0621a_clk_init clkmode 0x210a: 0x%x\n",
+ phydev->phy_id);
+ } else {
+ /* crystal */
+ pr_debug("maxio: mae0621a_clk_init clkmode 0x200a: 0x%x\n",
+ phydev->phy_id);
+ phy_write(phydev, 0x02, clkmode & (~BIT(8)));
+ }
+ }
+
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0x0);
+ phy_write(phydev, MAXIO_PAGE_SELECT, oldpage);
+
+ return 0;
+}
+
+static int maxio_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
+{
+ int ret = 0, oldpage;
+
+ oldpage = phy_read(phydev, MAXIO_PAGE_SELECT);
+
+ if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) {
+ /* eee info */
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0);
+ phy_write(phydev, 0xd, MDIO_MMD_AN);
+ phy_write(phydev, 0xe, MDIO_AN_EEE_ADV);
+ phy_write(phydev, 0xd, 0x4000 | MDIO_MMD_AN);
+ ret = phy_read(phydev, 0x0e);
+ } else {
+ ret = -EOPNOTSUPP;
+ }
+ phy_write(phydev, MAXIO_PAGE_SELECT, oldpage);
+
+ return ret;
+}
+
+static int maxio_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, u16 val)
+{
+ int ret = 0, oldpage;
+
+ oldpage = phy_read(phydev, MAXIO_PAGE_SELECT);
+
+ if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) {
+ /* eee info */
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0);
+ ret |= phy_write(phydev, 0xd, MDIO_MMD_AN);
+ ret |= phy_write(phydev, 0xe, MDIO_AN_EEE_ADV);
+ ret |= phy_write(phydev, 0xd, 0x4000 | MDIO_MMD_AN);
+ ret |= phy_write(phydev, 0xe, val);
+ msleep(100);
+ ret |= genphy_restart_aneg(phydev);
+ } else {
+ ret = -EOPNOTSUPP;
+ }
+ phy_write(phydev, MAXIO_PAGE_SELECT, oldpage);
+
+ return ret;
+}
+
+static int maxio_mae0621a_config_aneg(struct phy_device *phydev)
+{
+ return genphy_config_aneg(phydev);
+}
+
+static int maxio_mae0621a_config_init(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ u16 val;
+ int ret;
+ u32 broken = 0;
+
+ maxio_mae0621a_clk_init(phydev);
+
+ /* disable eee */
+ pr_debug("eee value: 0x%x\n", maxio_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV));
+ maxio_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0);
+ pr_debug("eee value: 0x%x\n", maxio_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV));
+ broken |= MDIO_EEE_100TX;
+ broken |= MDIO_EEE_1000T;
+ phydev->eee_broken_modes = broken;
+
+ /* enable auto_speed_down */
+ ret = maxio_write_paged(phydev, 0xd8f, 0x0, 0x300);
+
+ /* adjust tx/rx delay */
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ val = 0x0;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ val = MAXIO_MAE0621A_TX_DELAY | MAXIO_MAE0621A_RX_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ val = MAXIO_MAE0621A_RX_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ val = MAXIO_MAE0621A_TX_DELAY;
+ break;
+ default:
+ /* leave delays as-is. */
+ goto delay_skip;
+ }
+
+ ret = maxio_read_paged(phydev, 0xd96, 0x0);
+
+ if (ret < 0) {
+ dev_err(dev, "Failed to update the TX delay register\n");
+ return ret;
+ }
+ ret = maxio_write_paged(phydev, 0xd96, 0x0, val | ret);
+
+ if (ret < 0) {
+ dev_err(dev, "Failed to update the TX delay register\n");
+ return ret;
+ } else if (ret == 0) {
+ dev_dbg(dev,
+ "2ns delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
+ val ? "enabled" : "disabled");
+ }
+delay_skip:
+
+ phy_write(phydev, MII_BMCR, BMCR_RESET | phy_read(phydev, MII_BMCR));
+ msleep(20);
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0x0);
+
+ return 0;
+}
+
+static int maxio_mae0621a_resume(struct phy_device *phydev)
+{
+ int ret = genphy_resume(phydev);
+
+ ret |= phy_write(phydev, MII_BMCR, BMCR_RESET | phy_read(phydev, MII_BMCR));
+
+ msleep(20);
+
+ return ret;
+}
+
+int maxio_mae0621a_suspend(struct phy_device *phydev)
+{
+ genphy_suspend(phydev);
+ phy_write(phydev, MAXIO_PAGE_SELECT, 0);
+
+ return 0;
+}
+
+static int maxio_mae0621a_status(struct phy_device *phydev)
+{
+ return genphy_read_status(phydev);
+}
+
+static int maxio_mae0621a_probe(struct phy_device *phydev)
+{
+ int ret = maxio_mae0621a_clk_init(phydev);
+
+ mdelay(100);
+
+ return ret;
+}
+
+static struct phy_driver maxio_nc_drvs[] = {
+ {
+ .phy_id = 0x7b744411,
+ .phy_id_mask = 0x7fffffff,
+ .name = "MAE0621A Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ .probe = maxio_mae0621a_probe,
+ .config_init = maxio_mae0621a_config_init,
+ .config_aneg = maxio_mae0621a_config_aneg,
+ .read_status = maxio_mae0621a_status,
+ .suspend = maxio_mae0621a_suspend,
+ .resume = maxio_mae0621a_resume,
+ },
+};
+
+module_phy_driver(maxio_nc_drvs);
+
+MODULE_DESCRIPTION("Maxio PHY Driver");
+MODULE_AUTHOR("Zhao Yang <yang_zhao@maxio-tech.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8cff61dbc4b5..d27a396b0905 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -844,6 +844,15 @@ static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id)
{
int phy_reg;
+#ifdef CONFIG_MAXIO_PHY
+ /**
+ * An MDIO connects to multiple PHYs requiring write before read.
+ * This operation does not affect one MDIO connected to a single PHY
+ * MII_PHYSID2 is a read-only register and writing to it has no effect
+ */
+ mdiobus_write(bus, addr, MII_PHYSID2, 0);
+#endif
+
/* Grab the bits from PHYIR1, and put them in the upper half */
phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
if (phy_reg < 0) {
--
2.34.1