CONFIG_MDIO_BUS=y
CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_GPIO=y
-CONFIG_MDIO_IPQ40XX=y
+CONFIG_MDIO_IPQ4019=y
# CONFIG_MDM_GCC_9615 is not set
# CONFIG_MDM_LCC_9615 is not set
CONFIG_MEMFD_CREATE=y
--- /dev/null
+From 466ed24fb22342f3ae1c10758a6a0c6a8c081b2d Mon Sep 17 00:00:00 2001
+Date: Thu, 30 Apr 2020 11:07:05 +0200
+Subject: [PATCH] net: phy: mdio: add IPQ4019 MDIO driver
+
+This patch adds the driver for the MDIO interface
+inside of Qualcomm IPQ40xx series SoC-s.
+
+---
+ drivers/net/phy/Kconfig | 7 ++
+ drivers/net/phy/Makefile | 1 +
+ drivers/net/phy/mdio-ipq4019.c | 160 +++++++++++++++++++++++++++++++++
+ 3 files changed, 168 insertions(+)
+ create mode 100644 drivers/net/phy/mdio-ipq4019.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -156,6 +156,13 @@ config MDIO_I2C
+
+ This is library mode.
+
++config MDIO_IPQ4019
++ tristate "Qualcomm IPQ4019 MDIO interface support"
++ depends on HAS_IOMEM && OF_MDIO
++ help
++ This driver supports the MDIO interface found in Qualcomm
++ IPQ40xx series Soc-s.
++
+ config MDIO_MOXART
+ tristate "MOXA ART MDIO interface support"
+ depends on ARCH_MOXART || COMPILE_TEST
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -50,6 +50,7 @@ obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium
+ obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
+ obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o
+ obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o
++obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
+ obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
+ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
+ obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
+--- /dev/null
++++ b/drivers/net/phy/mdio-ipq4019.c
+@@ -0,0 +1,160 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved. */
++/* Copyright (c) 2020 Sartura Ltd. */
++
++#include <linux/delay.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/of_address.h>
++#include <linux/of_mdio.h>
++#include <linux/phy.h>
++#include <linux/platform_device.h>
++
++#define MDIO_ADDR_REG 0x44
++#define MDIO_DATA_WRITE_REG 0x48
++#define MDIO_DATA_READ_REG 0x4c
++#define MDIO_CMD_REG 0x50
++#define MDIO_CMD_ACCESS_BUSY BIT(16)
++#define MDIO_CMD_ACCESS_START BIT(8)
++#define MDIO_CMD_ACCESS_CODE_READ 0
++#define MDIO_CMD_ACCESS_CODE_WRITE 1
++
++#define ipq4019_MDIO_TIMEOUT 10000
++#define ipq4019_MDIO_SLEEP 10
++
++struct ipq4019_mdio_data {
++ void __iomem *membase;
++};
++
++static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
++{
++ struct ipq4019_mdio_data *priv = bus->priv;
++ unsigned int busy;
++
++ return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy,
++ (busy & MDIO_CMD_ACCESS_BUSY) == 0,
++ ipq4019_MDIO_SLEEP, ipq4019_MDIO_TIMEOUT);
++}
++
++static int ipq4019_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
++{
++ struct ipq4019_mdio_data *priv = bus->priv;
++ unsigned int cmd;
++
++ /* Reject clause 45 */
++ if (regnum & MII_ADDR_C45)
++ return -EOPNOTSUPP;
++
++ if (ipq4019_mdio_wait_busy(bus))
++ return -ETIMEDOUT;
++
++ /* issue the phy address and reg */
++ writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
++
++ cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
++
++ /* issue read command */
++ writel(cmd, priv->membase + MDIO_CMD_REG);
++
++ /* Wait read complete */
++ if (ipq4019_mdio_wait_busy(bus))
++ return -ETIMEDOUT;
++
++ /* Read and return data */
++ return readl(priv->membase + MDIO_DATA_READ_REG);
++}
++
++static int ipq4019_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
++ u16 value)
++{
++ struct ipq4019_mdio_data *priv = bus->priv;
++ unsigned int cmd;
++
++ /* Reject clause 45 */
++ if (regnum & MII_ADDR_C45)
++ return -EOPNOTSUPP;
++
++ if (ipq4019_mdio_wait_busy(bus))
++ return -ETIMEDOUT;
++
++ /* issue the phy address and reg */
++ writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
++
++ /* issue write data */
++ writel(value, priv->membase + MDIO_DATA_WRITE_REG);
++
++ cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
++ /* issue write command */
++ writel(cmd, priv->membase + MDIO_CMD_REG);
++
++ /* Wait write complete */
++ if (ipq4019_mdio_wait_busy(bus))
++ return -ETIMEDOUT;
++
++ return 0;
++}
++
++static int ipq4019_mdio_probe(struct platform_device *pdev)
++{
++ struct ipq4019_mdio_data *priv;
++ struct mii_bus *bus;
++ int ret;
++
++ bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));
++ if (!bus)
++ return -ENOMEM;
++
++ priv = bus->priv;
++
++ priv->membase = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(priv->membase))
++ return PTR_ERR(priv->membase);
++
++ bus->name = "ipq4019_mdio";
++ bus->read = ipq4019_mdio_read;
++ bus->write = ipq4019_mdio_write;
++ bus->parent = &pdev->dev;
++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);
++
++ ret = of_mdiobus_register(bus, pdev->dev.of_node);
++ if (ret) {
++ dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
++ return ret;
++ }
++
++ platform_set_drvdata(pdev, bus);
++
++ return 0;
++}
++
++static int ipq4019_mdio_remove(struct platform_device *pdev)
++{
++ struct mii_bus *bus = platform_get_drvdata(pdev);
++
++ mdiobus_unregister(bus);
++
++ return 0;
++}
++
++static const struct of_device_id ipq4019_mdio_dt_ids[] = {
++ { .compatible = "qcom,ipq4019-mdio" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids);
++
++static struct platform_driver ipq4019_mdio_driver = {
++ .probe = ipq4019_mdio_probe,
++ .remove = ipq4019_mdio_remove,
++ .driver = {
++ .name = "ipq4019-mdio",
++ .of_match_table = ipq4019_mdio_dt_ids,
++ },
++};
++
++module_platform_driver(ipq4019_mdio_driver);
++
++MODULE_DESCRIPTION("ipq4019 MDIO interface driver");
++MODULE_AUTHOR("Qualcomm Atheros");
++MODULE_LICENSE("Dual BSD/GPL");
--- /dev/null
+From 9c8c0f70ec6fdac2398632c723c48277be09b7c0 Mon Sep 17 00:00:00 2001
+Date: Thu, 30 Apr 2020 11:07:07 +0200
+Subject: [PATCH] ARM: dts: qcom: ipq4019: add MDIO node
+
+This patch adds the necessary MDIO interface node
+to the Qualcomm IPQ4019 DTSI.
+
+Built-in QCA8337N switch is managed using it,
+and since we have a driver for it lets add it.
+
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -577,5 +577,33 @@
+ "legacy";
+ status = "disabled";
+ };
++
++ mdio: mdio@90000 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "qcom,ipq4019-mdio";
++ reg = <0x90000 0x64>;
++ status = "disabled";
++
++ ethphy0: ethernet-phy@0 {
++ reg = <0>;
++ };
++
++ ethphy1: ethernet-phy@1 {
++ reg = <1>;
++ };
++
++ ethphy2: ethernet-phy@2 {
++ reg = <2>;
++ };
++
++ ethphy3: ethernet-phy@3 {
++ reg = <3>;
++ };
++
++ ethphy4: ethernet-phy@4 {
++ reg = <4>;
++ };
++ };
+ };
+ };
+++ /dev/null
-From 9c8c0f70ec6fdac2398632c723c48277be09b7c0 Mon Sep 17 00:00:00 2001
-Date: Thu, 30 Apr 2020 11:07:07 +0200
-Subject: [PATCH] ARM: dts: qcom: ipq4019: add MDIO node
-
-This patch adds the necessary MDIO interface node
-to the Qualcomm IPQ4019 DTSI.
-
-Built-in QCA8337N switch is managed using it,
-and since we have a driver for it lets add it.
-
----
- arch/arm/boot/dts/qcom-ipq4019.dtsi | 28 ++++++++++++++++++++++++++++
- 1 file changed, 28 insertions(+)
-
---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
-+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
-@@ -577,5 +577,33 @@
- "legacy";
- status = "disabled";
- };
-+
-+ mdio: mdio@90000 {
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+ compatible = "qcom,ipq4019-mdio";
-+ reg = <0x90000 0x64>;
-+ status = "disabled";
-+
-+ ethphy0: ethernet-phy@0 {
-+ reg = <0>;
-+ };
-+
-+ ethphy1: ethernet-phy@1 {
-+ reg = <1>;
-+ };
-+
-+ ethphy2: ethernet-phy@2 {
-+ reg = <2>;
-+ };
-+
-+ ethphy3: ethernet-phy@3 {
-+ reg = <3>;
-+ };
-+
-+ ethphy4: ethernet-phy@4 {
-+ reg = <4>;
-+ };
-+ };
- };
- };
+++ /dev/null
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -577,6 +577,13 @@ config XILINX_GMII2RGMII
- the Reduced Gigabit Media Independent Interface(RGMII) between
- Ethernet physical media devices and the Gigabit Ethernet controller.
-
-+config MDIO_IPQ40XX
-+ tristate "Qualcomm Atheros ipq40xx MDIO interface"
-+ depends on HAS_IOMEM && OF
-+ ---help---
-+ This driver supports the MDIO interface found in Qualcomm
-+ Atheros ipq40xx Soc chip.
-+
- endif # PHYLIB
-
- config MICREL_KS8995MA
---- a/drivers/net/phy/Makefile
-+++ b/drivers/net/phy/Makefile
-@@ -50,6 +50,7 @@ obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium
- obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
- obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o
- obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o
-+obj-$(CONFIG_MDIO_IPQ40XX) += mdio-ipq40xx.o
- obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
- obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
- obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
---- /dev/null
-+++ b/drivers/net/phy/mdio-ipq40xx.c
-@@ -0,0 +1,196 @@
-+/*
-+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
-+ *
-+ * Permission to use, copy, modify, and/or distribute this software for
-+ * any purpose with or without fee is hereby granted, provided that the
-+ * above copyright notice and this permission notice appear in all copies.
-+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-+ */
-+
-+#include <linux/delay.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/mutex.h>
-+#include <linux/io.h>
-+#include <linux/of_address.h>
-+#include <linux/of_mdio.h>
-+#include <linux/phy.h>
-+#include <linux/platform_device.h>
-+
-+#define MDIO_CTRL_0_REG 0x40
-+#define MDIO_CTRL_1_REG 0x44
-+#define MDIO_CTRL_2_REG 0x48
-+#define MDIO_CTRL_3_REG 0x4c
-+#define MDIO_CTRL_4_REG 0x50
-+#define MDIO_CTRL_4_ACCESS_BUSY BIT(16)
-+#define MDIO_CTRL_4_ACCESS_START BIT(8)
-+#define MDIO_CTRL_4_ACCESS_CODE_READ 0
-+#define MDIO_CTRL_4_ACCESS_CODE_WRITE 1
-+#define CTRL_0_REG_DEFAULT_VALUE 0x150FF
-+
-+#define IPQ40XX_MDIO_RETRY 1000
-+#define IPQ40XX_MDIO_DELAY 10
-+
-+struct ipq40xx_mdio_data {
-+ struct mii_bus *mii_bus;
-+ void __iomem *membase;
-+ struct device *dev;
-+};
-+
-+static int ipq40xx_mdio_wait_busy(struct ipq40xx_mdio_data *am)
-+{
-+ int i;
-+
-+ for (i = 0; i < IPQ40XX_MDIO_RETRY; i++) {
-+ unsigned int busy;
-+
-+ busy = readl(am->membase + MDIO_CTRL_4_REG) &
-+ MDIO_CTRL_4_ACCESS_BUSY;
-+ if (!busy)
-+ return 0;
-+
-+ /* BUSY might take to be cleard by 15~20 times of loop */
-+ udelay(IPQ40XX_MDIO_DELAY);
-+ }
-+
-+ dev_err(am->dev, "%s: MDIO operation timed out\n", am->mii_bus->name);
-+
-+ return -ETIMEDOUT;
-+}
-+
-+static int ipq40xx_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
-+{
-+ struct ipq40xx_mdio_data *am = bus->priv;
-+ int value = 0;
-+ unsigned int cmd = 0;
-+
-+ lockdep_assert_held(&bus->mdio_lock);
-+
-+ if (ipq40xx_mdio_wait_busy(am))
-+ return -ETIMEDOUT;
-+
-+ /* issue the phy address and reg */
-+ writel((mii_id << 8) | regnum, am->membase + MDIO_CTRL_1_REG);
-+
-+ cmd = MDIO_CTRL_4_ACCESS_START|MDIO_CTRL_4_ACCESS_CODE_READ;
-+
-+ /* issue read command */
-+ writel(cmd, am->membase + MDIO_CTRL_4_REG);
-+
-+ /* Wait read complete */
-+ if (ipq40xx_mdio_wait_busy(am))
-+ return -ETIMEDOUT;
-+
-+ /* Read data */
-+ value = readl(am->membase + MDIO_CTRL_3_REG);
-+
-+ return value;
-+}
-+
-+static int ipq40xx_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
-+ u16 value)
-+{
-+ struct ipq40xx_mdio_data *am = bus->priv;
-+ unsigned int cmd = 0;
-+
-+ lockdep_assert_held(&bus->mdio_lock);
-+
-+ if (ipq40xx_mdio_wait_busy(am))
-+ return -ETIMEDOUT;
-+
-+ /* issue the phy address and reg */
-+ writel((mii_id << 8) | regnum, am->membase + MDIO_CTRL_1_REG);
-+
-+ /* issue write data */
-+ writel(value, am->membase + MDIO_CTRL_2_REG);
-+
-+ cmd = MDIO_CTRL_4_ACCESS_START|MDIO_CTRL_4_ACCESS_CODE_WRITE;
-+ /* issue write command */
-+ writel(cmd, am->membase + MDIO_CTRL_4_REG);
-+
-+ /* Wait write complete */
-+ if (ipq40xx_mdio_wait_busy(am))
-+ return -ETIMEDOUT;
-+
-+ return 0;
-+}
-+
-+static int ipq40xx_mdio_probe(struct platform_device *pdev)
-+{
-+ struct ipq40xx_mdio_data *am;
-+ struct resource *res;
-+ int i;
-+
-+ am = devm_kzalloc(&pdev->dev, sizeof(*am), GFP_KERNEL);
-+ if (!am)
-+ return -ENOMEM;
-+
-+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+ if (!res) {
-+ dev_err(&pdev->dev, "no iomem resource found\n");
-+ return -ENXIO;
-+ }
-+
-+ am->membase = devm_ioremap_resource(&pdev->dev, res);
-+ if (IS_ERR(am->membase)) {
-+ dev_err(&pdev->dev, "unable to ioremap registers\n");
-+ return PTR_ERR(am->membase);
-+ }
-+
-+ am->mii_bus = devm_mdiobus_alloc(&pdev->dev);
-+ if (!am->mii_bus)
-+ return -ENOMEM;
-+
-+ writel(CTRL_0_REG_DEFAULT_VALUE, am->membase + MDIO_CTRL_0_REG);
-+
-+ am->mii_bus->name = "ipq40xx_mdio";
-+ am->mii_bus->read = ipq40xx_mdio_read;
-+ am->mii_bus->write = ipq40xx_mdio_write;
-+ am->mii_bus->priv = am;
-+ am->mii_bus->parent = &pdev->dev;
-+ snprintf(am->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(&pdev->dev));
-+
-+ am->dev = &pdev->dev;
-+ platform_set_drvdata(pdev, am);
-+
-+ return of_mdiobus_register(am->mii_bus, pdev->dev.of_node);
-+}
-+
-+static int ipq40xx_mdio_remove(struct platform_device *pdev)
-+{
-+ struct ipq40xx_mdio_data *am = platform_get_drvdata(pdev);
-+
-+ mdiobus_unregister(am->mii_bus);
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id ipq40xx_mdio_dt_ids[] = {
-+ { .compatible = "qcom,ipq4019-mdio" },
-+ { }
-+};
-+MODULE_DEVICE_TABLE(of, ipq40xx_mdio_dt_ids);
-+
-+static struct platform_driver ipq40xx_mdio_driver = {
-+ .probe = ipq40xx_mdio_probe,
-+ .remove = ipq40xx_mdio_remove,
-+ .driver = {
-+ .name = "ipq40xx-mdio",
-+ .of_match_table = ipq40xx_mdio_dt_ids,
-+ },
-+};
-+
-+module_platform_driver(ipq40xx_mdio_driver);
-+
-+#define DRV_VERSION "1.0"
-+
-+MODULE_DESCRIPTION("IPQ40XX MDIO interface driver");
-+MODULE_AUTHOR("Qualcomm Atheros");
-+MODULE_VERSION(DRV_VERSION);
-+MODULE_LICENSE("Dual BSD/GPL");