[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20210504222915.17206-20-ansuelsmth@gmail.com>
Date: Wed, 5 May 2021 00:29:14 +0200
From: Ansuel Smith <ansuelsmth@...il.com>
To: Florian Fainelli <f.fainelli@...il.com>
Cc: Ansuel Smith <ansuelsmth@...il.com>, Andrew Lunn <andrew@...n.ch>,
Heiner Kallweit <hkallweit1@...il.com>,
Russell King <linux@...linux.org.uk>,
"David S. Miller" <davem@...emloft.net>,
Jakub Kicinski <kuba@...nel.org>, linux-kernel@...r.kernel.org,
netdev@...r.kernel.org
Subject: [RFC PATCH net-next v3 20/20] net: phy: add qca8k driver for qca8k switch internal PHY
Add initial support for qca8k internal PHYs. The internal PHYs requires
special mmd and debug values to be set based on the switch revision
passwd using the dev_flags. Supports output of idle, receive and eee_wake
errors stats.
Some debug values sets can't be translated as the documentation lacks any
reference about them.
Signed-off-by: Ansuel Smith <ansuelsmth@...il.com>
---
drivers/net/phy/Kconfig | 7 ++
drivers/net/phy/Makefile | 1 +
drivers/net/phy/qca8k.c | 174 +++++++++++++++++++++++++++++++++++++++
3 files changed, 182 insertions(+)
create mode 100644 drivers/net/phy/qca8k.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 698bea312adc..cdf01613eb37 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -245,6 +245,13 @@ config QSEMI_PHY
help
Currently supports the qs6612
+config QCA8K_PHY
+ tristate "Qualcomm Atheros AR833x Internal PHYs"
+ help
+ This PHY is for the internal PHYs present on the QCA833x switch.
+
+ Currently supports the AR8334, AR8337 model
+
config REALTEK_PHY
tristate "Realtek PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index a13e402074cf..5f3cfd5606bb 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc/
obj-$(CONFIG_NATIONAL_PHY) += national.o
obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
+obj-$(CONFIG_QCA8K_PHY) += qca8k.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o
obj-$(CONFIG_RENESAS_PHY) += uPD60620.o
obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o
diff --git a/drivers/net/phy/qca8k.c b/drivers/net/phy/qca8k.c
new file mode 100644
index 000000000000..514250bb9e71
--- /dev/null
+++ b/drivers/net/phy/qca8k.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool_netlink.h>
+
+#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
+
+#define QCA8K_PHY_ID_MASK 0xffffffff
+#define QCA8K_PHY_ID_QCA8327 0x004dd034
+#define QCA8K_PHY_ID_QCA8337 0x004dd036
+
+#define MDIO_AZ_DEBUG 0x800d
+
+#define MDIO_DBG_ANALOG_TEST 0x0
+#define MDIO_DBG_SYSTEM_CONTROL_MODE 0x5
+#define MDIO_DBG_CONTROL_FEATURE_CONF 0x3d
+
+/* QCA specific MII registers */
+#define MII_ATH_DBG_ADDR 0x1d
+#define MII_ATH_DBG_DATA 0x1e
+
+/* QCA specific MII registers access function */
+static void qca8k_phy_dbg_write(struct mii_bus *bus, int phy_addr, u16 dbg_addr, u16 dbg_data)
+{
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
+ bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
+ mutex_unlock(&bus->mdio_lock);
+}
+
+enum stat_access_type {
+ PHY,
+ MMD
+};
+
+struct qca8k_hw_stat {
+ const char *string;
+ u8 reg;
+ u32 mask;
+ enum stat_access_type access_type;
+};
+
+static struct qca8k_hw_stat qca8k_hw_stats[] = {
+ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
+ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
+ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
+};
+
+struct qca8k_phy_priv {
+ u8 switch_revision;
+ u64 stats[ARRAY_SIZE(qca8k_hw_stats)];
+};
+
+static int qca8k_get_sset_count(struct phy_device *phydev)
+{
+ return ARRAY_SIZE(qca8k_hw_stats);
+}
+
+static void qca8k_get_strings(struct phy_device *phydev, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(qca8k_hw_stats); i++) {
+ strscpy(data + i * ETH_GSTRING_LEN,
+ qca8k_hw_stats[i].string, ETH_GSTRING_LEN);
+ }
+}
+
+static u64 qca8k_get_stat(struct phy_device *phydev, int i)
+{
+ struct qca8k_hw_stat stat = qca8k_hw_stats[i];
+ struct qca8k_phy_priv *priv = phydev->priv;
+ int val;
+ u64 ret;
+
+ if (stat.access_type == MMD)
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
+ else
+ val = phy_read(phydev, stat.reg);
+
+ if (val < 0) {
+ ret = U64_MAX;
+ } else {
+ val = val & stat.mask;
+ priv->stats[i] += val;
+ ret = priv->stats[i];
+ }
+
+ return ret;
+}
+
+static void qca8k_get_stats(struct phy_device *phydev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(qca8k_hw_stats); i++)
+ data[i] = qca8k_get_stat(phydev, i);
+}
+
+static int qca8k_config_init(struct phy_device *phydev)
+{
+ struct qca8k_phy_priv *priv = phydev->priv;
+ struct mii_bus *bus = phydev->mdio.bus;
+ int phy_addr = phydev->mdio.addr;
+
+ priv->switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
+
+ switch (priv->switch_revision) {
+ case 1:
+ /* For 100M waveform */
+ qca8k_phy_dbg_write(bus, phy_addr, MDIO_DBG_ANALOG_TEST, 0x02ea);
+ /* Turn on Gigabit clock */
+ qca8k_phy_dbg_write(bus, phy_addr, MDIO_DBG_CONTROL_FEATURE_CONF, 0x68a0);
+ break;
+
+ case 2:
+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
+ fallthrough;
+ case 4:
+ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
+ qca8k_phy_dbg_write(bus, phy_addr, MDIO_DBG_CONTROL_FEATURE_CONF, 0x6860);
+ qca8k_phy_dbg_write(bus, phy_addr, MDIO_DBG_SYSTEM_CONTROL_MODE, 0x2c46);
+ qca8k_phy_dbg_write(bus, phy_addr, 0x3c, 0x6000);
+ break;
+ }
+
+ return 0;
+}
+
+static int qca8k_probe(struct phy_device *phydev)
+{
+ struct qca8k_phy_priv *priv;
+
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
+static struct phy_driver qca8k_drivers[] = {
+ {
+ .phy_id = QCA8K_PHY_ID_QCA8337,
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
+ .name = "QCA PHY 8337",
+ /* PHY_GBIT_FEATURES */
+ .probe = qca8k_probe,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca8k_config_init,
+ .soft_reset = genphy_soft_reset,
+ .get_sset_count = qca8k_get_sset_count,
+ .get_strings = qca8k_get_strings,
+ .get_stats = qca8k_get_stats,
+ },
+};
+
+module_phy_driver(qca8k_drivers);
+
+static struct mdio_device_id __maybe_unused qca8k_tbl[] = {
+ { QCA8K_PHY_ID_QCA8337, QCA8K_PHY_ID_MASK },
+ { }
+};
+
+MODULE_DEVICE_TABLE(mdio, qca8k_tbl);
+MODULE_DESCRIPTION("Qualcomm QCA8k PHY driver");
+MODULE_AUTHOR("Ansuel Smith");
+MODULE_LICENSE("GPL");
--
2.30.2
Powered by blists - more mailing lists