diff mbox series

[RFC,net-next,v4,28/28] net: phy: add qca8k driver for qca8k switch internal PHY

Message ID 20210508002920.19945-28-ansuelsmth@gmail.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [RFC,net-next,v4,01/28] net: mdio: ipq8064: clean whitespaces in define | expand

Checks

Context Check Description
netdev/apply fail Patch does not apply to net-next
netdev/tree_selection success Clearly marked for net-next

Commit Message

Christian Marangi May 8, 2021, 12:29 a.m. UTC
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@gmail.com>
---
 drivers/net/phy/Kconfig  |   7 ++
 drivers/net/phy/Makefile |   1 +
 drivers/net/phy/qca8k.c  | 172 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+)
 create mode 100644 drivers/net/phy/qca8k.c

Comments

Qingfang Deng May 8, 2021, 4:35 a.m. UTC | #1
On Sat, May 08, 2021 at 02:29:18AM +0200, Ansuel Smith wrote:
> 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.

I think this can be merged into at803x.c, as they have almost the same
registers, and some features such as interrupt handler and cable test
can be reused.

> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
Christian Marangi May 8, 2021, 11:30 a.m. UTC | #2
On Sat, May 08, 2021 at 12:35:35PM +0800, DENG Qingfang wrote:
> On Sat, May 08, 2021 at 02:29:18AM +0200, Ansuel Smith wrote:
> > 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.
> 
> I think this can be merged into at803x.c, as they have almost the same
> registers, and some features such as interrupt handler and cable test
> can be reused.
>

Wouldn't this be a little bit confusing? But actually yes... interrupt
handler and cable test have the same regs. My main concern is about the
phy_dev flags and the dbg regs that I think are different and would
create some confusion. If this It's not a proble, sure I can rework this
a put in the at803x.c phy driver.

> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
Florian Fainelli May 8, 2021, 3:45 p.m. UTC | #3
On 5/8/2021 4:30 AM, Ansuel Smith wrote:
> On Sat, May 08, 2021 at 12:35:35PM +0800, DENG Qingfang wrote:
>> On Sat, May 08, 2021 at 02:29:18AM +0200, Ansuel Smith wrote:
>>> 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.
>>
>> I think this can be merged into at803x.c, as they have almost the same
>> registers, and some features such as interrupt handler and cable test
>> can be reused.
>>
> 
> Wouldn't this be a little bit confusing? But actually yes... interrupt
> handler and cable test have the same regs. My main concern is about the
> phy_dev flags and the dbg regs that I think are different and would
> create some confusion. If this It's not a proble, sure I can rework this
> a put in the at803x.c phy driver.

Consistency is key, and having all of your PHY eggs in the same PHY
driver basket is easier for maintenance and generalizing features.
drivers/net/phy/broadcom.c contains PHY entries for integrated PHY
switches, too (5395, 53125). As far as phydev::dev_flags, you can skip
interpreting those unless you match the QCA8K PHY driver entry/probe.
diff mbox series

Patch

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..53bbd506d184
--- /dev/null
+++ b/drivers/net/phy/qca8k.c
@@ -0,0 +1,172 @@ 
+// 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)
+{
+	bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
+	bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
+}
+
+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");