mirror of
https://github.com/libretro/Lakka-LibreELEC.git
synced 2024-11-28 02:44:37 +00:00
364 lines
10 KiB
Diff
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
|
|
|