* [PATCH v3 0/3] add hisilicon hip04 ethernet driver
@ 2014-03-24 14:14 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
v3:
Suggest from Arnd, use syscon & regmap_write/read to replace static void __iomem *ppebase.
Modify hisilicon-hip04-net.txt accrordingly to suggestion from Florian and Sergei.
v2:
Got many suggestions from Russell, Arnd, Florian, Mark and Sergei
Remove memcpy, use dma_map/unmap_single, use dma_alloc_coherent rather than dma_pool, etc.
Refer property in ethernet.txt, change ppe description, etc.
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 110 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++
7 files changed, 1063 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH v3 0/3] add hisilicon hip04 ethernet driver
@ 2014-03-24 14:14 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: linux-arm-kernel
v3:
Suggest from Arnd, use syscon & regmap_write/read to replace static void __iomem *ppebase.
Modify hisilicon-hip04-net.txt accrordingly to suggestion from Florian and Sergei.
v2:
Got many suggestions from Russell, Arnd, Florian, Mark and Sergei
Remove memcpy, use dma_map/unmap_single, use dma_alloc_coherent rather than dma_pool, etc.
Refer property in ethernet.txt, change ppe description, etc.
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 110 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++
7 files changed, 1063 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 1/3] Documentation: add Device tree bindings for Hisilicon hip04 ethernet
2014-03-24 14:14 ` Zhangfei Gao
@ 2014-03-24 14:14 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
This patch adds the Device Tree bindings for the Hisilicon hip04
Ethernet controller, including 100M / 1000M controller.
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
.../bindings/net/hisilicon-hip04-net.txt | 110 ++++++++++++++++++++
1 file changed, 110 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
new file mode 100644
index 0000000..bd8ad3b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
@@ -0,0 +1,110 @@
+Hisilicon hip04 Ethernet Controller
+
+* Ethernet controller node
+
+Required properties:
+- compatible: should be "hisilicon,hip04-mac".
+- reg: address and length of the register set for the device.
+- interrupts: interrupt for the device.
+- port-handle: phandle, specifies a reference to a node representing
+ the connected port
+- phy-mode: see ethernet.txt [1].
+
+Optional properties:
+- phy-handle: see ethernet.txt [1].
+
+[1] Documentation/devicetree/bindings/net/ethernet.txt
+
+
+* Ethernet ppe node:
+Control rx & tx fifos of all ethernet controllers.
+Have 2048 recv channels shared by all ethernet controllers, only if no overlap.
+
+Required properties:
+- #address-cells : Should be <1>
+- #size-cells : Should be <0>
+- compatible: "hisilicon,hip04-ppe", "syscon".
+- reg: address and length of the register set for the device.
+
+==Child node==
+
+Required properties:
+- reg: port physical number, range from 0 to 0x1f.
+- channel: recv channel start from channel * number (RX_DESC_NUM).
+
+
+* MDIO bus node:
+
+Required properties:
+
+- compatible: should be "hisilicon,hip04-mdio".
+- Inherits from MDIO bus node binding [2]
+[2] Documentation/devicetree/bindings/net/phy.txt
+
+Example:
+ mdio {
+ compatible = "hisilicon,hip04-mdio";
+ reg = <0x28f1000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy0: ethernet-phy@0 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <0>;
+ marvell,reg-init = <18 0x14 0 0x8001>;
+ };
+
+ phy1: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ marvell,reg-init = <18 0x14 0 0x8001>;
+ };
+ };
+
+ ppe: ppe@28c0000 {
+ compatible = "hisilicon,hip04-ppe", "syscon";
+ reg = <0x28c0000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth0_port: port@1f {
+ reg = <0x1f>;
+ channel = <0>;
+ };
+
+ eth1_port: port@0 {
+ reg = <0>;
+ channel = <1>;
+ };
+
+ eth2_port: port@8 {
+ reg = <8>;
+ channel = <2>;
+ };
+ };
+
+ fe: ethernet@28b0000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x28b0000 0x10000>;
+ interrupts = <0 413 4>;
+ phy-mode = "mii";
+ port-handle = <ð0_port>;
+ };
+
+ ge0: ethernet@2800000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x2800000 0x10000>;
+ interrupts = <0 402 4>;
+ phy-mode = "sgmii";
+ port-handle = <ð1_port>;
+ phy-handle = <&phy0>;
+ };
+
+ ge8: ethernet@2880000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x2880000 0x10000>;
+ interrupts = <0 410 4>;
+ phy-mode = "sgmii";
+ port-handle = <ð2_port>;
+ phy-handle = <&phy1>;
+ };
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 1/3] Documentation: add Device tree bindings for Hisilicon hip04 ethernet
@ 2014-03-24 14:14 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds the Device Tree bindings for the Hisilicon hip04
Ethernet controller, including 100M / 1000M controller.
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
.../bindings/net/hisilicon-hip04-net.txt | 110 ++++++++++++++++++++
1 file changed, 110 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
new file mode 100644
index 0000000..bd8ad3b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
@@ -0,0 +1,110 @@
+Hisilicon hip04 Ethernet Controller
+
+* Ethernet controller node
+
+Required properties:
+- compatible: should be "hisilicon,hip04-mac".
+- reg: address and length of the register set for the device.
+- interrupts: interrupt for the device.
+- port-handle: phandle, specifies a reference to a node representing
+ the connected port
+- phy-mode: see ethernet.txt [1].
+
+Optional properties:
+- phy-handle: see ethernet.txt [1].
+
+[1] Documentation/devicetree/bindings/net/ethernet.txt
+
+
+* Ethernet ppe node:
+Control rx & tx fifos of all ethernet controllers.
+Have 2048 recv channels shared by all ethernet controllers, only if no overlap.
+
+Required properties:
+- #address-cells : Should be <1>
+- #size-cells : Should be <0>
+- compatible: "hisilicon,hip04-ppe", "syscon".
+- reg: address and length of the register set for the device.
+
+==Child node==
+
+Required properties:
+- reg: port physical number, range from 0 to 0x1f.
+- channel: recv channel start from channel * number (RX_DESC_NUM).
+
+
+* MDIO bus node:
+
+Required properties:
+
+- compatible: should be "hisilicon,hip04-mdio".
+- Inherits from MDIO bus node binding [2]
+[2] Documentation/devicetree/bindings/net/phy.txt
+
+Example:
+ mdio {
+ compatible = "hisilicon,hip04-mdio";
+ reg = <0x28f1000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy0: ethernet-phy at 0 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <0>;
+ marvell,reg-init = <18 0x14 0 0x8001>;
+ };
+
+ phy1: ethernet-phy at 1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ marvell,reg-init = <18 0x14 0 0x8001>;
+ };
+ };
+
+ ppe: ppe at 28c0000 {
+ compatible = "hisilicon,hip04-ppe", "syscon";
+ reg = <0x28c0000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth0_port: port at 1f {
+ reg = <0x1f>;
+ channel = <0>;
+ };
+
+ eth1_port: port at 0 {
+ reg = <0>;
+ channel = <1>;
+ };
+
+ eth2_port: port at 8 {
+ reg = <8>;
+ channel = <2>;
+ };
+ };
+
+ fe: ethernet at 28b0000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x28b0000 0x10000>;
+ interrupts = <0 413 4>;
+ phy-mode = "mii";
+ port-handle = <ð0_port>;
+ };
+
+ ge0: ethernet at 2800000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x2800000 0x10000>;
+ interrupts = <0 402 4>;
+ phy-mode = "sgmii";
+ port-handle = <ð1_port>;
+ phy-handle = <&phy0>;
+ };
+
+ ge8: ethernet at 2880000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x2880000 0x10000>;
+ interrupts = <0 410 4>;
+ phy-mode = "sgmii";
+ port-handle = <ð2_port>;
+ phy-handle = <&phy1>;
+ };
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
[parent not found: <1395670496-17381-1-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>]
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-24 14:14 ` Zhangfei Gao
@ 2014-03-24 14:14 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linux-lFZ/pmaqli7XmaaqVzeoHQ,
arnd-r2nGTMty4D4, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8,
mark.rutland-5wv7dgnIgG8
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
Zhangfei Gao
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 225 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..b0ee739
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,32 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-24 14:14 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: linux-arm-kernel
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 225 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..b0ee739
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,32 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-24 14:14 ` Zhangfei Gao
@ 2014-03-24 14:14 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Support Hisilicon hip04 ethernet driver, including 100M / 1000M controller
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/hisilicon/Makefile | 2 +-
drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++++++++++
2 files changed, 729 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
index 1d6eb6e..e6fe7af 100644
--- a/drivers/net/ethernet/hisilicon/Makefile
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -2,4 +2,4 @@
# Makefile for the HISILICON network device drivers.
#
-obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
+obj-$(CONFIG_HIP04_ETH) += hip04_eth.o hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
new file mode 100644
index 0000000..c1131b2
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -0,0 +1,728 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/phy.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define PPE_CFG_RX_ADDR 0x100
+#define PPE_CFG_POOL_GRP 0x300
+#define PPE_CFG_RX_BUF_SIZE 0x400
+#define PPE_CFG_RX_FIFO_SIZE 0x500
+#define PPE_CURR_BUF_CNT 0xa200
+
+#define GE_DUPLEX_TYPE 0x8
+#define GE_MAX_FRM_SIZE_REG 0x3c
+#define GE_PORT_MODE 0x40
+#define GE_PORT_EN 0x44
+#define GE_SHORT_RUNTS_THR_REG 0x50
+#define GE_TX_LOCAL_PAGE_REG 0x5c
+#define GE_TRANSMIT_CONTROL_REG 0x60
+#define GE_CF_CRC_STRIP_REG 0x1b0
+#define GE_MODE_CHANGE_EN 0x1b4
+#define GE_RECV_CONTROL_REG 0x1e0
+#define GE_STATION_MAC_ADDRESS 0x210
+#define PPE_CFG_TX_PKT_BD_ADDR 0x420
+#define PPE_CFG_MAX_FRAME_LEN_REG 0x408
+#define PPE_CFG_BUS_CTRL_REG 0x424
+#define PPE_CFG_RX_CTRL_REG 0x428
+#define PPE_CFG_RX_PKT_MODE_REG 0x438
+#define PPE_CFG_QOS_VMID_GEN 0x500
+#define PPE_CFG_RX_PKT_INT 0x538
+#define PPE_INTEN 0x600
+#define PPE_INTSTS 0x608
+#define PPE_RINT 0x604
+#define PPE_CFG_STS_MODE 0x700
+#define PPE_HIS_RX_PKT_CNT 0x804
+
+/* REG_INTERRUPT */
+#define RCV_INT BIT(10)
+#define RCV_NOBUF BIT(8)
+#define DEF_INT_MASK 0x41fdf
+
+#define RX_DESC_NUM 64
+#define TX_DESC_NUM 64
+#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM-1))
+#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM-1))
+
+#define GMAC_PPE_RX_PKT_MAX_LEN 379
+#define GMAC_MAX_PKT_LEN 1516
+#define DESC_DEF_CFG 0x14
+#define RX_BUF_SIZE 1600
+#define RX_PKT_ERR 0x3
+#define TX_TIMEOUT (6 * HZ)
+
+#define DRV_NAME "hip04-ether"
+
+struct tx_desc {
+ u32 send_addr;
+ u16 reserved_16;
+ u16 send_size;
+ u32 reserved_32;
+ u32 cfg;
+ u32 wb_addr;
+} ____cacheline_aligned;
+
+struct rx_desc {
+ u16 reserved_16;
+ u16 pkt_len;
+ u32 reserve1[3];
+ u32 pkt_err;
+ u32 reserve2[4];
+};
+
+struct hip04_priv {
+ void __iomem *base;
+ int phy_mode;
+ int chan;
+ unsigned int port;
+ unsigned int speed;
+ unsigned int duplex;
+ unsigned int reg_inten;
+
+ struct napi_struct napi;
+ struct net_device *ndev;
+
+ struct tx_desc *tx_desc;
+ dma_addr_t tx_desc_dma;
+ struct sk_buff *tx_skb[TX_DESC_NUM];
+ dma_addr_t tx_phys[TX_DESC_NUM];
+ unsigned int tx_head;
+ unsigned int tx_tail;
+ unsigned int tx_count;
+
+ unsigned char *rx_buf[RX_DESC_NUM];
+ dma_addr_t rx_phys[RX_DESC_NUM];
+ unsigned int rx_head;
+ unsigned int rx_buf_size;
+
+ struct device_node *phy_node;
+ struct phy_device *phy;
+ struct regmap *map;
+};
+
+static void hip04_config_port(struct hip04_priv *priv, u32 speed, u32 duplex)
+{
+ u32 val;
+
+ priv->speed = speed;
+ priv->duplex = duplex;
+
+ switch (priv->phy_mode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ if (speed == SPEED_1000)
+ val = 8;
+ else
+ val = 7;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ val = 1; /* SPEED_100 */
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ writel_relaxed(val, priv->base + GE_PORT_MODE);
+
+ val = (duplex) ? BIT(0) : 0;
+ writel_relaxed(val, priv->base + GE_DUPLEX_TYPE);
+
+ val = BIT(0);
+ writel_relaxed(val, priv->base + GE_MODE_CHANGE_EN);
+}
+
+static void hip04_reset_ppe(struct hip04_priv *priv)
+{
+ u32 val, tmp;
+
+ do {
+ regmap_read(priv->map, priv->port * 4 + PPE_CURR_BUF_CNT, &val);
+ regmap_read(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, &tmp);
+ } while (val & 0xfff);
+}
+
+static void hip04_config_fifo(struct hip04_priv *priv)
+{
+ u32 val;
+
+ val = readl_relaxed(priv->base + PPE_CFG_STS_MODE);
+ val |= BIT(12); /* PPE_HIS_RX_PKT_CNT read clear */
+ writel_relaxed(val, priv->base + PPE_CFG_STS_MODE);
+
+ val = BIT(priv->port);
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_POOL_GRP, val);
+
+ val = priv->port << 8;
+ val |= BIT(14);
+ writel_relaxed(val, priv->base + PPE_CFG_QOS_VMID_GEN);
+
+ val = RX_BUF_SIZE;
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_BUF_SIZE, val);
+
+ val = RX_DESC_NUM << 16; /* depth */
+ val |= BIT(11); /* seq: first set first ues */
+ val |= RX_DESC_NUM * priv->chan; /* start_addr */
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_FIFO_SIZE, val);
+
+ /* pkt store format */
+ val = NET_IP_ALIGN << 11; /* align */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_CTRL_REG);
+
+ /* following cfg required for 1000M */
+ /* pkt mode */
+ val = BIT(18); /* align */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_MODE_REG);
+
+ /* set bus ctrl */
+ val = BIT(14); /* buffer locally release */
+ val |= BIT(0); /* big endian */
+ writel_relaxed(val, priv->base + PPE_CFG_BUS_CTRL_REG);
+
+ /* set max pkt len, curtail if exceed */
+ val = GMAC_PPE_RX_PKT_MAX_LEN; /* max buffer len */
+ writel_relaxed(val, priv->base + PPE_CFG_MAX_FRAME_LEN_REG);
+
+ /* set max len of each pkt */
+ val = GMAC_MAX_PKT_LEN; /* max buffer len */
+ writel_relaxed(val, priv->base + GE_MAX_FRM_SIZE_REG);
+
+ /* set min len of each pkt */
+ val = 31; /* min buffer len */
+ writel_relaxed(val, priv->base + GE_SHORT_RUNTS_THR_REG);
+
+ /* tx */
+ val = readl_relaxed(priv->base + GE_TRANSMIT_CONTROL_REG);
+ val |= BIT(5); /* tx auto neg */
+ val |= BIT(6); /* tx add crc */
+ val |= BIT(7); /* tx short pad through */
+ writel_relaxed(val, priv->base + GE_TRANSMIT_CONTROL_REG);
+
+ /* rx crc */
+ val = BIT(0); /* rx strip crc */
+ writel_relaxed(val, priv->base + GE_CF_CRC_STRIP_REG);
+
+ /* rx */
+ val = readl_relaxed(priv->base + GE_RECV_CONTROL_REG);
+ val |= BIT(3); /* rx strip pad */
+ val |= BIT(4); /* run pkt en */
+ writel_relaxed(val, priv->base + GE_RECV_CONTROL_REG);
+
+ /* auto neg control */
+ val = BIT(0);
+ writel_relaxed(val, priv->base + GE_TX_LOCAL_PAGE_REG);
+}
+
+static void hip04_mac_enable(struct net_device *ndev, bool enable)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 val;
+
+ if (enable) {
+ /* enable tx & rx */
+ val = readl_relaxed(priv->base + GE_PORT_EN);
+ val |= BIT(1); /* rx*/
+ val |= BIT(2); /* tx*/
+ writel_relaxed(val, priv->base + GE_PORT_EN);
+
+ /* enable interrupt */
+ priv->reg_inten = DEF_INT_MASK;
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+
+ /* clear rx int */
+ val = RCV_INT;
+ writel_relaxed(val, priv->base + PPE_RINT);
+
+ /* config recv int*/
+ val = BIT(6); /* int threshold 1 package */
+ val |= 0x4; /* recv timeout */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT);
+ } else {
+ /* disable int */
+ priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+
+ /* disable tx & rx */
+ val = readl_relaxed(priv->base + GE_PORT_EN);
+ val &= ~(BIT(1)); /* rx*/
+ val &= ~(BIT(2)); /* tx*/
+ writel_relaxed(val, priv->base + GE_PORT_EN);
+ }
+}
+
+static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys)
+{
+ writel(phys, priv->base + PPE_CFG_TX_PKT_BD_ADDR);
+}
+
+static void hip04_set_recv_desc(struct hip04_priv *priv, dma_addr_t phys)
+{
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, phys);
+}
+
+static u32 hip04_recv_cnt(struct hip04_priv *priv)
+{
+ return readl(priv->base + PPE_HIS_RX_PKT_CNT);
+}
+
+static void hip04_update_mac_address(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+
+ writel_relaxed(((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])),
+ priv->base + GE_STATION_MAC_ADDRESS);
+ writel_relaxed(((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5])),
+ priv->base + GE_STATION_MAC_ADDRESS + 4);
+}
+
+static int hip04_set_mac_address(struct net_device *ndev, void *addr)
+{
+ eth_mac_addr(ndev, addr);
+ hip04_update_mac_address(ndev);
+ return 0;
+}
+
+static int hip04_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
+ struct net_device *ndev = priv->ndev;
+ struct net_device_stats *stats = &ndev->stats;
+ unsigned int cnt = hip04_recv_cnt(priv);
+ struct sk_buff *skb;
+ struct rx_desc *desc;
+ unsigned char *buf;
+ dma_addr_t phys;
+ int rx = 0;
+ u16 len;
+ u32 err;
+
+ while (cnt) {
+ buf = priv->rx_buf[priv->rx_head];
+ skb = build_skb(buf, priv->rx_buf_size);
+ if (unlikely(!skb))
+ net_dbg_ratelimited("build_skb failed\n");
+
+ dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[priv->rx_head] = 0;
+
+ desc = (struct rx_desc *)skb->data;
+ len = be16_to_cpu(desc->pkt_len);
+ err = be32_to_cpu(desc->pkt_err);
+
+ if (len > RX_BUF_SIZE)
+ len = RX_BUF_SIZE;
+ if (0 == len)
+ break;
+
+ if (err & RX_PKT_ERR) {
+ dev_kfree_skb_any(skb);
+ stats->rx_dropped++;
+ stats->rx_errors++;
+ continue;
+ }
+
+ stats->rx_packets++;
+ stats->rx_bytes += len;
+
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(&priv->napi, skb);
+
+ buf = netdev_alloc_frag(priv->rx_buf_size);
+ if (!buf)
+ return -ENOMEM;
+ phys = dma_map_single(&ndev->dev, buf,
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_buf[priv->rx_head] = buf;
+ priv->rx_phys[priv->rx_head] = phys;
+ hip04_set_recv_desc(priv, phys);
+
+ priv->rx_head = RX_NEXT(priv->rx_head);
+ if (rx++ >= budget)
+ break;
+
+ if (--cnt == 0)
+ cnt = hip04_recv_cnt(priv);
+ }
+
+ if (rx < budget) {
+ napi_complete(napi);
+
+ /* enable rx interrupt */
+ priv->reg_inten |= RCV_INT | RCV_NOBUF;
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+ }
+
+ return rx;
+}
+
+static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 ists = readl_relaxed(priv->base + PPE_INTSTS);
+ u32 val = DEF_INT_MASK;
+
+ writel_relaxed(val, priv->base + PPE_RINT);
+
+ if (ists & (RCV_INT | RCV_NOBUF)) {
+ if (napi_schedule_prep(&priv->napi)) {
+ /* disable rx interrupt */
+ priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hip04_tx_reclaim(struct net_device *ndev, bool force)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ unsigned tx_head = priv->tx_head;
+ unsigned tx_tail = priv->tx_tail;
+ struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
+
+ while (tx_tail != tx_head) {
+ if (desc->send_addr != 0) {
+ if (force)
+ desc->send_addr = 0;
+ else
+ break;
+ }
+ if (priv->tx_phys[tx_tail]) {
+ dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
+ priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
+ priv->tx_phys[tx_tail] = 0;
+ }
+ dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
+ priv->tx_skb[tx_tail] = NULL;
+ tx_tail = TX_NEXT(tx_tail);
+ priv->tx_count--;
+ }
+ priv->tx_tail = tx_tail;
+}
+
+static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ unsigned int tx_head = priv->tx_head;
+ struct tx_desc *desc = &priv->tx_desc[tx_head];
+ dma_addr_t phys;
+
+ hip04_tx_reclaim(ndev, false);
+
+ if (priv->tx_count++ >= TX_DESC_NUM) {
+ net_dbg_ratelimited("no TX space for packet\n");
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+
+ phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ priv->tx_skb[tx_head] = skb;
+ priv->tx_phys[tx_head] = phys;
+ desc->send_addr = cpu_to_be32(phys);
+ desc->send_size = cpu_to_be16(skb->len);
+ desc->cfg = cpu_to_be32(DESC_DEF_CFG);
+ phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
+ desc->wb_addr = cpu_to_be32(phys);
+ skb_tx_timestamp(skb);
+ hip04_set_xmit_desc(priv, phys);
+ priv->tx_head = TX_NEXT(tx_head);
+
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+
+ return NETDEV_TX_OK;
+}
+
+static void hip04_adjust_link(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct phy_device *phy = priv->phy;
+
+ if ((priv->speed != phy->speed) || (priv->duplex != phy->duplex)) {
+ hip04_config_port(priv, phy->speed, phy->duplex);
+ phy_print_status(phy);
+ }
+}
+
+static int hip04_mac_open(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ priv->rx_head = 0;
+ priv->tx_head = 0;
+ priv->tx_tail = 0;
+ priv->tx_count = 0;
+
+ hip04_reset_ppe(priv);
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ dma_addr_t phys;
+
+ phys = dma_map_single(&ndev->dev, priv->rx_buf[i],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[i] = phys;
+ hip04_set_recv_desc(priv, phys);
+ }
+
+ if (priv->phy_node) {
+ priv->phy = of_phy_connect(ndev, priv->phy_node,
+ &hip04_adjust_link, 0, priv->phy_mode);
+ if (!priv->phy)
+ return -ENODEV;
+ phy_start(priv->phy);
+ }
+
+ netif_start_queue(ndev);
+ hip04_mac_enable(ndev, true);
+ napi_enable(&priv->napi);
+
+ return 0;
+}
+
+static int hip04_mac_stop(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ if (priv->phy)
+ phy_disconnect(priv->phy);
+
+ priv->phy = NULL;
+ napi_disable(&priv->napi);
+ netif_stop_queue(ndev);
+ hip04_mac_enable(ndev, false);
+ hip04_tx_reclaim(ndev, true);
+ hip04_reset_ppe(priv);
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ if (priv->rx_phys[i]) {
+ dma_unmap_single(&ndev->dev, priv->rx_phys[i],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+static void hip04_timeout(struct net_device *ndev)
+{
+ netif_wake_queue(ndev);
+ return;
+}
+
+static struct net_device_ops hip04_netdev_ops = {
+ .ndo_open = hip04_mac_open,
+ .ndo_stop = hip04_mac_stop,
+ .ndo_start_xmit = hip04_mac_start_xmit,
+ .ndo_set_mac_address = hip04_set_mac_address,
+ .ndo_tx_timeout = hip04_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int hip04_alloc_ring(struct net_device *ndev, struct device *d)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ priv->tx_desc = dma_alloc_coherent(d,
+ TX_DESC_NUM * sizeof(struct tx_desc),
+ &priv->tx_desc_dma, GFP_KERNEL);
+ if (!priv->tx_desc)
+ return -ENOMEM;
+
+ priv->rx_buf_size = RX_BUF_SIZE +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ priv->rx_buf[i] = netdev_alloc_frag(priv->rx_buf_size);
+ if (!priv->rx_buf[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void hip04_free_ring(struct net_device *ndev, struct device *d)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_DESC_NUM; i++)
+ if (priv->rx_buf[i])
+ put_page(virt_to_head_page(priv->rx_buf[i]));
+
+ for (i = 0; i < TX_DESC_NUM; i++)
+ if (priv->tx_skb[i])
+ dev_kfree_skb_any(priv->tx_skb[i]);
+
+ dma_free_coherent(d, TX_DESC_NUM * sizeof(struct tx_desc),
+ priv->tx_desc, priv->tx_desc_dma);
+}
+
+static int hip04_mac_probe(struct platform_device *pdev)
+{
+ struct device *d = &pdev->dev;
+ struct device_node *n, *node = d->of_node;
+ struct net_device *ndev;
+ struct hip04_priv *priv;
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct hip04_priv));
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ platform_set_drvdata(pdev, ndev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(d, res);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto init_fail;
+ }
+
+ priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
+ if (IS_ERR(priv->map)) {
+ dev_warn(d, "no hisilicon,hip04-ppe\n");
+ ret = PTR_ERR(priv->map);
+ goto init_fail;
+ }
+
+ n = of_parse_phandle(node, "port-handle", 0);
+ if (n) {
+ ret = of_property_read_u32(n, "reg", &priv->port);
+ if (ret) {
+ dev_warn(d, "no reg info\n");
+ goto init_fail;
+ }
+
+ ret = of_property_read_u32(n, "channel", &priv->chan);
+ if (ret) {
+ dev_warn(d, "no channel info\n");
+ goto init_fail;
+ }
+ } else {
+ dev_warn(d, "no port-handle\n");
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ priv->phy_mode = of_get_phy_mode(node);
+ if (priv->phy_mode < 0) {
+ dev_warn(d, "not find phy-mode\n");
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &hip04_netdev_ops;
+ ndev->watchdog_timeo = TX_TIMEOUT;
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+ ndev->irq = irq;
+ netif_napi_add(ndev, &priv->napi, hip04_rx_poll, RX_DESC_NUM);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ hip04_reset_ppe(priv);
+ if (priv->phy_mode == PHY_INTERFACE_MODE_MII)
+ hip04_config_port(priv, SPEED_100, DUPLEX_FULL);
+
+ hip04_config_fifo(priv);
+ random_ether_addr(ndev->dev_addr);
+ hip04_update_mac_address(ndev);
+
+ ret = hip04_alloc_ring(ndev, d);
+ if (ret) {
+ netdev_err(ndev, "alloc ring fail\n");
+ goto alloc_fail;
+ }
+
+ ret = devm_request_irq(d, irq, hip04_mac_interrupt,
+ 0, pdev->name, ndev);
+ if (ret) {
+ netdev_err(ndev, "devm_request_irq failed\n");
+ goto alloc_fail;
+ }
+
+ priv->phy_node = of_parse_phandle(node, "phy-handle", 0);
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ free_netdev(ndev);
+ goto alloc_fail;
+ }
+
+ return 0;
+
+alloc_fail:
+ hip04_free_ring(ndev, d);
+init_fail:
+ of_node_put(priv->phy_node);
+ free_netdev(ndev);
+ return ret;
+}
+
+static int hip04_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct device *d = &pdev->dev;
+
+ hip04_free_ring(ndev, d);
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ of_node_put(priv->phy_node);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mac_match[] = {
+ { .compatible = "hisilicon,hip04-mac" },
+ { }
+};
+
+static struct platform_driver hip04_mac_driver = {
+ .probe = hip04_mac_probe,
+ .remove = hip04_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mac_match,
+ },
+};
+module_platform_driver(hip04_mac_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 Ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-ether");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-24 14:14 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-24 14:14 UTC (permalink / raw)
To: linux-arm-kernel
Support Hisilicon hip04 ethernet driver, including 100M / 1000M controller
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/hisilicon/Makefile | 2 +-
drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++++++++++
2 files changed, 729 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
index 1d6eb6e..e6fe7af 100644
--- a/drivers/net/ethernet/hisilicon/Makefile
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -2,4 +2,4 @@
# Makefile for the HISILICON network device drivers.
#
-obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
+obj-$(CONFIG_HIP04_ETH) += hip04_eth.o hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
new file mode 100644
index 0000000..c1131b2
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -0,0 +1,728 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/phy.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define PPE_CFG_RX_ADDR 0x100
+#define PPE_CFG_POOL_GRP 0x300
+#define PPE_CFG_RX_BUF_SIZE 0x400
+#define PPE_CFG_RX_FIFO_SIZE 0x500
+#define PPE_CURR_BUF_CNT 0xa200
+
+#define GE_DUPLEX_TYPE 0x8
+#define GE_MAX_FRM_SIZE_REG 0x3c
+#define GE_PORT_MODE 0x40
+#define GE_PORT_EN 0x44
+#define GE_SHORT_RUNTS_THR_REG 0x50
+#define GE_TX_LOCAL_PAGE_REG 0x5c
+#define GE_TRANSMIT_CONTROL_REG 0x60
+#define GE_CF_CRC_STRIP_REG 0x1b0
+#define GE_MODE_CHANGE_EN 0x1b4
+#define GE_RECV_CONTROL_REG 0x1e0
+#define GE_STATION_MAC_ADDRESS 0x210
+#define PPE_CFG_TX_PKT_BD_ADDR 0x420
+#define PPE_CFG_MAX_FRAME_LEN_REG 0x408
+#define PPE_CFG_BUS_CTRL_REG 0x424
+#define PPE_CFG_RX_CTRL_REG 0x428
+#define PPE_CFG_RX_PKT_MODE_REG 0x438
+#define PPE_CFG_QOS_VMID_GEN 0x500
+#define PPE_CFG_RX_PKT_INT 0x538
+#define PPE_INTEN 0x600
+#define PPE_INTSTS 0x608
+#define PPE_RINT 0x604
+#define PPE_CFG_STS_MODE 0x700
+#define PPE_HIS_RX_PKT_CNT 0x804
+
+/* REG_INTERRUPT */
+#define RCV_INT BIT(10)
+#define RCV_NOBUF BIT(8)
+#define DEF_INT_MASK 0x41fdf
+
+#define RX_DESC_NUM 64
+#define TX_DESC_NUM 64
+#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM-1))
+#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM-1))
+
+#define GMAC_PPE_RX_PKT_MAX_LEN 379
+#define GMAC_MAX_PKT_LEN 1516
+#define DESC_DEF_CFG 0x14
+#define RX_BUF_SIZE 1600
+#define RX_PKT_ERR 0x3
+#define TX_TIMEOUT (6 * HZ)
+
+#define DRV_NAME "hip04-ether"
+
+struct tx_desc {
+ u32 send_addr;
+ u16 reserved_16;
+ u16 send_size;
+ u32 reserved_32;
+ u32 cfg;
+ u32 wb_addr;
+} ____cacheline_aligned;
+
+struct rx_desc {
+ u16 reserved_16;
+ u16 pkt_len;
+ u32 reserve1[3];
+ u32 pkt_err;
+ u32 reserve2[4];
+};
+
+struct hip04_priv {
+ void __iomem *base;
+ int phy_mode;
+ int chan;
+ unsigned int port;
+ unsigned int speed;
+ unsigned int duplex;
+ unsigned int reg_inten;
+
+ struct napi_struct napi;
+ struct net_device *ndev;
+
+ struct tx_desc *tx_desc;
+ dma_addr_t tx_desc_dma;
+ struct sk_buff *tx_skb[TX_DESC_NUM];
+ dma_addr_t tx_phys[TX_DESC_NUM];
+ unsigned int tx_head;
+ unsigned int tx_tail;
+ unsigned int tx_count;
+
+ unsigned char *rx_buf[RX_DESC_NUM];
+ dma_addr_t rx_phys[RX_DESC_NUM];
+ unsigned int rx_head;
+ unsigned int rx_buf_size;
+
+ struct device_node *phy_node;
+ struct phy_device *phy;
+ struct regmap *map;
+};
+
+static void hip04_config_port(struct hip04_priv *priv, u32 speed, u32 duplex)
+{
+ u32 val;
+
+ priv->speed = speed;
+ priv->duplex = duplex;
+
+ switch (priv->phy_mode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ if (speed == SPEED_1000)
+ val = 8;
+ else
+ val = 7;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ val = 1; /* SPEED_100 */
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ writel_relaxed(val, priv->base + GE_PORT_MODE);
+
+ val = (duplex) ? BIT(0) : 0;
+ writel_relaxed(val, priv->base + GE_DUPLEX_TYPE);
+
+ val = BIT(0);
+ writel_relaxed(val, priv->base + GE_MODE_CHANGE_EN);
+}
+
+static void hip04_reset_ppe(struct hip04_priv *priv)
+{
+ u32 val, tmp;
+
+ do {
+ regmap_read(priv->map, priv->port * 4 + PPE_CURR_BUF_CNT, &val);
+ regmap_read(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, &tmp);
+ } while (val & 0xfff);
+}
+
+static void hip04_config_fifo(struct hip04_priv *priv)
+{
+ u32 val;
+
+ val = readl_relaxed(priv->base + PPE_CFG_STS_MODE);
+ val |= BIT(12); /* PPE_HIS_RX_PKT_CNT read clear */
+ writel_relaxed(val, priv->base + PPE_CFG_STS_MODE);
+
+ val = BIT(priv->port);
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_POOL_GRP, val);
+
+ val = priv->port << 8;
+ val |= BIT(14);
+ writel_relaxed(val, priv->base + PPE_CFG_QOS_VMID_GEN);
+
+ val = RX_BUF_SIZE;
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_BUF_SIZE, val);
+
+ val = RX_DESC_NUM << 16; /* depth */
+ val |= BIT(11); /* seq: first set first ues */
+ val |= RX_DESC_NUM * priv->chan; /* start_addr */
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_FIFO_SIZE, val);
+
+ /* pkt store format */
+ val = NET_IP_ALIGN << 11; /* align */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_CTRL_REG);
+
+ /* following cfg required for 1000M */
+ /* pkt mode */
+ val = BIT(18); /* align */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_MODE_REG);
+
+ /* set bus ctrl */
+ val = BIT(14); /* buffer locally release */
+ val |= BIT(0); /* big endian */
+ writel_relaxed(val, priv->base + PPE_CFG_BUS_CTRL_REG);
+
+ /* set max pkt len, curtail if exceed */
+ val = GMAC_PPE_RX_PKT_MAX_LEN; /* max buffer len */
+ writel_relaxed(val, priv->base + PPE_CFG_MAX_FRAME_LEN_REG);
+
+ /* set max len of each pkt */
+ val = GMAC_MAX_PKT_LEN; /* max buffer len */
+ writel_relaxed(val, priv->base + GE_MAX_FRM_SIZE_REG);
+
+ /* set min len of each pkt */
+ val = 31; /* min buffer len */
+ writel_relaxed(val, priv->base + GE_SHORT_RUNTS_THR_REG);
+
+ /* tx */
+ val = readl_relaxed(priv->base + GE_TRANSMIT_CONTROL_REG);
+ val |= BIT(5); /* tx auto neg */
+ val |= BIT(6); /* tx add crc */
+ val |= BIT(7); /* tx short pad through */
+ writel_relaxed(val, priv->base + GE_TRANSMIT_CONTROL_REG);
+
+ /* rx crc */
+ val = BIT(0); /* rx strip crc */
+ writel_relaxed(val, priv->base + GE_CF_CRC_STRIP_REG);
+
+ /* rx */
+ val = readl_relaxed(priv->base + GE_RECV_CONTROL_REG);
+ val |= BIT(3); /* rx strip pad */
+ val |= BIT(4); /* run pkt en */
+ writel_relaxed(val, priv->base + GE_RECV_CONTROL_REG);
+
+ /* auto neg control */
+ val = BIT(0);
+ writel_relaxed(val, priv->base + GE_TX_LOCAL_PAGE_REG);
+}
+
+static void hip04_mac_enable(struct net_device *ndev, bool enable)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 val;
+
+ if (enable) {
+ /* enable tx & rx */
+ val = readl_relaxed(priv->base + GE_PORT_EN);
+ val |= BIT(1); /* rx*/
+ val |= BIT(2); /* tx*/
+ writel_relaxed(val, priv->base + GE_PORT_EN);
+
+ /* enable interrupt */
+ priv->reg_inten = DEF_INT_MASK;
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+
+ /* clear rx int */
+ val = RCV_INT;
+ writel_relaxed(val, priv->base + PPE_RINT);
+
+ /* config recv int*/
+ val = BIT(6); /* int threshold 1 package */
+ val |= 0x4; /* recv timeout */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT);
+ } else {
+ /* disable int */
+ priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+
+ /* disable tx & rx */
+ val = readl_relaxed(priv->base + GE_PORT_EN);
+ val &= ~(BIT(1)); /* rx*/
+ val &= ~(BIT(2)); /* tx*/
+ writel_relaxed(val, priv->base + GE_PORT_EN);
+ }
+}
+
+static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys)
+{
+ writel(phys, priv->base + PPE_CFG_TX_PKT_BD_ADDR);
+}
+
+static void hip04_set_recv_desc(struct hip04_priv *priv, dma_addr_t phys)
+{
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, phys);
+}
+
+static u32 hip04_recv_cnt(struct hip04_priv *priv)
+{
+ return readl(priv->base + PPE_HIS_RX_PKT_CNT);
+}
+
+static void hip04_update_mac_address(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+
+ writel_relaxed(((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])),
+ priv->base + GE_STATION_MAC_ADDRESS);
+ writel_relaxed(((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5])),
+ priv->base + GE_STATION_MAC_ADDRESS + 4);
+}
+
+static int hip04_set_mac_address(struct net_device *ndev, void *addr)
+{
+ eth_mac_addr(ndev, addr);
+ hip04_update_mac_address(ndev);
+ return 0;
+}
+
+static int hip04_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
+ struct net_device *ndev = priv->ndev;
+ struct net_device_stats *stats = &ndev->stats;
+ unsigned int cnt = hip04_recv_cnt(priv);
+ struct sk_buff *skb;
+ struct rx_desc *desc;
+ unsigned char *buf;
+ dma_addr_t phys;
+ int rx = 0;
+ u16 len;
+ u32 err;
+
+ while (cnt) {
+ buf = priv->rx_buf[priv->rx_head];
+ skb = build_skb(buf, priv->rx_buf_size);
+ if (unlikely(!skb))
+ net_dbg_ratelimited("build_skb failed\n");
+
+ dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[priv->rx_head] = 0;
+
+ desc = (struct rx_desc *)skb->data;
+ len = be16_to_cpu(desc->pkt_len);
+ err = be32_to_cpu(desc->pkt_err);
+
+ if (len > RX_BUF_SIZE)
+ len = RX_BUF_SIZE;
+ if (0 == len)
+ break;
+
+ if (err & RX_PKT_ERR) {
+ dev_kfree_skb_any(skb);
+ stats->rx_dropped++;
+ stats->rx_errors++;
+ continue;
+ }
+
+ stats->rx_packets++;
+ stats->rx_bytes += len;
+
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(&priv->napi, skb);
+
+ buf = netdev_alloc_frag(priv->rx_buf_size);
+ if (!buf)
+ return -ENOMEM;
+ phys = dma_map_single(&ndev->dev, buf,
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_buf[priv->rx_head] = buf;
+ priv->rx_phys[priv->rx_head] = phys;
+ hip04_set_recv_desc(priv, phys);
+
+ priv->rx_head = RX_NEXT(priv->rx_head);
+ if (rx++ >= budget)
+ break;
+
+ if (--cnt == 0)
+ cnt = hip04_recv_cnt(priv);
+ }
+
+ if (rx < budget) {
+ napi_complete(napi);
+
+ /* enable rx interrupt */
+ priv->reg_inten |= RCV_INT | RCV_NOBUF;
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+ }
+
+ return rx;
+}
+
+static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 ists = readl_relaxed(priv->base + PPE_INTSTS);
+ u32 val = DEF_INT_MASK;
+
+ writel_relaxed(val, priv->base + PPE_RINT);
+
+ if (ists & (RCV_INT | RCV_NOBUF)) {
+ if (napi_schedule_prep(&priv->napi)) {
+ /* disable rx interrupt */
+ priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hip04_tx_reclaim(struct net_device *ndev, bool force)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ unsigned tx_head = priv->tx_head;
+ unsigned tx_tail = priv->tx_tail;
+ struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
+
+ while (tx_tail != tx_head) {
+ if (desc->send_addr != 0) {
+ if (force)
+ desc->send_addr = 0;
+ else
+ break;
+ }
+ if (priv->tx_phys[tx_tail]) {
+ dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
+ priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
+ priv->tx_phys[tx_tail] = 0;
+ }
+ dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
+ priv->tx_skb[tx_tail] = NULL;
+ tx_tail = TX_NEXT(tx_tail);
+ priv->tx_count--;
+ }
+ priv->tx_tail = tx_tail;
+}
+
+static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ unsigned int tx_head = priv->tx_head;
+ struct tx_desc *desc = &priv->tx_desc[tx_head];
+ dma_addr_t phys;
+
+ hip04_tx_reclaim(ndev, false);
+
+ if (priv->tx_count++ >= TX_DESC_NUM) {
+ net_dbg_ratelimited("no TX space for packet\n");
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+
+ phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ priv->tx_skb[tx_head] = skb;
+ priv->tx_phys[tx_head] = phys;
+ desc->send_addr = cpu_to_be32(phys);
+ desc->send_size = cpu_to_be16(skb->len);
+ desc->cfg = cpu_to_be32(DESC_DEF_CFG);
+ phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
+ desc->wb_addr = cpu_to_be32(phys);
+ skb_tx_timestamp(skb);
+ hip04_set_xmit_desc(priv, phys);
+ priv->tx_head = TX_NEXT(tx_head);
+
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+
+ return NETDEV_TX_OK;
+}
+
+static void hip04_adjust_link(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct phy_device *phy = priv->phy;
+
+ if ((priv->speed != phy->speed) || (priv->duplex != phy->duplex)) {
+ hip04_config_port(priv, phy->speed, phy->duplex);
+ phy_print_status(phy);
+ }
+}
+
+static int hip04_mac_open(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ priv->rx_head = 0;
+ priv->tx_head = 0;
+ priv->tx_tail = 0;
+ priv->tx_count = 0;
+
+ hip04_reset_ppe(priv);
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ dma_addr_t phys;
+
+ phys = dma_map_single(&ndev->dev, priv->rx_buf[i],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[i] = phys;
+ hip04_set_recv_desc(priv, phys);
+ }
+
+ if (priv->phy_node) {
+ priv->phy = of_phy_connect(ndev, priv->phy_node,
+ &hip04_adjust_link, 0, priv->phy_mode);
+ if (!priv->phy)
+ return -ENODEV;
+ phy_start(priv->phy);
+ }
+
+ netif_start_queue(ndev);
+ hip04_mac_enable(ndev, true);
+ napi_enable(&priv->napi);
+
+ return 0;
+}
+
+static int hip04_mac_stop(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ if (priv->phy)
+ phy_disconnect(priv->phy);
+
+ priv->phy = NULL;
+ napi_disable(&priv->napi);
+ netif_stop_queue(ndev);
+ hip04_mac_enable(ndev, false);
+ hip04_tx_reclaim(ndev, true);
+ hip04_reset_ppe(priv);
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ if (priv->rx_phys[i]) {
+ dma_unmap_single(&ndev->dev, priv->rx_phys[i],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+static void hip04_timeout(struct net_device *ndev)
+{
+ netif_wake_queue(ndev);
+ return;
+}
+
+static struct net_device_ops hip04_netdev_ops = {
+ .ndo_open = hip04_mac_open,
+ .ndo_stop = hip04_mac_stop,
+ .ndo_start_xmit = hip04_mac_start_xmit,
+ .ndo_set_mac_address = hip04_set_mac_address,
+ .ndo_tx_timeout = hip04_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int hip04_alloc_ring(struct net_device *ndev, struct device *d)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ priv->tx_desc = dma_alloc_coherent(d,
+ TX_DESC_NUM * sizeof(struct tx_desc),
+ &priv->tx_desc_dma, GFP_KERNEL);
+ if (!priv->tx_desc)
+ return -ENOMEM;
+
+ priv->rx_buf_size = RX_BUF_SIZE +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ priv->rx_buf[i] = netdev_alloc_frag(priv->rx_buf_size);
+ if (!priv->rx_buf[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void hip04_free_ring(struct net_device *ndev, struct device *d)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_DESC_NUM; i++)
+ if (priv->rx_buf[i])
+ put_page(virt_to_head_page(priv->rx_buf[i]));
+
+ for (i = 0; i < TX_DESC_NUM; i++)
+ if (priv->tx_skb[i])
+ dev_kfree_skb_any(priv->tx_skb[i]);
+
+ dma_free_coherent(d, TX_DESC_NUM * sizeof(struct tx_desc),
+ priv->tx_desc, priv->tx_desc_dma);
+}
+
+static int hip04_mac_probe(struct platform_device *pdev)
+{
+ struct device *d = &pdev->dev;
+ struct device_node *n, *node = d->of_node;
+ struct net_device *ndev;
+ struct hip04_priv *priv;
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct hip04_priv));
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ platform_set_drvdata(pdev, ndev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(d, res);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto init_fail;
+ }
+
+ priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
+ if (IS_ERR(priv->map)) {
+ dev_warn(d, "no hisilicon,hip04-ppe\n");
+ ret = PTR_ERR(priv->map);
+ goto init_fail;
+ }
+
+ n = of_parse_phandle(node, "port-handle", 0);
+ if (n) {
+ ret = of_property_read_u32(n, "reg", &priv->port);
+ if (ret) {
+ dev_warn(d, "no reg info\n");
+ goto init_fail;
+ }
+
+ ret = of_property_read_u32(n, "channel", &priv->chan);
+ if (ret) {
+ dev_warn(d, "no channel info\n");
+ goto init_fail;
+ }
+ } else {
+ dev_warn(d, "no port-handle\n");
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ priv->phy_mode = of_get_phy_mode(node);
+ if (priv->phy_mode < 0) {
+ dev_warn(d, "not find phy-mode\n");
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &hip04_netdev_ops;
+ ndev->watchdog_timeo = TX_TIMEOUT;
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+ ndev->irq = irq;
+ netif_napi_add(ndev, &priv->napi, hip04_rx_poll, RX_DESC_NUM);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ hip04_reset_ppe(priv);
+ if (priv->phy_mode == PHY_INTERFACE_MODE_MII)
+ hip04_config_port(priv, SPEED_100, DUPLEX_FULL);
+
+ hip04_config_fifo(priv);
+ random_ether_addr(ndev->dev_addr);
+ hip04_update_mac_address(ndev);
+
+ ret = hip04_alloc_ring(ndev, d);
+ if (ret) {
+ netdev_err(ndev, "alloc ring fail\n");
+ goto alloc_fail;
+ }
+
+ ret = devm_request_irq(d, irq, hip04_mac_interrupt,
+ 0, pdev->name, ndev);
+ if (ret) {
+ netdev_err(ndev, "devm_request_irq failed\n");
+ goto alloc_fail;
+ }
+
+ priv->phy_node = of_parse_phandle(node, "phy-handle", 0);
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ free_netdev(ndev);
+ goto alloc_fail;
+ }
+
+ return 0;
+
+alloc_fail:
+ hip04_free_ring(ndev, d);
+init_fail:
+ of_node_put(priv->phy_node);
+ free_netdev(ndev);
+ return ret;
+}
+
+static int hip04_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct device *d = &pdev->dev;
+
+ hip04_free_ring(ndev, d);
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ of_node_put(priv->phy_node);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mac_match[] = {
+ { .compatible = "hisilicon,hip04-mac" },
+ { }
+};
+
+static struct platform_driver hip04_mac_driver = {
+ .probe = hip04_mac_probe,
+ .remove = hip04_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mac_match,
+ },
+};
+module_platform_driver(hip04_mac_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 Ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-ether");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
[parent not found: <1395670496-17381-4-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>]
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-24 14:14 ` Zhangfei Gao
@ 2014-03-24 15:18 ` Arnd Bergmann
-1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-24 15:18 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Zhangfei Gao, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
linux-lFZ/pmaqli7XmaaqVzeoHQ, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8,
mark.rutland-5wv7dgnIgG8, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> +
> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + unsigned tx_head = priv->tx_head;
> + unsigned tx_tail = priv->tx_tail;
> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> +
> + while (tx_tail != tx_head) {
> + if (desc->send_addr != 0) {
> + if (force)
> + desc->send_addr = 0;
> + else
> + break;
> + }
> + if (priv->tx_phys[tx_tail]) {
> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> + priv->tx_phys[tx_tail] = 0;
> + }
> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
> + priv->tx_skb[tx_tail] = NULL;
> + tx_tail = TX_NEXT(tx_tail);
> + priv->tx_count--;
> + }
> + priv->tx_tail = tx_tail;
> +}
I think you still need to find a solution to ensure that the tx reclaim is
called eventually through a method other than start_xmit.
> +
> + priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
> + if (IS_ERR(priv->map)) {
> + dev_warn(d, "no hisilicon,hip04-ppe\n");
> + ret = PTR_ERR(priv->map);
> + goto init_fail;
> + }
> +
> + n = of_parse_phandle(node, "port-handle", 0);
> + if (n) {
> + ret = of_property_read_u32(n, "reg", &priv->port);
> + if (ret) {
> + dev_warn(d, "no reg info\n");
> + goto init_fail;
> + }
> +
> + ret = of_property_read_u32(n, "channel", &priv->chan);
> + if (ret) {
> + dev_warn(d, "no channel info\n");
> + goto init_fail;
> + }
> + } else {
> + dev_warn(d, "no port-handle\n");
> + ret = -EINVAL;
> + goto init_fail;
> + }
Doing the lookup by "compatible" string doesn't really help you
at solve the problem of single-instance ppe at all, because that
function will only ever look at the first one. Use
syscon_regmap_lookup_by_phandle instead and pass the phandle
you get from the "port-handle" property.
Also, since you decided to treat the ppe as a dump regmap, I would
recommend moving the 'reg' and 'channel' properties into arguments
of the port-handle link, and retting rid of the child nodes of
the ppe, like:
+ ppe: ppe@28c0000 {
+ compatible = "hisilicon,hip04-ppe", "syscon";
+ reg = <0x28c0000 0x10000>;
+ };
+
+ fe: ethernet@28b0000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x28b0000 0x10000>;
+ interrupts = <0 413 4>;
+ phy-mode = "mii";
+ port-handle = <&ppe 0xf1 0>;
+ };
Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-24 15:18 ` Arnd Bergmann
0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-24 15:18 UTC (permalink / raw)
To: linux-arm-kernel
On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> +
> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + unsigned tx_head = priv->tx_head;
> + unsigned tx_tail = priv->tx_tail;
> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> +
> + while (tx_tail != tx_head) {
> + if (desc->send_addr != 0) {
> + if (force)
> + desc->send_addr = 0;
> + else
> + break;
> + }
> + if (priv->tx_phys[tx_tail]) {
> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> + priv->tx_phys[tx_tail] = 0;
> + }
> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
> + priv->tx_skb[tx_tail] = NULL;
> + tx_tail = TX_NEXT(tx_tail);
> + priv->tx_count--;
> + }
> + priv->tx_tail = tx_tail;
> +}
I think you still need to find a solution to ensure that the tx reclaim is
called eventually through a method other than start_xmit.
> +
> + priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
> + if (IS_ERR(priv->map)) {
> + dev_warn(d, "no hisilicon,hip04-ppe\n");
> + ret = PTR_ERR(priv->map);
> + goto init_fail;
> + }
> +
> + n = of_parse_phandle(node, "port-handle", 0);
> + if (n) {
> + ret = of_property_read_u32(n, "reg", &priv->port);
> + if (ret) {
> + dev_warn(d, "no reg info\n");
> + goto init_fail;
> + }
> +
> + ret = of_property_read_u32(n, "channel", &priv->chan);
> + if (ret) {
> + dev_warn(d, "no channel info\n");
> + goto init_fail;
> + }
> + } else {
> + dev_warn(d, "no port-handle\n");
> + ret = -EINVAL;
> + goto init_fail;
> + }
Doing the lookup by "compatible" string doesn't really help you
at solve the problem of single-instance ppe at all, because that
function will only ever look at the first one. Use
syscon_regmap_lookup_by_phandle instead and pass the phandle
you get from the "port-handle" property.
Also, since you decided to treat the ppe as a dump regmap, I would
recommend moving the 'reg' and 'channel' properties into arguments
of the port-handle link, and retting rid of the child nodes of
the ppe, like:
+ ppe: ppe at 28c0000 {
+ compatible = "hisilicon,hip04-ppe", "syscon";
+ reg = <0x28c0000 0x10000>;
+ };
+
+ fe: ethernet at 28b0000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x28b0000 0x10000>;
+ interrupts = <0 413 4>;
+ phy-mode = "mii";
+ port-handle = <&ppe 0xf1 0>;
+ };
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-24 15:18 ` Arnd Bergmann
@ 2014-03-25 4:06 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-25 4:06 UTC (permalink / raw)
To: Arnd Bergmann
Cc: linux-arm-kernel, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA, Florian Fainelli,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
David S. Miller
Dear Arnd
On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
>
>> +
>> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + unsigned tx_head = priv->tx_head;
>> + unsigned tx_tail = priv->tx_tail;
>> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> +
>> + while (tx_tail != tx_head) {
>> + if (desc->send_addr != 0) {
>> + if (force)
>> + desc->send_addr = 0;
>> + else
>> + break;
>> + }
>> + if (priv->tx_phys[tx_tail]) {
>> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> + priv->tx_phys[tx_tail] = 0;
>> + }
>> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>> + priv->tx_skb[tx_tail] = NULL;
>> + tx_tail = TX_NEXT(tx_tail);
>> + priv->tx_count--;
>> + }
>> + priv->tx_tail = tx_tail;
>> +}
>
> I think you still need to find a solution to ensure that the tx reclaim is
> called eventually through a method other than start_xmit.
In the iperf stress test, if move reclaim to poll, there is some
error, sometimes sending zero packets.
While keep reclaim in the xmit to reclaim transmitted packets looks
stable in the test,
There TX_DESC_NUM desc can be used.
>
>> +
>> + priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
>> + if (IS_ERR(priv->map)) {
>> + dev_warn(d, "no hisilicon,hip04-ppe\n");
>> + ret = PTR_ERR(priv->map);
>> + goto init_fail;
>> + }
>> +
>> + n = of_parse_phandle(node, "port-handle", 0);
>> + if (n) {
>> + ret = of_property_read_u32(n, "reg", &priv->port);
>> + if (ret) {
>> + dev_warn(d, "no reg info\n");
>> + goto init_fail;
>> + }
>> +
>> + ret = of_property_read_u32(n, "channel", &priv->chan);
>> + if (ret) {
>> + dev_warn(d, "no channel info\n");
>> + goto init_fail;
>> + }
>> + } else {
>> + dev_warn(d, "no port-handle\n");
>> + ret = -EINVAL;
>> + goto init_fail;
>> + }
>
> Doing the lookup by "compatible" string doesn't really help you
> at solve the problem of single-instance ppe at all, because that
> function will only ever look at the first one. Use
> syscon_regmap_lookup_by_phandle instead and pass the phandle
> you get from the "port-handle" property.
Did some experiment, the only ppe base is got from syscon probe.
And looking up by "compatible" did work for three controllers at the same time.
>
> Also, since you decided to treat the ppe as a dump regmap, I would
> recommend moving the 'reg' and 'channel' properties into arguments
> of the port-handle link, and retting rid of the child nodes of
> the ppe, like:
>
> + ppe: ppe@28c0000 {
> + compatible = "hisilicon,hip04-ppe", "syscon";
> + reg = <0x28c0000 0x10000>;
> + };
> +
> + fe: ethernet@28b0000 {
> + compatible = "hisilicon,hip04-mac";
> + reg = <0x28b0000 0x10000>;
> + interrupts = <0 413 4>;
> + phy-mode = "mii";
> + port-handle = <&ppe 0xf1 0>;
> + };
>
Would you mind giving more hints about how to parse the args.
I am thinking of using of_parse_phandle_with_args, is that correct method?
Thanks
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 4:06 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-25 4:06 UTC (permalink / raw)
To: linux-arm-kernel
Dear Arnd
On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
>
>> +
>> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + unsigned tx_head = priv->tx_head;
>> + unsigned tx_tail = priv->tx_tail;
>> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> +
>> + while (tx_tail != tx_head) {
>> + if (desc->send_addr != 0) {
>> + if (force)
>> + desc->send_addr = 0;
>> + else
>> + break;
>> + }
>> + if (priv->tx_phys[tx_tail]) {
>> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> + priv->tx_phys[tx_tail] = 0;
>> + }
>> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>> + priv->tx_skb[tx_tail] = NULL;
>> + tx_tail = TX_NEXT(tx_tail);
>> + priv->tx_count--;
>> + }
>> + priv->tx_tail = tx_tail;
>> +}
>
> I think you still need to find a solution to ensure that the tx reclaim is
> called eventually through a method other than start_xmit.
In the iperf stress test, if move reclaim to poll, there is some
error, sometimes sending zero packets.
While keep reclaim in the xmit to reclaim transmitted packets looks
stable in the test,
There TX_DESC_NUM desc can be used.
>
>> +
>> + priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
>> + if (IS_ERR(priv->map)) {
>> + dev_warn(d, "no hisilicon,hip04-ppe\n");
>> + ret = PTR_ERR(priv->map);
>> + goto init_fail;
>> + }
>> +
>> + n = of_parse_phandle(node, "port-handle", 0);
>> + if (n) {
>> + ret = of_property_read_u32(n, "reg", &priv->port);
>> + if (ret) {
>> + dev_warn(d, "no reg info\n");
>> + goto init_fail;
>> + }
>> +
>> + ret = of_property_read_u32(n, "channel", &priv->chan);
>> + if (ret) {
>> + dev_warn(d, "no channel info\n");
>> + goto init_fail;
>> + }
>> + } else {
>> + dev_warn(d, "no port-handle\n");
>> + ret = -EINVAL;
>> + goto init_fail;
>> + }
>
> Doing the lookup by "compatible" string doesn't really help you
> at solve the problem of single-instance ppe at all, because that
> function will only ever look at the first one. Use
> syscon_regmap_lookup_by_phandle instead and pass the phandle
> you get from the "port-handle" property.
Did some experiment, the only ppe base is got from syscon probe.
And looking up by "compatible" did work for three controllers at the same time.
>
> Also, since you decided to treat the ppe as a dump regmap, I would
> recommend moving the 'reg' and 'channel' properties into arguments
> of the port-handle link, and retting rid of the child nodes of
> the ppe, like:
>
> + ppe: ppe at 28c0000 {
> + compatible = "hisilicon,hip04-ppe", "syscon";
> + reg = <0x28c0000 0x10000>;
> + };
> +
> + fe: ethernet at 28b0000 {
> + compatible = "hisilicon,hip04-mac";
> + reg = <0x28b0000 0x10000>;
> + interrupts = <0 413 4>;
> + phy-mode = "mii";
> + port-handle = <&ppe 0xf1 0>;
> + };
>
Would you mind giving more hints about how to parse the args.
I am thinking of using of_parse_phandle_with_args, is that correct method?
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 4:06 ` Zhangfei Gao
@ 2014-03-25 8:12 ` Arnd Bergmann
-1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 8:12 UTC (permalink / raw)
To: Zhangfei Gao
Cc: Mark Rutland, devicetree, Florian Fainelli,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
David S. Miller, linux-arm-kernel
On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
> Dear Arnd
>
> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> >
> >> +
> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> >> +{
> >> + struct hip04_priv *priv = netdev_priv(ndev);
> >> + unsigned tx_head = priv->tx_head;
> >> + unsigned tx_tail = priv->tx_tail;
> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> >> +
> >> + while (tx_tail != tx_head) {
> >> + if (desc->send_addr != 0) {
> >> + if (force)
> >> + desc->send_addr = 0;
> >> + else
> >> + break;
> >> + }
> >> + if (priv->tx_phys[tx_tail]) {
> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> >> + priv->tx_phys[tx_tail] = 0;
> >> + }
> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
> >> + priv->tx_skb[tx_tail] = NULL;
> >> + tx_tail = TX_NEXT(tx_tail);
> >> + priv->tx_count--;
> >> + }
> >> + priv->tx_tail = tx_tail;
> >> +}
> >
> > I think you still need to find a solution to ensure that the tx reclaim is
> > called eventually through a method other than start_xmit.
>
> In the iperf stress test, if move reclaim to poll, there is some
> error, sometimes sending zero packets.
> While keep reclaim in the xmit to reclaim transmitted packets looks
> stable in the test,
> There TX_DESC_NUM desc can be used.
What I meant is that you need a correct implementation, presumably
you added a bug when you moved the function to poll(), and also you
forgot to add a timer.
> >> + priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
> >> + if (IS_ERR(priv->map)) {
> >> + dev_warn(d, "no hisilicon,hip04-ppe\n");
> >> + ret = PTR_ERR(priv->map);
> >> + goto init_fail;
> >> + }
> >> +
> >> + n = of_parse_phandle(node, "port-handle", 0);
> >> + if (n) {
> >> + ret = of_property_read_u32(n, "reg", &priv->port);
> >> + if (ret) {
> >> + dev_warn(d, "no reg info\n");
> >> + goto init_fail;
> >> + }
> >> +
> >> + ret = of_property_read_u32(n, "channel", &priv->chan);
> >> + if (ret) {
> >> + dev_warn(d, "no channel info\n");
> >> + goto init_fail;
> >> + }
> >> + } else {
> >> + dev_warn(d, "no port-handle\n");
> >> + ret = -EINVAL;
> >> + goto init_fail;
> >> + }
> >
> > Doing the lookup by "compatible" string doesn't really help you
> > at solve the problem of single-instance ppe at all, because that
> > function will only ever look at the first one. Use
> > syscon_regmap_lookup_by_phandle instead and pass the phandle
> > you get from the "port-handle" property.
>
> Did some experiment, the only ppe base is got from syscon probe.
> And looking up by "compatible" did work for three controllers at the same time.
The point is that it won't work for more than one ppe instance.
> > Also, since you decided to treat the ppe as a dump regmap, I would
> > recommend moving the 'reg' and 'channel' properties into arguments
> > of the port-handle link, and retting rid of the child nodes of
> > the ppe, like:
> >
> > + ppe: ppe@28c0000 {
> > + compatible = "hisilicon,hip04-ppe", "syscon";
> > + reg = <0x28c0000 0x10000>;
> > + };
> > +
> > + fe: ethernet@28b0000 {
> > + compatible = "hisilicon,hip04-mac";
> > + reg = <0x28b0000 0x10000>;
> > + interrupts = <0 413 4>;
> > + phy-mode = "mii";
> > + port-handle = <&ppe 0xf1 0>;
> > + };
> >
>
> Would you mind giving more hints about how to parse the args.
> I am thinking of using of_parse_phandle_with_args, is that correct method?
I would just use of_property_read_u32_array() to read all three cells,
then pass the first cell entry into syscon_regmap_lookup_by_phandle.
of_parse_phandle_with_args() is what you would use if you were registering
a higher-level driver for the ppe rather than using syscon.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 8:12 ` Arnd Bergmann
0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 8:12 UTC (permalink / raw)
To: linux-arm-kernel
On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
> Dear Arnd
>
> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> >
> >> +
> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> >> +{
> >> + struct hip04_priv *priv = netdev_priv(ndev);
> >> + unsigned tx_head = priv->tx_head;
> >> + unsigned tx_tail = priv->tx_tail;
> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> >> +
> >> + while (tx_tail != tx_head) {
> >> + if (desc->send_addr != 0) {
> >> + if (force)
> >> + desc->send_addr = 0;
> >> + else
> >> + break;
> >> + }
> >> + if (priv->tx_phys[tx_tail]) {
> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> >> + priv->tx_phys[tx_tail] = 0;
> >> + }
> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
> >> + priv->tx_skb[tx_tail] = NULL;
> >> + tx_tail = TX_NEXT(tx_tail);
> >> + priv->tx_count--;
> >> + }
> >> + priv->tx_tail = tx_tail;
> >> +}
> >
> > I think you still need to find a solution to ensure that the tx reclaim is
> > called eventually through a method other than start_xmit.
>
> In the iperf stress test, if move reclaim to poll, there is some
> error, sometimes sending zero packets.
> While keep reclaim in the xmit to reclaim transmitted packets looks
> stable in the test,
> There TX_DESC_NUM desc can be used.
What I meant is that you need a correct implementation, presumably
you added a bug when you moved the function to poll(), and also you
forgot to add a timer.
> >> + priv->map = syscon_regmap_lookup_by_compatible("hisilicon,hip04-ppe");
> >> + if (IS_ERR(priv->map)) {
> >> + dev_warn(d, "no hisilicon,hip04-ppe\n");
> >> + ret = PTR_ERR(priv->map);
> >> + goto init_fail;
> >> + }
> >> +
> >> + n = of_parse_phandle(node, "port-handle", 0);
> >> + if (n) {
> >> + ret = of_property_read_u32(n, "reg", &priv->port);
> >> + if (ret) {
> >> + dev_warn(d, "no reg info\n");
> >> + goto init_fail;
> >> + }
> >> +
> >> + ret = of_property_read_u32(n, "channel", &priv->chan);
> >> + if (ret) {
> >> + dev_warn(d, "no channel info\n");
> >> + goto init_fail;
> >> + }
> >> + } else {
> >> + dev_warn(d, "no port-handle\n");
> >> + ret = -EINVAL;
> >> + goto init_fail;
> >> + }
> >
> > Doing the lookup by "compatible" string doesn't really help you
> > at solve the problem of single-instance ppe at all, because that
> > function will only ever look at the first one. Use
> > syscon_regmap_lookup_by_phandle instead and pass the phandle
> > you get from the "port-handle" property.
>
> Did some experiment, the only ppe base is got from syscon probe.
> And looking up by "compatible" did work for three controllers at the same time.
The point is that it won't work for more than one ppe instance.
> > Also, since you decided to treat the ppe as a dump regmap, I would
> > recommend moving the 'reg' and 'channel' properties into arguments
> > of the port-handle link, and retting rid of the child nodes of
> > the ppe, like:
> >
> > + ppe: ppe at 28c0000 {
> > + compatible = "hisilicon,hip04-ppe", "syscon";
> > + reg = <0x28c0000 0x10000>;
> > + };
> > +
> > + fe: ethernet at 28b0000 {
> > + compatible = "hisilicon,hip04-mac";
> > + reg = <0x28b0000 0x10000>;
> > + interrupts = <0 413 4>;
> > + phy-mode = "mii";
> > + port-handle = <&ppe 0xf1 0>;
> > + };
> >
>
> Would you mind giving more hints about how to parse the args.
> I am thinking of using of_parse_phandle_with_args, is that correct method?
I would just use of_property_read_u32_array() to read all three cells,
then pass the first cell entry into syscon_regmap_lookup_by_phandle.
of_parse_phandle_with_args() is what you would use if you were registering
a higher-level driver for the ppe rather than using syscon.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 8:12 ` Arnd Bergmann
@ 2014-03-25 17:00 ` Florian Fainelli
-1 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-25 17:00 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Zhangfei Gao, linux-arm-kernel, Mark Rutland, devicetree,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
David S. Miller
2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
>> Dear Arnd
>>
>> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
>> >
>> >> +
>> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> >> +{
>> >> + struct hip04_priv *priv = netdev_priv(ndev);
>> >> + unsigned tx_head = priv->tx_head;
>> >> + unsigned tx_tail = priv->tx_tail;
>> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> >> +
>> >> + while (tx_tail != tx_head) {
>> >> + if (desc->send_addr != 0) {
>> >> + if (force)
>> >> + desc->send_addr = 0;
>> >> + else
>> >> + break;
>> >> + }
>> >> + if (priv->tx_phys[tx_tail]) {
>> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> >> + priv->tx_phys[tx_tail] = 0;
>> >> + }
>> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>> >> + priv->tx_skb[tx_tail] = NULL;
>> >> + tx_tail = TX_NEXT(tx_tail);
>> >> + priv->tx_count--;
>> >> + }
>> >> + priv->tx_tail = tx_tail;
>> >> +}
>> >
>> > I think you still need to find a solution to ensure that the tx reclaim is
>> > called eventually through a method other than start_xmit.
>>
>> In the iperf stress test, if move reclaim to poll, there is some
>> error, sometimes sending zero packets.
>> While keep reclaim in the xmit to reclaim transmitted packets looks
>> stable in the test,
>> There TX_DESC_NUM desc can be used.
>
> What I meant is that you need a correct implementation, presumably
> you added a bug when you moved the function to poll(), and also you
> forgot to add a timer.
Using a timer to ensure completion of TX packets is a trick that
worked in the past, but now that the networking stack got smarter,
this might artificially increase the processing time of packets in the
transmit path, and this will defeat features like TCP small queues
etc.. as could be seen with the mvneta driver [1]. The best way really
is to rely on TX completion interrupts when those exist as they cannot
lie about the hardware status (in theory) and they should provide the
fastest way to complete TX packets.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 17:00 ` Florian Fainelli
0 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-25 17:00 UTC (permalink / raw)
To: linux-arm-kernel
2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
>> Dear Arnd
>>
>> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
>> >
>> >> +
>> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> >> +{
>> >> + struct hip04_priv *priv = netdev_priv(ndev);
>> >> + unsigned tx_head = priv->tx_head;
>> >> + unsigned tx_tail = priv->tx_tail;
>> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> >> +
>> >> + while (tx_tail != tx_head) {
>> >> + if (desc->send_addr != 0) {
>> >> + if (force)
>> >> + desc->send_addr = 0;
>> >> + else
>> >> + break;
>> >> + }
>> >> + if (priv->tx_phys[tx_tail]) {
>> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> >> + priv->tx_phys[tx_tail] = 0;
>> >> + }
>> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>> >> + priv->tx_skb[tx_tail] = NULL;
>> >> + tx_tail = TX_NEXT(tx_tail);
>> >> + priv->tx_count--;
>> >> + }
>> >> + priv->tx_tail = tx_tail;
>> >> +}
>> >
>> > I think you still need to find a solution to ensure that the tx reclaim is
>> > called eventually through a method other than start_xmit.
>>
>> In the iperf stress test, if move reclaim to poll, there is some
>> error, sometimes sending zero packets.
>> While keep reclaim in the xmit to reclaim transmitted packets looks
>> stable in the test,
>> There TX_DESC_NUM desc can be used.
>
> What I meant is that you need a correct implementation, presumably
> you added a bug when you moved the function to poll(), and also you
> forgot to add a timer.
Using a timer to ensure completion of TX packets is a trick that
worked in the past, but now that the networking stack got smarter,
this might artificially increase the processing time of packets in the
transmit path, and this will defeat features like TCP small queues
etc.. as could be seen with the mvneta driver [1]. The best way really
is to rely on TX completion interrupts when those exist as they cannot
lie about the hardware status (in theory) and they should provide the
fastest way to complete TX packets.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:00 ` Florian Fainelli
@ 2014-03-25 17:05 ` Arnd Bergmann
-1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 17:05 UTC (permalink / raw)
To: Florian Fainelli
Cc: Zhangfei Gao, linux-arm-kernel, Mark Rutland, devicetree,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
David S. Miller
On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
> 2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> > On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
> >> Dear Arnd
> >>
> >> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> >> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> >> >
> >> >> +
> >> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> >> >> +{
> >> >> + struct hip04_priv *priv = netdev_priv(ndev);
> >> >> + unsigned tx_head = priv->tx_head;
> >> >> + unsigned tx_tail = priv->tx_tail;
> >> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> >> >> +
> >> >> + while (tx_tail != tx_head) {
> >> >> + if (desc->send_addr != 0) {
> >> >> + if (force)
> >> >> + desc->send_addr = 0;
> >> >> + else
> >> >> + break;
> >> >> + }
> >> >> + if (priv->tx_phys[tx_tail]) {
> >> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> >> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> >> >> + priv->tx_phys[tx_tail] = 0;
> >> >> + }
> >> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
> >> >> + priv->tx_skb[tx_tail] = NULL;
> >> >> + tx_tail = TX_NEXT(tx_tail);
> >> >> + priv->tx_count--;
> >> >> + }
> >> >> + priv->tx_tail = tx_tail;
> >> >> +}
> >> >
> >> > I think you still need to find a solution to ensure that the tx reclaim is
> >> > called eventually through a method other than start_xmit.
> >>
> >> In the iperf stress test, if move reclaim to poll, there is some
> >> error, sometimes sending zero packets.
> >> While keep reclaim in the xmit to reclaim transmitted packets looks
> >> stable in the test,
> >> There TX_DESC_NUM desc can be used.
> >
> > What I meant is that you need a correct implementation, presumably
> > you added a bug when you moved the function to poll(), and also you
> > forgot to add a timer.
>
> Using a timer to ensure completion of TX packets is a trick that
> worked in the past, but now that the networking stack got smarter,
> this might artificially increase the processing time of packets in the
> transmit path, and this will defeat features like TCP small queues
> etc.. as could be seen with the mvneta driver [1]. The best way really
> is to rely on TX completion interrupts when those exist as they cannot
> lie about the hardware status (in theory) and they should provide the
> fastest way to complete TX packets.
By as Zhangfei Gao pointed out, this hardware does not have a working
TX completion interrupt. Using timers to do this has always just been
a workaround for broken hardware IMHO.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 17:05 ` Arnd Bergmann
0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 17:05 UTC (permalink / raw)
To: linux-arm-kernel
On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
> 2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> > On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
> >> Dear Arnd
> >>
> >> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> >> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> >> >
> >> >> +
> >> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> >> >> +{
> >> >> + struct hip04_priv *priv = netdev_priv(ndev);
> >> >> + unsigned tx_head = priv->tx_head;
> >> >> + unsigned tx_tail = priv->tx_tail;
> >> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> >> >> +
> >> >> + while (tx_tail != tx_head) {
> >> >> + if (desc->send_addr != 0) {
> >> >> + if (force)
> >> >> + desc->send_addr = 0;
> >> >> + else
> >> >> + break;
> >> >> + }
> >> >> + if (priv->tx_phys[tx_tail]) {
> >> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> >> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> >> >> + priv->tx_phys[tx_tail] = 0;
> >> >> + }
> >> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
> >> >> + priv->tx_skb[tx_tail] = NULL;
> >> >> + tx_tail = TX_NEXT(tx_tail);
> >> >> + priv->tx_count--;
> >> >> + }
> >> >> + priv->tx_tail = tx_tail;
> >> >> +}
> >> >
> >> > I think you still need to find a solution to ensure that the tx reclaim is
> >> > called eventually through a method other than start_xmit.
> >>
> >> In the iperf stress test, if move reclaim to poll, there is some
> >> error, sometimes sending zero packets.
> >> While keep reclaim in the xmit to reclaim transmitted packets looks
> >> stable in the test,
> >> There TX_DESC_NUM desc can be used.
> >
> > What I meant is that you need a correct implementation, presumably
> > you added a bug when you moved the function to poll(), and also you
> > forgot to add a timer.
>
> Using a timer to ensure completion of TX packets is a trick that
> worked in the past, but now that the networking stack got smarter,
> this might artificially increase the processing time of packets in the
> transmit path, and this will defeat features like TCP small queues
> etc.. as could be seen with the mvneta driver [1]. The best way really
> is to rely on TX completion interrupts when those exist as they cannot
> lie about the hardware status (in theory) and they should provide the
> fastest way to complete TX packets.
By as Zhangfei Gao pointed out, this hardware does not have a working
TX completion interrupt. Using timers to do this has always just been
a workaround for broken hardware IMHO.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:05 ` Arnd Bergmann
@ 2014-03-25 17:16 ` Florian Fainelli
-1 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-25 17:16 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Zhangfei Gao, linux-arm-kernel, Mark Rutland, devicetree,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
David S. Miller
2014-03-25 10:05 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
>> 2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
>> > On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
>> >> Dear Arnd
>> >>
>> >> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
>> >> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
>> >> >
>> >> >> +
>> >> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> >> >> +{
>> >> >> + struct hip04_priv *priv = netdev_priv(ndev);
>> >> >> + unsigned tx_head = priv->tx_head;
>> >> >> + unsigned tx_tail = priv->tx_tail;
>> >> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> >> >> +
>> >> >> + while (tx_tail != tx_head) {
>> >> >> + if (desc->send_addr != 0) {
>> >> >> + if (force)
>> >> >> + desc->send_addr = 0;
>> >> >> + else
>> >> >> + break;
>> >> >> + }
>> >> >> + if (priv->tx_phys[tx_tail]) {
>> >> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> >> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> >> >> + priv->tx_phys[tx_tail] = 0;
>> >> >> + }
>> >> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>> >> >> + priv->tx_skb[tx_tail] = NULL;
>> >> >> + tx_tail = TX_NEXT(tx_tail);
>> >> >> + priv->tx_count--;
>> >> >> + }
>> >> >> + priv->tx_tail = tx_tail;
>> >> >> +}
>> >> >
>> >> > I think you still need to find a solution to ensure that the tx reclaim is
>> >> > called eventually through a method other than start_xmit.
>> >>
>> >> In the iperf stress test, if move reclaim to poll, there is some
>> >> error, sometimes sending zero packets.
>> >> While keep reclaim in the xmit to reclaim transmitted packets looks
>> >> stable in the test,
>> >> There TX_DESC_NUM desc can be used.
>> >
>> > What I meant is that you need a correct implementation, presumably
>> > you added a bug when you moved the function to poll(), and also you
>> > forgot to add a timer.
>>
>> Using a timer to ensure completion of TX packets is a trick that
>> worked in the past, but now that the networking stack got smarter,
>> this might artificially increase the processing time of packets in the
>> transmit path, and this will defeat features like TCP small queues
>> etc.. as could be seen with the mvneta driver [1]. The best way really
>> is to rely on TX completion interrupts when those exist as they cannot
>> lie about the hardware status (in theory) and they should provide the
>> fastest way to complete TX packets.
>
> By as Zhangfei Gao pointed out, this hardware does not have a working
> TX completion interrupt. Using timers to do this has always just been
> a workaround for broken hardware IMHO.
Ok, well that's really unfortunate, to achieve the best of everything,
the workaround should probably look like:
- keep reclaiming TX buffers in ndo_start_xmit() in case you push more
packets to the NICs than your timer can free
- reclaim TX buffers in NAPI poll() context for "symetrical" workloads
where e.g: TCP ACKs received allow you to complete TX buffers
- have a timer like you suggest which should help with transmit only
workloads at a slow rate
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 17:16 ` Florian Fainelli
0 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-25 17:16 UTC (permalink / raw)
To: linux-arm-kernel
2014-03-25 10:05 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
>> 2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
>> > On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
>> >> Dear Arnd
>> >>
>> >> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
>> >> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
>> >> >
>> >> >> +
>> >> >> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> >> >> +{
>> >> >> + struct hip04_priv *priv = netdev_priv(ndev);
>> >> >> + unsigned tx_head = priv->tx_head;
>> >> >> + unsigned tx_tail = priv->tx_tail;
>> >> >> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> >> >> +
>> >> >> + while (tx_tail != tx_head) {
>> >> >> + if (desc->send_addr != 0) {
>> >> >> + if (force)
>> >> >> + desc->send_addr = 0;
>> >> >> + else
>> >> >> + break;
>> >> >> + }
>> >> >> + if (priv->tx_phys[tx_tail]) {
>> >> >> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> >> >> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> >> >> + priv->tx_phys[tx_tail] = 0;
>> >> >> + }
>> >> >> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>> >> >> + priv->tx_skb[tx_tail] = NULL;
>> >> >> + tx_tail = TX_NEXT(tx_tail);
>> >> >> + priv->tx_count--;
>> >> >> + }
>> >> >> + priv->tx_tail = tx_tail;
>> >> >> +}
>> >> >
>> >> > I think you still need to find a solution to ensure that the tx reclaim is
>> >> > called eventually through a method other than start_xmit.
>> >>
>> >> In the iperf stress test, if move reclaim to poll, there is some
>> >> error, sometimes sending zero packets.
>> >> While keep reclaim in the xmit to reclaim transmitted packets looks
>> >> stable in the test,
>> >> There TX_DESC_NUM desc can be used.
>> >
>> > What I meant is that you need a correct implementation, presumably
>> > you added a bug when you moved the function to poll(), and also you
>> > forgot to add a timer.
>>
>> Using a timer to ensure completion of TX packets is a trick that
>> worked in the past, but now that the networking stack got smarter,
>> this might artificially increase the processing time of packets in the
>> transmit path, and this will defeat features like TCP small queues
>> etc.. as could be seen with the mvneta driver [1]. The best way really
>> is to rely on TX completion interrupts when those exist as they cannot
>> lie about the hardware status (in theory) and they should provide the
>> fastest way to complete TX packets.
>
> By as Zhangfei Gao pointed out, this hardware does not have a working
> TX completion interrupt. Using timers to do this has always just been
> a workaround for broken hardware IMHO.
Ok, well that's really unfortunate, to achieve the best of everything,
the workaround should probably look like:
- keep reclaiming TX buffers in ndo_start_xmit() in case you push more
packets to the NICs than your timer can free
- reclaim TX buffers in NAPI poll() context for "symetrical" workloads
where e.g: TCP ACKs received allow you to complete TX buffers
- have a timer like you suggest which should help with transmit only
workloads at a slow rate
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:16 ` Florian Fainelli
@ 2014-03-25 17:57 ` Arnd Bergmann
-1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 17:57 UTC (permalink / raw)
To: Florian Fainelli
Cc: Mark Rutland, devicetree, Russell King - ARM Linux,
Sergei Shtylyov, netdev, Zhangfei Gao, Zhangfei Gao,
David S. Miller, linux-arm-kernel
On Tuesday 25 March 2014 10:16:28 Florian Fainelli wrote:
>
> Ok, well that's really unfortunate, to achieve the best of everything,
> the workaround should probably look like:
>
> - keep reclaiming TX buffers in ndo_start_xmit() in case you push more
> packets to the NICs than your timer can free
> - reclaim TX buffers in NAPI poll() context for "symetrical" workloads
> where e.g: TCP ACKs received allow you to complete TX buffers
> - have a timer like you suggest which should help with transmit only
> workloads at a slow rate
Yes, that is what I was thinking, but with orphaning the tx skbs,
we can probably be a little smarter. Note that in order to check
the state of the queue, we have to do a read from uncached memory,
since the hardware also doesn't support cache coherent DMA.
We don't want to do that too often.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 17:57 ` Arnd Bergmann
0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 17:57 UTC (permalink / raw)
To: linux-arm-kernel
On Tuesday 25 March 2014 10:16:28 Florian Fainelli wrote:
>
> Ok, well that's really unfortunate, to achieve the best of everything,
> the workaround should probably look like:
>
> - keep reclaiming TX buffers in ndo_start_xmit() in case you push more
> packets to the NICs than your timer can free
> - reclaim TX buffers in NAPI poll() context for "symetrical" workloads
> where e.g: TCP ACKs received allow you to complete TX buffers
> - have a timer like you suggest which should help with transmit only
> workloads at a slow rate
Yes, that is what I was thinking, but with orphaning the tx skbs,
we can probably be a little smarter. Note that in order to check
the state of the queue, we have to do a read from uncached memory,
since the hardware also doesn't support cache coherent DMA.
We don't want to do that too often.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* RE: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:57 ` Arnd Bergmann
@ 2014-03-26 9:55 ` David Laight
-1 siblings, 0 replies; 78+ messages in thread
From: David Laight @ 2014-03-26 9:55 UTC (permalink / raw)
To: 'Arnd Bergmann', Florian Fainelli
Cc: Zhangfei Gao, linux-arm-kernel, Mark Rutland, devicetree,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
David S. Miller
From: Arnd Bergmann
> On Tuesday 25 March 2014 10:16:28 Florian Fainelli wrote:
> >
> > Ok, well that's really unfortunate, to achieve the best of everything,
> > the workaround should probably look like:
> >
> > - keep reclaiming TX buffers in ndo_start_xmit() in case you push more
> > packets to the NICs than your timer can free
> > - reclaim TX buffers in NAPI poll() context for "symetrical" workloads
> > where e.g: TCP ACKs received allow you to complete TX buffers
> > - have a timer like you suggest which should help with transmit only
> > workloads at a slow rate
>
> Yes, that is what I was thinking, but with orphaning the tx skbs,
> we can probably be a little smarter. Note that in order to check
> the state of the queue, we have to do a read from uncached memory,
> since the hardware also doesn't support cache coherent DMA.
> We don't want to do that too often.
Possibly you can check for all the pending transmits having
completed - the most likely case. Instead of checking them
individually?
You should limit the number of tx bytes as well as the number
of tx frames - buffering a ring full of large frames subverts
some of the algorithms higher up the stack.
David
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-26 9:55 ` David Laight
0 siblings, 0 replies; 78+ messages in thread
From: David Laight @ 2014-03-26 9:55 UTC (permalink / raw)
To: linux-arm-kernel
From: Arnd Bergmann
> On Tuesday 25 March 2014 10:16:28 Florian Fainelli wrote:
> >
> > Ok, well that's really unfortunate, to achieve the best of everything,
> > the workaround should probably look like:
> >
> > - keep reclaiming TX buffers in ndo_start_xmit() in case you push more
> > packets to the NICs than your timer can free
> > - reclaim TX buffers in NAPI poll() context for "symetrical" workloads
> > where e.g: TCP ACKs received allow you to complete TX buffers
> > - have a timer like you suggest which should help with transmit only
> > workloads at a slow rate
>
> Yes, that is what I was thinking, but with orphaning the tx skbs,
> we can probably be a little smarter. Note that in order to check
> the state of the queue, we have to do a read from uncached memory,
> since the hardware also doesn't support cache coherent DMA.
> We don't want to do that too often.
Possibly you can check for all the pending transmits having
completed - the most likely case. Instead of checking them
individually?
You should limit the number of tx bytes as well as the number
of tx frames - buffering a ring full of large frames subverts
some of the algorithms higher up the stack.
David
^ permalink raw reply [flat|nested] 78+ messages in thread
* RE: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:05 ` Arnd Bergmann
@ 2014-03-25 17:17 ` David Laight
-1 siblings, 0 replies; 78+ messages in thread
From: David Laight @ 2014-03-25 17:17 UTC (permalink / raw)
To: 'Arnd Bergmann', Florian Fainelli
Cc: Zhangfei Gao, linux-arm-kernel, Mark Rutland, devicetree,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
David S. Miller
From: Arnd Bergmann
> > Using a timer to ensure completion of TX packets is a trick that
> > worked in the past, but now that the networking stack got smarter,
> > this might artificially increase the processing time of packets in the
> > transmit path, and this will defeat features like TCP small queues
> > etc.. as could be seen with the mvneta driver [1]. The best way really
> > is to rely on TX completion interrupts when those exist as they cannot
> > lie about the hardware status (in theory) and they should provide the
> > fastest way to complete TX packets.
>
> By as Zhangfei Gao pointed out, this hardware does not have a working
> TX completion interrupt. Using timers to do this has always just been
> a workaround for broken hardware IMHO.
I remember disabling the 'tx done' interrupt (unless the tx ring
was full) in order to get a significant increase in throughput
due to the reduced interrupt load.
The 'interrupt mitigation' logic on modern hardware probably makes
this less of a problem.
It might be possible to orphan the skb when they are put into the
tx ring, and to significantly limit the number of bytes in the
tx ring (BQL?).
That might upset TCP small queues less than delaying the actual
tx completions.
David
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 17:17 ` David Laight
0 siblings, 0 replies; 78+ messages in thread
From: David Laight @ 2014-03-25 17:17 UTC (permalink / raw)
To: linux-arm-kernel
From: Arnd Bergmann
> > Using a timer to ensure completion of TX packets is a trick that
> > worked in the past, but now that the networking stack got smarter,
> > this might artificially increase the processing time of packets in the
> > transmit path, and this will defeat features like TCP small queues
> > etc.. as could be seen with the mvneta driver [1]. The best way really
> > is to rely on TX completion interrupts when those exist as they cannot
> > lie about the hardware status (in theory) and they should provide the
> > fastest way to complete TX packets.
>
> By as Zhangfei Gao pointed out, this hardware does not have a working
> TX completion interrupt. Using timers to do this has always just been
> a workaround for broken hardware IMHO.
I remember disabling the 'tx done' interrupt (unless the tx ring
was full) in order to get a significant increase in throughput
due to the reduced interrupt load.
The 'interrupt mitigation' logic on modern hardware probably makes
this less of a problem.
It might be possible to orphan the skb when they are put into the
tx ring, and to significantly limit the number of bytes in the
tx ring (BQL?).
That might upset TCP small queues less than delaying the actual
tx completions.
David
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:05 ` Arnd Bergmann
@ 2014-03-25 17:21 ` Eric Dumazet
-1 siblings, 0 replies; 78+ messages in thread
From: Eric Dumazet @ 2014-03-25 17:21 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Mark Rutland, devicetree, Florian Fainelli,
Russell King - ARM Linux, Sergei Shtylyov, netdev, Zhangfei Gao,
Zhangfei Gao, David S. Miller, linux-arm-kernel
On Tue, 2014-03-25 at 18:05 +0100, Arnd Bergmann wrote:
> On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
> > 2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> > > On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
> > >> Dear Arnd
> > >>
> > >> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> > >> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> > >> >
...
> > >> > I think you still need to find a solution to ensure that the tx reclaim is
> > >> > called eventually through a method other than start_xmit.
> > >>
> > >> In the iperf stress test, if move reclaim to poll, there is some
> > >> error, sometimes sending zero packets.
> > >> While keep reclaim in the xmit to reclaim transmitted packets looks
> > >> stable in the test,
> > >> There TX_DESC_NUM desc can be used.
> > >
> > > What I meant is that you need a correct implementation, presumably
> > > you added a bug when you moved the function to poll(), and also you
> > > forgot to add a timer.
> >
> > Using a timer to ensure completion of TX packets is a trick that
> > worked in the past, but now that the networking stack got smarter,
> > this might artificially increase the processing time of packets in the
> > transmit path, and this will defeat features like TCP small queues
> > etc.. as could be seen with the mvneta driver [1]. The best way really
> > is to rely on TX completion interrupts when those exist as they cannot
> > lie about the hardware status (in theory) and they should provide the
> > fastest way to complete TX packets.
>
> By as Zhangfei Gao pointed out, this hardware does not have a working
> TX completion interrupt. Using timers to do this has always just been
> a workaround for broken hardware IMHO.
For this kind of drivers, calling skb_orphan() from ndo_start_xmit() is
mandatory.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 17:21 ` Eric Dumazet
0 siblings, 0 replies; 78+ messages in thread
From: Eric Dumazet @ 2014-03-25 17:21 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, 2014-03-25 at 18:05 +0100, Arnd Bergmann wrote:
> On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
> > 2014-03-25 1:12 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> > > On Tuesday 25 March 2014 12:06:31 Zhangfei Gao wrote:
> > >> Dear Arnd
> > >>
> > >> On Mon, Mar 24, 2014 at 11:18 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> > >> > On Monday 24 March 2014 22:14:56 Zhangfei Gao wrote:
> > >> >
...
> > >> > I think you still need to find a solution to ensure that the tx reclaim is
> > >> > called eventually through a method other than start_xmit.
> > >>
> > >> In the iperf stress test, if move reclaim to poll, there is some
> > >> error, sometimes sending zero packets.
> > >> While keep reclaim in the xmit to reclaim transmitted packets looks
> > >> stable in the test,
> > >> There TX_DESC_NUM desc can be used.
> > >
> > > What I meant is that you need a correct implementation, presumably
> > > you added a bug when you moved the function to poll(), and also you
> > > forgot to add a timer.
> >
> > Using a timer to ensure completion of TX packets is a trick that
> > worked in the past, but now that the networking stack got smarter,
> > this might artificially increase the processing time of packets in the
> > transmit path, and this will defeat features like TCP small queues
> > etc.. as could be seen with the mvneta driver [1]. The best way really
> > is to rely on TX completion interrupts when those exist as they cannot
> > lie about the hardware status (in theory) and they should provide the
> > fastest way to complete TX packets.
>
> By as Zhangfei Gao pointed out, this hardware does not have a working
> TX completion interrupt. Using timers to do this has always just been
> a workaround for broken hardware IMHO.
For this kind of drivers, calling skb_orphan() from ndo_start_xmit() is
mandatory.
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:21 ` Eric Dumazet
@ 2014-03-25 17:54 ` Arnd Bergmann
-1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 17:54 UTC (permalink / raw)
To: Eric Dumazet
Cc: Florian Fainelli, Zhangfei Gao, linux-arm-kernel, Mark Rutland,
devicetree, Russell King - ARM Linux, Sergei Shtylyov, netdev,
Zhangfei Gao, David S. Miller
On Tuesday 25 March 2014 10:21:42 Eric Dumazet wrote:
> On Tue, 2014-03-25 at 18:05 +0100, Arnd Bergmann wrote:
> > On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
> >
> > > Using a timer to ensure completion of TX packets is a trick that
> > > worked in the past, but now that the networking stack got smarter,
> > > this might artificially increase the processing time of packets in the
> > > transmit path, and this will defeat features like TCP small queues
> > > etc.. as could be seen with the mvneta driver [1]. The best way really
> > > is to rely on TX completion interrupts when those exist as they cannot
> > > lie about the hardware status (in theory) and they should provide the
> > > fastest way to complete TX packets.
> >
> > By as Zhangfei Gao pointed out, this hardware does not have a working
> > TX completion interrupt. Using timers to do this has always just been
> > a workaround for broken hardware IMHO.
>
> For this kind of drivers, calling skb_orphan() from ndo_start_xmit() is
> mandatory.
Cool, thanks for the information, I was wondering already if there was
a way to deal with hardware like this.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-25 17:54 ` Arnd Bergmann
0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-25 17:54 UTC (permalink / raw)
To: linux-arm-kernel
On Tuesday 25 March 2014 10:21:42 Eric Dumazet wrote:
> On Tue, 2014-03-25 at 18:05 +0100, Arnd Bergmann wrote:
> > On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
> >
> > > Using a timer to ensure completion of TX packets is a trick that
> > > worked in the past, but now that the networking stack got smarter,
> > > this might artificially increase the processing time of packets in the
> > > transmit path, and this will defeat features like TCP small queues
> > > etc.. as could be seen with the mvneta driver [1]. The best way really
> > > is to rely on TX completion interrupts when those exist as they cannot
> > > lie about the hardware status (in theory) and they should provide the
> > > fastest way to complete TX packets.
> >
> > By as Zhangfei Gao pointed out, this hardware does not have a working
> > TX completion interrupt. Using timers to do this has always just been
> > a workaround for broken hardware IMHO.
>
> For this kind of drivers, calling skb_orphan() from ndo_start_xmit() is
> mandatory.
Cool, thanks for the information, I was wondering already if there was
a way to deal with hardware like this.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-25 17:54 ` Arnd Bergmann
@ 2014-03-27 12:53 ` zhangfei
-1 siblings, 0 replies; 78+ messages in thread
From: zhangfei @ 2014-03-27 12:53 UTC (permalink / raw)
To: Arnd Bergmann, Eric Dumazet
Cc: Florian Fainelli, Zhangfei Gao, linux-arm-kernel, Mark Rutland,
devicetree, Russell King - ARM Linux, Sergei Shtylyov, netdev,
David S. Miller
On 03/26/2014 01:54 AM, Arnd Bergmann wrote:
> On Tuesday 25 March 2014 10:21:42 Eric Dumazet wrote:
>> On Tue, 2014-03-25 at 18:05 +0100, Arnd Bergmann wrote:
>>> On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
>>>
>>>> Using a timer to ensure completion of TX packets is a trick that
>>>> worked in the past, but now that the networking stack got smarter,
>>>> this might artificially increase the processing time of packets in the
>>>> transmit path, and this will defeat features like TCP small queues
>>>> etc.. as could be seen with the mvneta driver [1]. The best way really
>>>> is to rely on TX completion interrupts when those exist as they cannot
>>>> lie about the hardware status (in theory) and they should provide the
>>>> fastest way to complete TX packets.
>>>
>>> By as Zhangfei Gao pointed out, this hardware does not have a working
>>> TX completion interrupt. Using timers to do this has always just been
>>> a workaround for broken hardware IMHO.
>>
>> For this kind of drivers, calling skb_orphan() from ndo_start_xmit() is
>> mandatory.
>
> Cool, thanks for the information, I was wondering already if there was
> a way to deal with hardware like this.
>
That's great,
In the experiment, keeping reclaim in the ndo_start_xmit always get the
best throughput, also simpler, even no requirement of spin_lock.
By the way, still have confusion about build_skb.
At first, I thought we can malloc n*buffers as a ring and keep using
them for dma, every time when packet coming, using build_skb adds a
head, send to upper layer.
After data is consumed, we can continue reuse the buffer next time.
However, in the iperf stress test, always error happen.
The buffer is released in fact, and we need alloc new buffer for the
next transfer.
So the build_skb is not used for reusing buffers, but only for keeping
hot data in cache, right?
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-27 12:53 ` zhangfei
0 siblings, 0 replies; 78+ messages in thread
From: zhangfei @ 2014-03-27 12:53 UTC (permalink / raw)
To: linux-arm-kernel
On 03/26/2014 01:54 AM, Arnd Bergmann wrote:
> On Tuesday 25 March 2014 10:21:42 Eric Dumazet wrote:
>> On Tue, 2014-03-25 at 18:05 +0100, Arnd Bergmann wrote:
>>> On Tuesday 25 March 2014 10:00:30 Florian Fainelli wrote:
>>>
>>>> Using a timer to ensure completion of TX packets is a trick that
>>>> worked in the past, but now that the networking stack got smarter,
>>>> this might artificially increase the processing time of packets in the
>>>> transmit path, and this will defeat features like TCP small queues
>>>> etc.. as could be seen with the mvneta driver [1]. The best way really
>>>> is to rely on TX completion interrupts when those exist as they cannot
>>>> lie about the hardware status (in theory) and they should provide the
>>>> fastest way to complete TX packets.
>>>
>>> By as Zhangfei Gao pointed out, this hardware does not have a working
>>> TX completion interrupt. Using timers to do this has always just been
>>> a workaround for broken hardware IMHO.
>>
>> For this kind of drivers, calling skb_orphan() from ndo_start_xmit() is
>> mandatory.
>
> Cool, thanks for the information, I was wondering already if there was
> a way to deal with hardware like this.
>
That's great,
In the experiment, keeping reclaim in the ndo_start_xmit always get the
best throughput, also simpler, even no requirement of spin_lock.
By the way, still have confusion about build_skb.
At first, I thought we can malloc n*buffers as a ring and keep using
them for dma, every time when packet coming, using build_skb adds a
head, send to upper layer.
After data is consumed, we can continue reuse the buffer next time.
However, in the iperf stress test, always error happen.
The buffer is released in fact, and we need alloc new buffer for the
next transfer.
So the build_skb is not used for reusing buffers, but only for keeping
hot data in cache, right?
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-24 14:14 ` Zhangfei Gao
@ 2014-03-24 16:32 ` Florian Fainelli
-1 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-24 16:32 UTC (permalink / raw)
To: Zhangfei Gao
Cc: David Miller, Russell King, Arnd Bergmann, Sergei Shtylyov,
Mark Rutland, linux-arm-kernel, netdev, devicetree
2014-03-24 7:14 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
> Support Hisilicon hip04 ethernet driver, including 100M / 1000M controller
>
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> ---
> drivers/net/ethernet/hisilicon/Makefile | 2 +-
> drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++++++++++
> 2 files changed, 729 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
[snip]
> +static void hip04_config_port(struct hip04_priv *priv, u32 speed, u32 duplex)
> +{
> + u32 val;
> +
> + priv->speed = speed;
> + priv->duplex = duplex;
> +
> + switch (priv->phy_mode) {
> + case PHY_INTERFACE_MODE_SGMII:
> + if (speed == SPEED_1000)
> + val = 8;
> + else
> + val = 7;
> + break;
> + case PHY_INTERFACE_MODE_MII:
> + val = 1; /* SPEED_100 */
> + break;
> + default:
> + val = 0;
> + break;
Is 0 valid for e.g: 10Mbits/sec, regardless of the phy_mode?
[snip]
> +
> +static void hip04_mac_enable(struct net_device *ndev, bool enable)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + u32 val;
> +
> + if (enable) {
> + /* enable tx & rx */
> + val = readl_relaxed(priv->base + GE_PORT_EN);
> + val |= BIT(1); /* rx*/
> + val |= BIT(2); /* tx*/
> + writel_relaxed(val, priv->base + GE_PORT_EN);
> +
> + /* enable interrupt */
> + priv->reg_inten = DEF_INT_MASK;
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> +
> + /* clear rx int */
> + val = RCV_INT;
> + writel_relaxed(val, priv->base + PPE_RINT);
Should not you first clear the interrupt and then DEF_INT_MASK? Why is
there a RCV_INT applied to PPE_RINT register in the enable path, but
there is no such thing in the "disable" branch of your function?
> +
> + /* config recv int*/
> + val = BIT(6); /* int threshold 1 package */
> + val |= 0x4; /* recv timeout */
> + writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT);
> + } else {
> + /* disable int */
> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> +
> + /* disable tx & rx */
> + val = readl_relaxed(priv->base + GE_PORT_EN);
> + val &= ~(BIT(1)); /* rx*/
> + val &= ~(BIT(2)); /* tx*/
> + writel_relaxed(val, priv->base + GE_PORT_EN);
> + }
There is little to no sharing between the two branches, I would have
created separate helper functions for the enable/disable logic.
> +}
> +
> +static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys)
> +{
> + writel(phys, priv->base + PPE_CFG_TX_PKT_BD_ADDR);
This is not 64-bits/LPAE safe, do you have a High address part and a
Low address part for your address in the buffer descriptor address, if
so, better use it now.
[snip]
> +
> +static int hip04_rx_poll(struct napi_struct *napi, int budget)
> +{
> + struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
> + struct net_device *ndev = priv->ndev;
> + struct net_device_stats *stats = &ndev->stats;
> + unsigned int cnt = hip04_recv_cnt(priv);
> + struct sk_buff *skb;
> + struct rx_desc *desc;
> + unsigned char *buf;
> + dma_addr_t phys;
> + int rx = 0;
> + u16 len;
> + u32 err;
> +
> + while (cnt) {
> + buf = priv->rx_buf[priv->rx_head];
> + skb = build_skb(buf, priv->rx_buf_size);
> + if (unlikely(!skb))
> + net_dbg_ratelimited("build_skb failed\n");
> +
> + dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head],
> + RX_BUF_SIZE, DMA_FROM_DEVICE);
> + priv->rx_phys[priv->rx_head] = 0;
> +
> + desc = (struct rx_desc *)skb->data;
> + len = be16_to_cpu(desc->pkt_len);
> + err = be32_to_cpu(desc->pkt_err);
> +
> + if (len > RX_BUF_SIZE)
> + len = RX_BUF_SIZE;
> + if (0 == len)
> + break;
Should not this be a continue? This is an error packet, so you should
keep on processing the others, or does this have a special meaning?
> +
> + if (err & RX_PKT_ERR) {
> + dev_kfree_skb_any(skb);
> + stats->rx_dropped++;
> + stats->rx_errors++;
> + continue;
> + }
> +
> + stats->rx_packets++;
> + stats->rx_bytes += len;
> +
> + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
> + skb_put(skb, len);
> + skb->protocol = eth_type_trans(skb, ndev);
> + napi_gro_receive(&priv->napi, skb);
> +
> + buf = netdev_alloc_frag(priv->rx_buf_size);
> + if (!buf)
> + return -ENOMEM;
> + phys = dma_map_single(&ndev->dev, buf,
> + RX_BUF_SIZE, DMA_FROM_DEVICE);
Missing dma_mapping_error() check here.
> + priv->rx_buf[priv->rx_head] = buf;
> + priv->rx_phys[priv->rx_head] = phys;
> + hip04_set_recv_desc(priv, phys);
> +
> + priv->rx_head = RX_NEXT(priv->rx_head);
> + if (rx++ >= budget)
> + break;
> +
> + if (--cnt == 0)
> + cnt = hip04_recv_cnt(priv);
> + }
> +
> + if (rx < budget) {
> + napi_complete(napi);
> +
> + /* enable rx interrupt */
> + priv->reg_inten |= RCV_INT | RCV_NOBUF;
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> + }
> +
> + return rx;
> +}
> +
> +static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *) dev_id;
> + struct hip04_priv *priv = netdev_priv(ndev);
> + u32 ists = readl_relaxed(priv->base + PPE_INTSTS);
> + u32 val = DEF_INT_MASK;
> +
> + writel_relaxed(val, priv->base + PPE_RINT);
> +
> + if (ists & (RCV_INT | RCV_NOBUF)) {
> + if (napi_schedule_prep(&priv->napi)) {
> + /* disable rx interrupt */
> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> + __napi_schedule(&priv->napi);
> + }
> + }
You should also process TX completion interrupts here
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + unsigned tx_head = priv->tx_head;
> + unsigned tx_tail = priv->tx_tail;
> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> +
> + while (tx_tail != tx_head) {
> + if (desc->send_addr != 0) {
> + if (force)
> + desc->send_addr = 0;
> + else
> + break;
> + }
> + if (priv->tx_phys[tx_tail]) {
> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> + priv->tx_phys[tx_tail] = 0;
> + }
> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
dev_kfree_skb_irq() bypasses all sort of SKB tracking, you might want
to use kfree_skb() here instead.
> + priv->tx_skb[tx_tail] = NULL;
> + tx_tail = TX_NEXT(tx_tail);
> + priv->tx_count--;
> + }
> + priv->tx_tail = tx_tail;
> +}
> +
> +static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + unsigned int tx_head = priv->tx_head;
> + struct tx_desc *desc = &priv->tx_desc[tx_head];
> + dma_addr_t phys;
> +
> + hip04_tx_reclaim(ndev, false);
> +
> + if (priv->tx_count++ >= TX_DESC_NUM) {
> + net_dbg_ratelimited("no TX space for packet\n");
> + netif_stop_queue(ndev);
> + return NETDEV_TX_BUSY;
> + }
> +
> + phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE);
Missing dma_mapping_error() check here
> + priv->tx_skb[tx_head] = skb;
> + priv->tx_phys[tx_head] = phys;
> + desc->send_addr = cpu_to_be32(phys);
> + desc->send_size = cpu_to_be16(skb->len);
> + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
> + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
> + desc->wb_addr = cpu_to_be32(phys);
Don't we need a barrier here to ensure that all stores are completed
before we hand this descriptor address to hip40_set_xmit_desc() which
should make DMA start processing it?
> + skb_tx_timestamp(skb);
> + hip04_set_xmit_desc(priv, phys);
> + priv->tx_head = TX_NEXT(tx_head);
> +
> + stats->tx_bytes += skb->len;
> + stats->tx_packets++;
You cannot update the transmit stats here, what start_xmit() does it
just queue packets for the DMA engine to process them, but that does
not mean DMA has completed those. You should update statistics in the
tx_reclaim() function.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-24 16:32 ` Florian Fainelli
0 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-24 16:32 UTC (permalink / raw)
To: linux-arm-kernel
2014-03-24 7:14 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
> Support Hisilicon hip04 ethernet driver, including 100M / 1000M controller
>
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> ---
> drivers/net/ethernet/hisilicon/Makefile | 2 +-
> drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++++++++++
> 2 files changed, 729 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
[snip]
> +static void hip04_config_port(struct hip04_priv *priv, u32 speed, u32 duplex)
> +{
> + u32 val;
> +
> + priv->speed = speed;
> + priv->duplex = duplex;
> +
> + switch (priv->phy_mode) {
> + case PHY_INTERFACE_MODE_SGMII:
> + if (speed == SPEED_1000)
> + val = 8;
> + else
> + val = 7;
> + break;
> + case PHY_INTERFACE_MODE_MII:
> + val = 1; /* SPEED_100 */
> + break;
> + default:
> + val = 0;
> + break;
Is 0 valid for e.g: 10Mbits/sec, regardless of the phy_mode?
[snip]
> +
> +static void hip04_mac_enable(struct net_device *ndev, bool enable)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + u32 val;
> +
> + if (enable) {
> + /* enable tx & rx */
> + val = readl_relaxed(priv->base + GE_PORT_EN);
> + val |= BIT(1); /* rx*/
> + val |= BIT(2); /* tx*/
> + writel_relaxed(val, priv->base + GE_PORT_EN);
> +
> + /* enable interrupt */
> + priv->reg_inten = DEF_INT_MASK;
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> +
> + /* clear rx int */
> + val = RCV_INT;
> + writel_relaxed(val, priv->base + PPE_RINT);
Should not you first clear the interrupt and then DEF_INT_MASK? Why is
there a RCV_INT applied to PPE_RINT register in the enable path, but
there is no such thing in the "disable" branch of your function?
> +
> + /* config recv int*/
> + val = BIT(6); /* int threshold 1 package */
> + val |= 0x4; /* recv timeout */
> + writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT);
> + } else {
> + /* disable int */
> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> +
> + /* disable tx & rx */
> + val = readl_relaxed(priv->base + GE_PORT_EN);
> + val &= ~(BIT(1)); /* rx*/
> + val &= ~(BIT(2)); /* tx*/
> + writel_relaxed(val, priv->base + GE_PORT_EN);
> + }
There is little to no sharing between the two branches, I would have
created separate helper functions for the enable/disable logic.
> +}
> +
> +static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys)
> +{
> + writel(phys, priv->base + PPE_CFG_TX_PKT_BD_ADDR);
This is not 64-bits/LPAE safe, do you have a High address part and a
Low address part for your address in the buffer descriptor address, if
so, better use it now.
[snip]
> +
> +static int hip04_rx_poll(struct napi_struct *napi, int budget)
> +{
> + struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
> + struct net_device *ndev = priv->ndev;
> + struct net_device_stats *stats = &ndev->stats;
> + unsigned int cnt = hip04_recv_cnt(priv);
> + struct sk_buff *skb;
> + struct rx_desc *desc;
> + unsigned char *buf;
> + dma_addr_t phys;
> + int rx = 0;
> + u16 len;
> + u32 err;
> +
> + while (cnt) {
> + buf = priv->rx_buf[priv->rx_head];
> + skb = build_skb(buf, priv->rx_buf_size);
> + if (unlikely(!skb))
> + net_dbg_ratelimited("build_skb failed\n");
> +
> + dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head],
> + RX_BUF_SIZE, DMA_FROM_DEVICE);
> + priv->rx_phys[priv->rx_head] = 0;
> +
> + desc = (struct rx_desc *)skb->data;
> + len = be16_to_cpu(desc->pkt_len);
> + err = be32_to_cpu(desc->pkt_err);
> +
> + if (len > RX_BUF_SIZE)
> + len = RX_BUF_SIZE;
> + if (0 == len)
> + break;
Should not this be a continue? This is an error packet, so you should
keep on processing the others, or does this have a special meaning?
> +
> + if (err & RX_PKT_ERR) {
> + dev_kfree_skb_any(skb);
> + stats->rx_dropped++;
> + stats->rx_errors++;
> + continue;
> + }
> +
> + stats->rx_packets++;
> + stats->rx_bytes += len;
> +
> + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
> + skb_put(skb, len);
> + skb->protocol = eth_type_trans(skb, ndev);
> + napi_gro_receive(&priv->napi, skb);
> +
> + buf = netdev_alloc_frag(priv->rx_buf_size);
> + if (!buf)
> + return -ENOMEM;
> + phys = dma_map_single(&ndev->dev, buf,
> + RX_BUF_SIZE, DMA_FROM_DEVICE);
Missing dma_mapping_error() check here.
> + priv->rx_buf[priv->rx_head] = buf;
> + priv->rx_phys[priv->rx_head] = phys;
> + hip04_set_recv_desc(priv, phys);
> +
> + priv->rx_head = RX_NEXT(priv->rx_head);
> + if (rx++ >= budget)
> + break;
> +
> + if (--cnt == 0)
> + cnt = hip04_recv_cnt(priv);
> + }
> +
> + if (rx < budget) {
> + napi_complete(napi);
> +
> + /* enable rx interrupt */
> + priv->reg_inten |= RCV_INT | RCV_NOBUF;
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> + }
> +
> + return rx;
> +}
> +
> +static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *) dev_id;
> + struct hip04_priv *priv = netdev_priv(ndev);
> + u32 ists = readl_relaxed(priv->base + PPE_INTSTS);
> + u32 val = DEF_INT_MASK;
> +
> + writel_relaxed(val, priv->base + PPE_RINT);
> +
> + if (ists & (RCV_INT | RCV_NOBUF)) {
> + if (napi_schedule_prep(&priv->napi)) {
> + /* disable rx interrupt */
> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
> + __napi_schedule(&priv->napi);
> + }
> + }
You should also process TX completion interrupts here
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + unsigned tx_head = priv->tx_head;
> + unsigned tx_tail = priv->tx_tail;
> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
> +
> + while (tx_tail != tx_head) {
> + if (desc->send_addr != 0) {
> + if (force)
> + desc->send_addr = 0;
> + else
> + break;
> + }
> + if (priv->tx_phys[tx_tail]) {
> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
> + priv->tx_phys[tx_tail] = 0;
> + }
> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
dev_kfree_skb_irq() bypasses all sort of SKB tracking, you might want
to use kfree_skb() here instead.
> + priv->tx_skb[tx_tail] = NULL;
> + tx_tail = TX_NEXT(tx_tail);
> + priv->tx_count--;
> + }
> + priv->tx_tail = tx_tail;
> +}
> +
> +static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct hip04_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + unsigned int tx_head = priv->tx_head;
> + struct tx_desc *desc = &priv->tx_desc[tx_head];
> + dma_addr_t phys;
> +
> + hip04_tx_reclaim(ndev, false);
> +
> + if (priv->tx_count++ >= TX_DESC_NUM) {
> + net_dbg_ratelimited("no TX space for packet\n");
> + netif_stop_queue(ndev);
> + return NETDEV_TX_BUSY;
> + }
> +
> + phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE);
Missing dma_mapping_error() check here
> + priv->tx_skb[tx_head] = skb;
> + priv->tx_phys[tx_head] = phys;
> + desc->send_addr = cpu_to_be32(phys);
> + desc->send_size = cpu_to_be16(skb->len);
> + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
> + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
> + desc->wb_addr = cpu_to_be32(phys);
Don't we need a barrier here to ensure that all stores are completed
before we hand this descriptor address to hip40_set_xmit_desc() which
should make DMA start processing it?
> + skb_tx_timestamp(skb);
> + hip04_set_xmit_desc(priv, phys);
> + priv->tx_head = TX_NEXT(tx_head);
> +
> + stats->tx_bytes += skb->len;
> + stats->tx_packets++;
You cannot update the transmit stats here, what start_xmit() does it
just queue packets for the DMA engine to process them, but that does
not mean DMA has completed those. You should update statistics in the
tx_reclaim() function.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-24 16:32 ` Florian Fainelli
@ 2014-03-24 17:23 ` Arnd Bergmann
-1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-24 17:23 UTC (permalink / raw)
To: Florian Fainelli
Cc: Zhangfei Gao, David Miller, Russell King, Sergei Shtylyov,
Mark Rutland, linux-arm-kernel, netdev, devicetree
On Monday 24 March 2014 09:32:17 Florian Fainelli wrote:
> > + priv->tx_skb[tx_head] = skb;
> > + priv->tx_phys[tx_head] = phys;
> > + desc->send_addr = cpu_to_be32(phys);
> > + desc->send_size = cpu_to_be16(skb->len);
> > + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
> > + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
> > + desc->wb_addr = cpu_to_be32(phys);
>
> Don't we need a barrier here to ensure that all stores are completed
> before we hand this descriptor address to hip40_set_xmit_desc() which
> should make DMA start processing it?
I would think the writel() in set_xmit_desc() implies the barrier.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-24 17:23 ` Arnd Bergmann
0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2014-03-24 17:23 UTC (permalink / raw)
To: linux-arm-kernel
On Monday 24 March 2014 09:32:17 Florian Fainelli wrote:
> > + priv->tx_skb[tx_head] = skb;
> > + priv->tx_phys[tx_head] = phys;
> > + desc->send_addr = cpu_to_be32(phys);
> > + desc->send_size = cpu_to_be16(skb->len);
> > + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
> > + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
> > + desc->wb_addr = cpu_to_be32(phys);
>
> Don't we need a barrier here to ensure that all stores are completed
> before we hand this descriptor address to hip40_set_xmit_desc() which
> should make DMA start processing it?
I would think the writel() in set_xmit_desc() implies the barrier.
Arnd
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-24 17:23 ` Arnd Bergmann
@ 2014-03-24 17:35 ` Florian Fainelli
-1 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-24 17:35 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Mark Rutland, devicetree, Russell King, Sergei Shtylyov, netdev,
Zhangfei Gao, David Miller, linux-arm-kernel
2014-03-24 10:23 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> On Monday 24 March 2014 09:32:17 Florian Fainelli wrote:
>> > + priv->tx_skb[tx_head] = skb;
>> > + priv->tx_phys[tx_head] = phys;
>> > + desc->send_addr = cpu_to_be32(phys);
>> > + desc->send_size = cpu_to_be16(skb->len);
>> > + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
>> > + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
>> > + desc->wb_addr = cpu_to_be32(phys);
>>
>> Don't we need a barrier here to ensure that all stores are completed
>> before we hand this descriptor address to hip40_set_xmit_desc() which
>> should make DMA start processing it?
>
> I would think the writel() in set_xmit_desc() implies the barrier.
Right, which means that this should be properly documented to make
sure that this simplification is well understood and produces the
expected result.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-24 17:35 ` Florian Fainelli
0 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-24 17:35 UTC (permalink / raw)
To: linux-arm-kernel
2014-03-24 10:23 GMT-07:00 Arnd Bergmann <arnd@arndb.de>:
> On Monday 24 March 2014 09:32:17 Florian Fainelli wrote:
>> > + priv->tx_skb[tx_head] = skb;
>> > + priv->tx_phys[tx_head] = phys;
>> > + desc->send_addr = cpu_to_be32(phys);
>> > + desc->send_size = cpu_to_be16(skb->len);
>> > + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
>> > + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
>> > + desc->wb_addr = cpu_to_be32(phys);
>>
>> Don't we need a barrier here to ensure that all stores are completed
>> before we hand this descriptor address to hip40_set_xmit_desc() which
>> should make DMA start processing it?
>
> I would think the writel() in set_xmit_desc() implies the barrier.
Right, which means that this should be properly documented to make
sure that this simplification is well understood and produces the
expected result.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
2014-03-24 16:32 ` Florian Fainelli
@ 2014-03-27 6:27 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-27 6:27 UTC (permalink / raw)
To: Florian Fainelli
Cc: Zhangfei Gao, Mark Rutland, devicetree, Russell King,
Sergei Shtylyov, Arnd Bergmann, netdev, David Miller,
linux-arm-kernel
Dear Florian
Thanks for the kind suggestion.
On Tue, Mar 25, 2014 at 12:32 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> 2014-03-24 7:14 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
>> Support Hisilicon hip04 ethernet driver, including 100M / 1000M controller
>>
>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>> ---
>> drivers/net/ethernet/hisilicon/Makefile | 2 +-
>> drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++++++++++
>> 2 files changed, 729 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
>
> [snip]
>
>> +static void hip04_config_port(struct hip04_priv *priv, u32 speed, u32 duplex)
>> +{
>> + u32 val;
>> +
>> + priv->speed = speed;
>> + priv->duplex = duplex;
>> +
>> + switch (priv->phy_mode) {
>> + case PHY_INTERFACE_MODE_SGMII:
>> + if (speed == SPEED_1000)
>> + val = 8;
>> + else
>> + val = 7;
>> + break;
>> + case PHY_INTERFACE_MODE_MII:
>> + val = 1; /* SPEED_100 */
>> + break;
>> + default:
>> + val = 0;
>> + break;
>
> Is 0 valid for e.g: 10Mbits/sec, regardless of the phy_mode?
0 is only 10M for MII mode, will add warning here.
switch (priv->phy_mode) {
case PHY_INTERFACE_MODE_SGMII:
if (speed == SPEED_1000)
val = 8;
else if (speed == SPEED_100)
val = 7;
else
val = 6; /* SPEED_10 */
break;
case PHY_INTERFACE_MODE_MII:
if (speed == SPEED_100)
val = 1;
else
val = 0; /* SPEED_10 */
break;
default:
netdev_warn(ndev, "not supported mode\n");
val = 0;
break;
}
>> +
>> +static void hip04_mac_enable(struct net_device *ndev, bool enable)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + u32 val;
>> +
>> + if (enable) {
>> + /* enable tx & rx */
>> + val = readl_relaxed(priv->base + GE_PORT_EN);
>> + val |= BIT(1); /* rx*/
>> + val |= BIT(2); /* tx*/
>> + writel_relaxed(val, priv->base + GE_PORT_EN);
>> +
>> + /* enable interrupt */
>> + priv->reg_inten = DEF_INT_MASK;
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> +
>> + /* clear rx int */
>> + val = RCV_INT;
>> + writel_relaxed(val, priv->base + PPE_RINT);
>
> Should not you first clear the interrupt and then DEF_INT_MASK? Why is
OK, got it.
> there a RCV_INT applied to PPE_RINT register in the enable path, but
> there is no such thing in the "disable" branch of your function?
This required here since setting the following cmd, /* config recv int*/
Otherwise, the setting does not take effect.
>
>> +
>> + /* config recv int*/
>> + val = BIT(6); /* int threshold 1 package */
>> + val |= 0x4; /* recv timeout */
>> + writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT);
>> + } else {
>> + /* disable int */
>> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> +
>> + /* disable tx & rx */
>> + val = readl_relaxed(priv->base + GE_PORT_EN);
>> + val &= ~(BIT(1)); /* rx*/
>> + val &= ~(BIT(2)); /* tx*/
>> + writel_relaxed(val, priv->base + GE_PORT_EN);
>> + }
>
> There is little to no sharing between the two branches, I would have
> created separate helper functions for the enable/disable logic.
OK, got it.
>
>> +}
>> +
>> +static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys)
>> +{
>> + writel(phys, priv->base + PPE_CFG_TX_PKT_BD_ADDR);
>
> This is not 64-bits/LPAE safe, do you have a High address part and a
> Low address part for your address in the buffer descriptor address, if
> so, better use it now.
Unfortunately it is true, only 32bytes for this controller on A15.
Bits [33:32] of desc can be set in [5:4], but it may be ignored,
RX register is only have 32bits too.
So the controller is only for 32 bits.
The next version can be used on 64bits, and there is high address part.
Still not get spec yet.
>> +
>> +static int hip04_rx_poll(struct napi_struct *napi, int budget)
>> +{
>> + struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
>> + struct net_device *ndev = priv->ndev;
>> + struct net_device_stats *stats = &ndev->stats;
>> + unsigned int cnt = hip04_recv_cnt(priv);
>> + struct sk_buff *skb;
>> + struct rx_desc *desc;
>> + unsigned char *buf;
>> + dma_addr_t phys;
>> + int rx = 0;
>> + u16 len;
>> + u32 err;
>> +
>> + while (cnt) {
>> + buf = priv->rx_buf[priv->rx_head];
>> + skb = build_skb(buf, priv->rx_buf_size);
>> + if (unlikely(!skb))
>> + net_dbg_ratelimited("build_skb failed\n");
>> +
>> + dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head],
>> + RX_BUF_SIZE, DMA_FROM_DEVICE);
>> + priv->rx_phys[priv->rx_head] = 0;
>> +
>> + desc = (struct rx_desc *)skb->data;
>> + len = be16_to_cpu(desc->pkt_len);
>> + err = be32_to_cpu(desc->pkt_err);
>> +
>> + if (len > RX_BUF_SIZE)
>> + len = RX_BUF_SIZE;
>> + if (0 == len)
>> + break;
>
> Should not this be a continue? This is an error packet, so you should
> keep on processing the others, or does this have a special meaning?
len=0 indicates the last packet.
Will change the behavior here.
if (0 == len) {
dev_kfree_skb_any(skb);
last = true;
} else if (err & RX_PKT_ERR) {
dev_kfree_skb_any(skb);
stats->rx_dropped++;
stats->rx_errors++;
} else {
skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, ndev);
napi_gro_receive(&priv->napi, skb);
stats->rx_packets++;
stats->rx_bytes += len;
}
>
>> +
>> + if (err & RX_PKT_ERR) {
>> + dev_kfree_skb_any(skb);
>> + stats->rx_dropped++;
>> + stats->rx_errors++;
>> + continue;
>> + }
>> +
>> + stats->rx_packets++;
>> + stats->rx_bytes += len;
>> +
>> + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
>> + skb_put(skb, len);
>> + skb->protocol = eth_type_trans(skb, ndev);
>> + napi_gro_receive(&priv->napi, skb);
>> +
>> + buf = netdev_alloc_frag(priv->rx_buf_size);
>> + if (!buf)
>> + return -ENOMEM;
>> + phys = dma_map_single(&ndev->dev, buf,
>> + RX_BUF_SIZE, DMA_FROM_DEVICE);
>
> Missing dma_mapping_error() check here.
Yes, thanks
>
>> + priv->rx_buf[priv->rx_head] = buf;
>> + priv->rx_phys[priv->rx_head] = phys;
>> + hip04_set_recv_desc(priv, phys);
>> +
>> + priv->rx_head = RX_NEXT(priv->rx_head);
>> + if (rx++ >= budget)
>> + break;
>> +
>> + if (--cnt == 0)
>> + cnt = hip04_recv_cnt(priv);
>
>> + }
>> +
>> + if (rx < budget) {
>> + napi_complete(napi);
>> +
>> + /* enable rx interrupt */
>> + priv->reg_inten |= RCV_INT | RCV_NOBUF;
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> + }
>> +
>> + return rx;
>> +}
>> +
>> +static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id)
>> +{
>> + struct net_device *ndev = (struct net_device *) dev_id;
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + u32 ists = readl_relaxed(priv->base + PPE_INTSTS);
>> + u32 val = DEF_INT_MASK;
>> +
>> + writel_relaxed(val, priv->base + PPE_RINT);
>> +
>> + if (ists & (RCV_INT | RCV_NOBUF)) {
>> + if (napi_schedule_prep(&priv->napi)) {
>> + /* disable rx interrupt */
>> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> + __napi_schedule(&priv->napi);
>> + }
>> + }
>
> You should also process TX completion interrupts here
There is no such interrupt.
>
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + unsigned tx_head = priv->tx_head;
>> + unsigned tx_tail = priv->tx_tail;
>> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> +
>> + while (tx_tail != tx_head) {
>> + if (desc->send_addr != 0) {
>> + if (force)
>> + desc->send_addr = 0;
>> + else
>> + break;
>> + }
>> + if (priv->tx_phys[tx_tail]) {
>> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> + priv->tx_phys[tx_tail] = 0;
>> + }
>> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>
> dev_kfree_skb_irq() bypasses all sort of SKB tracking, you might want
> to use kfree_skb() here instead.
OK, will use dev_kfree_skb instead.
>
>> + priv->tx_skb[tx_tail] = NULL;
>> + tx_tail = TX_NEXT(tx_tail);
>> + priv->tx_count--;
>> + }
>> + priv->tx_tail = tx_tail;
>> +}
>> +
>> +static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + struct net_device_stats *stats = &ndev->stats;
>> + unsigned int tx_head = priv->tx_head;
>> + struct tx_desc *desc = &priv->tx_desc[tx_head];
>> + dma_addr_t phys;
>> +
>> + hip04_tx_reclaim(ndev, false);
>> +
>> + if (priv->tx_count++ >= TX_DESC_NUM) {
>> + net_dbg_ratelimited("no TX space for packet\n");
>> + netif_stop_queue(ndev);
>> + return NETDEV_TX_BUSY;
>> + }
>> +
>> + phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE);
>
> Missing dma_mapping_error() check here
>
>> + priv->tx_skb[tx_head] = skb;
>> + priv->tx_phys[tx_head] = phys;
>> + desc->send_addr = cpu_to_be32(phys);
>> + desc->send_size = cpu_to_be16(skb->len);
>> + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
>> + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
>> + desc->wb_addr = cpu_to_be32(phys);
>
> Don't we need a barrier here to ensure that all stores are completed
> before we hand this descriptor address to hip40_set_xmit_desc() which
> should make DMA start processing it?
>
>> + skb_tx_timestamp(skb);
>> + hip04_set_xmit_desc(priv, phys);
>> + priv->tx_head = TX_NEXT(tx_head);
>> +
>> + stats->tx_bytes += skb->len;
>> + stats->tx_packets++;
>
> You cannot update the transmit stats here, what start_xmit() does it
> just queue packets for the DMA engine to process them, but that does
> not mean DMA has completed those. You should update statistics in the
> tx_reclaim() function.
Yes, however, since no TX complete interrupt, tx_reclaim may be called
rather late, it may be more suitable to put here.
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 3/3] net: hisilicon: new hip04 ethernet driver
@ 2014-03-27 6:27 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-27 6:27 UTC (permalink / raw)
To: linux-arm-kernel
Dear Florian
Thanks for the kind suggestion.
On Tue, Mar 25, 2014 at 12:32 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> 2014-03-24 7:14 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
>> Support Hisilicon hip04 ethernet driver, including 100M / 1000M controller
>>
>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>> ---
>> drivers/net/ethernet/hisilicon/Makefile | 2 +-
>> drivers/net/ethernet/hisilicon/hip04_eth.c | 728 ++++++++++++++++++++++++++++
>> 2 files changed, 729 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
>
> [snip]
>
>> +static void hip04_config_port(struct hip04_priv *priv, u32 speed, u32 duplex)
>> +{
>> + u32 val;
>> +
>> + priv->speed = speed;
>> + priv->duplex = duplex;
>> +
>> + switch (priv->phy_mode) {
>> + case PHY_INTERFACE_MODE_SGMII:
>> + if (speed == SPEED_1000)
>> + val = 8;
>> + else
>> + val = 7;
>> + break;
>> + case PHY_INTERFACE_MODE_MII:
>> + val = 1; /* SPEED_100 */
>> + break;
>> + default:
>> + val = 0;
>> + break;
>
> Is 0 valid for e.g: 10Mbits/sec, regardless of the phy_mode?
0 is only 10M for MII mode, will add warning here.
switch (priv->phy_mode) {
case PHY_INTERFACE_MODE_SGMII:
if (speed == SPEED_1000)
val = 8;
else if (speed == SPEED_100)
val = 7;
else
val = 6; /* SPEED_10 */
break;
case PHY_INTERFACE_MODE_MII:
if (speed == SPEED_100)
val = 1;
else
val = 0; /* SPEED_10 */
break;
default:
netdev_warn(ndev, "not supported mode\n");
val = 0;
break;
}
>> +
>> +static void hip04_mac_enable(struct net_device *ndev, bool enable)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + u32 val;
>> +
>> + if (enable) {
>> + /* enable tx & rx */
>> + val = readl_relaxed(priv->base + GE_PORT_EN);
>> + val |= BIT(1); /* rx*/
>> + val |= BIT(2); /* tx*/
>> + writel_relaxed(val, priv->base + GE_PORT_EN);
>> +
>> + /* enable interrupt */
>> + priv->reg_inten = DEF_INT_MASK;
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> +
>> + /* clear rx int */
>> + val = RCV_INT;
>> + writel_relaxed(val, priv->base + PPE_RINT);
>
> Should not you first clear the interrupt and then DEF_INT_MASK? Why is
OK, got it.
> there a RCV_INT applied to PPE_RINT register in the enable path, but
> there is no such thing in the "disable" branch of your function?
This required here since setting the following cmd, /* config recv int*/
Otherwise, the setting does not take effect.
>
>> +
>> + /* config recv int*/
>> + val = BIT(6); /* int threshold 1 package */
>> + val |= 0x4; /* recv timeout */
>> + writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT);
>> + } else {
>> + /* disable int */
>> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> +
>> + /* disable tx & rx */
>> + val = readl_relaxed(priv->base + GE_PORT_EN);
>> + val &= ~(BIT(1)); /* rx*/
>> + val &= ~(BIT(2)); /* tx*/
>> + writel_relaxed(val, priv->base + GE_PORT_EN);
>> + }
>
> There is little to no sharing between the two branches, I would have
> created separate helper functions for the enable/disable logic.
OK, got it.
>
>> +}
>> +
>> +static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys)
>> +{
>> + writel(phys, priv->base + PPE_CFG_TX_PKT_BD_ADDR);
>
> This is not 64-bits/LPAE safe, do you have a High address part and a
> Low address part for your address in the buffer descriptor address, if
> so, better use it now.
Unfortunately it is true, only 32bytes for this controller on A15.
Bits [33:32] of desc can be set in [5:4], but it may be ignored,
RX register is only have 32bits too.
So the controller is only for 32 bits.
The next version can be used on 64bits, and there is high address part.
Still not get spec yet.
>> +
>> +static int hip04_rx_poll(struct napi_struct *napi, int budget)
>> +{
>> + struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
>> + struct net_device *ndev = priv->ndev;
>> + struct net_device_stats *stats = &ndev->stats;
>> + unsigned int cnt = hip04_recv_cnt(priv);
>> + struct sk_buff *skb;
>> + struct rx_desc *desc;
>> + unsigned char *buf;
>> + dma_addr_t phys;
>> + int rx = 0;
>> + u16 len;
>> + u32 err;
>> +
>> + while (cnt) {
>> + buf = priv->rx_buf[priv->rx_head];
>> + skb = build_skb(buf, priv->rx_buf_size);
>> + if (unlikely(!skb))
>> + net_dbg_ratelimited("build_skb failed\n");
>> +
>> + dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head],
>> + RX_BUF_SIZE, DMA_FROM_DEVICE);
>> + priv->rx_phys[priv->rx_head] = 0;
>> +
>> + desc = (struct rx_desc *)skb->data;
>> + len = be16_to_cpu(desc->pkt_len);
>> + err = be32_to_cpu(desc->pkt_err);
>> +
>> + if (len > RX_BUF_SIZE)
>> + len = RX_BUF_SIZE;
>> + if (0 == len)
>> + break;
>
> Should not this be a continue? This is an error packet, so you should
> keep on processing the others, or does this have a special meaning?
len=0 indicates the last packet.
Will change the behavior here.
if (0 == len) {
dev_kfree_skb_any(skb);
last = true;
} else if (err & RX_PKT_ERR) {
dev_kfree_skb_any(skb);
stats->rx_dropped++;
stats->rx_errors++;
} else {
skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, ndev);
napi_gro_receive(&priv->napi, skb);
stats->rx_packets++;
stats->rx_bytes += len;
}
>
>> +
>> + if (err & RX_PKT_ERR) {
>> + dev_kfree_skb_any(skb);
>> + stats->rx_dropped++;
>> + stats->rx_errors++;
>> + continue;
>> + }
>> +
>> + stats->rx_packets++;
>> + stats->rx_bytes += len;
>> +
>> + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
>> + skb_put(skb, len);
>> + skb->protocol = eth_type_trans(skb, ndev);
>> + napi_gro_receive(&priv->napi, skb);
>> +
>> + buf = netdev_alloc_frag(priv->rx_buf_size);
>> + if (!buf)
>> + return -ENOMEM;
>> + phys = dma_map_single(&ndev->dev, buf,
>> + RX_BUF_SIZE, DMA_FROM_DEVICE);
>
> Missing dma_mapping_error() check here.
Yes, thanks
>
>> + priv->rx_buf[priv->rx_head] = buf;
>> + priv->rx_phys[priv->rx_head] = phys;
>> + hip04_set_recv_desc(priv, phys);
>> +
>> + priv->rx_head = RX_NEXT(priv->rx_head);
>> + if (rx++ >= budget)
>> + break;
>> +
>> + if (--cnt == 0)
>> + cnt = hip04_recv_cnt(priv);
>
>> + }
>> +
>> + if (rx < budget) {
>> + napi_complete(napi);
>> +
>> + /* enable rx interrupt */
>> + priv->reg_inten |= RCV_INT | RCV_NOBUF;
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> + }
>> +
>> + return rx;
>> +}
>> +
>> +static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id)
>> +{
>> + struct net_device *ndev = (struct net_device *) dev_id;
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + u32 ists = readl_relaxed(priv->base + PPE_INTSTS);
>> + u32 val = DEF_INT_MASK;
>> +
>> + writel_relaxed(val, priv->base + PPE_RINT);
>> +
>> + if (ists & (RCV_INT | RCV_NOBUF)) {
>> + if (napi_schedule_prep(&priv->napi)) {
>> + /* disable rx interrupt */
>> + priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
>> + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
>> + __napi_schedule(&priv->napi);
>> + }
>> + }
>
> You should also process TX completion interrupts here
There is no such interrupt.
>
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void hip04_tx_reclaim(struct net_device *ndev, bool force)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + unsigned tx_head = priv->tx_head;
>> + unsigned tx_tail = priv->tx_tail;
>> + struct tx_desc *desc = &priv->tx_desc[priv->tx_tail];
>> +
>> + while (tx_tail != tx_head) {
>> + if (desc->send_addr != 0) {
>> + if (force)
>> + desc->send_addr = 0;
>> + else
>> + break;
>> + }
>> + if (priv->tx_phys[tx_tail]) {
>> + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
>> + priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
>> + priv->tx_phys[tx_tail] = 0;
>> + }
>> + dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
>
> dev_kfree_skb_irq() bypasses all sort of SKB tracking, you might want
> to use kfree_skb() here instead.
OK, will use dev_kfree_skb instead.
>
>> + priv->tx_skb[tx_tail] = NULL;
>> + tx_tail = TX_NEXT(tx_tail);
>> + priv->tx_count--;
>> + }
>> + priv->tx_tail = tx_tail;
>> +}
>> +
>> +static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
>> +{
>> + struct hip04_priv *priv = netdev_priv(ndev);
>> + struct net_device_stats *stats = &ndev->stats;
>> + unsigned int tx_head = priv->tx_head;
>> + struct tx_desc *desc = &priv->tx_desc[tx_head];
>> + dma_addr_t phys;
>> +
>> + hip04_tx_reclaim(ndev, false);
>> +
>> + if (priv->tx_count++ >= TX_DESC_NUM) {
>> + net_dbg_ratelimited("no TX space for packet\n");
>> + netif_stop_queue(ndev);
>> + return NETDEV_TX_BUSY;
>> + }
>> +
>> + phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE);
>
> Missing dma_mapping_error() check here
>
>> + priv->tx_skb[tx_head] = skb;
>> + priv->tx_phys[tx_head] = phys;
>> + desc->send_addr = cpu_to_be32(phys);
>> + desc->send_size = cpu_to_be16(skb->len);
>> + desc->cfg = cpu_to_be32(DESC_DEF_CFG);
>> + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
>> + desc->wb_addr = cpu_to_be32(phys);
>
> Don't we need a barrier here to ensure that all stores are completed
> before we hand this descriptor address to hip40_set_xmit_desc() which
> should make DMA start processing it?
>
>> + skb_tx_timestamp(skb);
>> + hip04_set_xmit_desc(priv, phys);
>> + priv->tx_head = TX_NEXT(tx_head);
>> +
>> + stats->tx_bytes += skb->len;
>> + stats->tx_packets++;
>
> You cannot update the transmit stats here, what start_xmit() does it
> just queue packets for the DMA engine to process them, but that does
> not mean DMA has completed those. You should update statistics in the
> tx_reclaim() function.
Yes, however, since no TX complete interrupt, tx_reclaim may be called
rather late, it may be more suitable to put here.
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH v7 0/3] add hisilicon hip04 ethernet driver
@ 2014-04-05 4:35 Zhangfei Gao
2014-04-05 4:35 ` Zhangfei Gao
0 siblings, 1 reply; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-05 4:35 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
v7:
Remove select NET_CORE in 0002
v6:
Suggest by Russell: Use netdev_sent_queue & netdev_completed_queue to solve latency issue
Also shorten the period of timer, which is used to wakeup the queue since no
tx completed interrupt.
v5:
no big change, fix typo
v4:
Modify accoringly to the suggetion from Arnd, Florian, Eric, David
Use of_parse_phandle_with_fixed_args & syscon_node_to_regmap get ppe info
Add skb_orphan() and tx_timer for reclaim since no tx_finished interrupt
Update timeout, and move of_phy_connect to probe to reuse open/stop
v3:
Suggest from Arnd, use syscon & regmap_write/read to replace static void __iomem *ppebase.
Modify hisilicon-hip04-net.txt accrordingly to suggestion from Florian and Sergei.
v2:
Got many suggestions from Russell, Arnd, Florian, Mark and Sergei
Remove memcpy, use dma_map/unmap_single, use dma_alloc_coherent rather than dma_pool, etc.
Refer property in ethernet.txt, change ppe description, etc.
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 88 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 777 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++
7 files changed, 1089 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-04-05 4:35 [PATCH v7 0/3] add hisilicon " Zhangfei Gao
@ 2014-04-05 4:35 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-05 4:35 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 224 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..628537f
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Hisilicon devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-04-05 4:35 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-05 4:35 UTC (permalink / raw)
To: linux-arm-kernel
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 224 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..628537f
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Hisilicon devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-04-05 4:35 ` Zhangfei Gao
@ 2014-04-07 18:47 ` David Miller
-1 siblings, 0 replies; 78+ messages in thread
From: David Miller @ 2014-04-07 18:47 UTC (permalink / raw)
To: zhangfei.gao
Cc: linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet, linux-arm-kernel, netdev, devicetree
From: Zhangfei Gao <zhangfei.gao@linaro.org>
Date: Sat, 5 Apr 2014 12:35:05 +0800
> +}
> +
> +
> +static int hip04_mdio_reset(struct mii_bus *bus)
Please one empty line between functions, not two.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH v6 0/3] add hisilicon hip04 ethernet driver
@ 2014-04-04 15:16 Zhangfei Gao
2014-04-04 15:16 ` Zhangfei Gao
0 siblings, 1 reply; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-04 15:16 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
v6:
Suggest by Russell: Use netdev_sent_queue & netdev_completed_queue to solve latency issue
Also shorten the period of timer, which is used to wakeup the queue since no
tx completed interrupt.
v5:
no big change, fix typo
v4:
Modify accoringly to the suggetion from Arnd, Florian, Eric, David
Use of_parse_phandle_with_fixed_args & syscon_node_to_regmap get ppe info
Add skb_orphan() and tx_timer for reclaim since no tx_finished interrupt
Update timeout, and move of_phy_connect to probe to reuse open/stop
v3:
Suggest from Arnd, use syscon & regmap_write/read to replace static void __iomem *ppebase.
Modify hisilicon-hip04-net.txt accrordingly to suggestion from Florian and Sergei.
v2:
Got many suggestions from Russell, Arnd, Florian, Mark and Sergei
Remove memcpy, use dma_map/unmap_single, use dma_alloc_coherent rather than dma_pool, etc.
Refer property in ethernet.txt, change ppe description, etc.
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 88 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 777 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++
7 files changed, 1090 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-04-04 15:16 [PATCH v6 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
@ 2014-04-04 15:16 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-04 15:16 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 225 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..39b5c7e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,32 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Hisilicon devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-04-04 15:16 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-04 15:16 UTC (permalink / raw)
To: linux-arm-kernel
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 225 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..39b5c7e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,32 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Hisilicon devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-04-04 15:16 ` Zhangfei Gao
@ 2014-04-04 15:30 ` David Miller
-1 siblings, 0 replies; 78+ messages in thread
From: David Miller @ 2014-04-04 15:30 UTC (permalink / raw)
To: zhangfei.gao
Cc: linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet, linux-arm-kernel, netdev, devicetree
From: Zhangfei Gao <zhangfei.gao@linaro.org>
Date: Fri, 4 Apr 2014 23:16:36 +0800
> +config HIP04_ETH
> + tristate "HISILICON P04 Ethernet support"
> + select NET_CORE
> + select PHYLIB
> + select MARVELL_PHY
> + select MFD_SYSCON
> + ---help---
> + If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
> + want to use the internal ethernet then you should answer Y to this.
Selecting NET_CORE makes no sense, if it's not set this driver's
Kconfig file won't even be considered.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-04-04 15:30 ` David Miller
0 siblings, 0 replies; 78+ messages in thread
From: David Miller @ 2014-04-04 15:30 UTC (permalink / raw)
To: linux-arm-kernel
From: Zhangfei Gao <zhangfei.gao@linaro.org>
Date: Fri, 4 Apr 2014 23:16:36 +0800
> +config HIP04_ETH
> + tristate "HISILICON P04 Ethernet support"
> + select NET_CORE
> + select PHYLIB
> + select MARVELL_PHY
> + select MFD_SYSCON
> + ---help---
> + If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
> + want to use the internal ethernet then you should answer Y to this.
Selecting NET_CORE makes no sense, if it's not set this driver's
Kconfig file won't even be considered.
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-04-04 15:30 ` David Miller
@ 2014-04-04 15:40 ` zhangfei
-1 siblings, 0 replies; 78+ messages in thread
From: zhangfei @ 2014-04-04 15:40 UTC (permalink / raw)
To: David Miller
Cc: linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet, linux-arm-kernel, netdev, devicetree
On 04/04/2014 11:30 PM, David Miller wrote:
> From: Zhangfei Gao <zhangfei.gao@linaro.org>
> Date: Fri, 4 Apr 2014 23:16:36 +0800
>
>> +config HIP04_ETH
>> + tristate "HISILICON P04 Ethernet support"
>> + select NET_CORE
>> + select PHYLIB
>> + select MARVELL_PHY
>> + select MFD_SYSCON
>> + ---help---
>> + If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
>> + want to use the internal ethernet then you should answer Y to this.
>
> Selecting NET_CORE makes no sense, if it's not set this driver's
> Kconfig file won't even be considered.
>
Thanks Daivd, could I update following this thread first.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-04-04 15:40 ` zhangfei
0 siblings, 0 replies; 78+ messages in thread
From: zhangfei @ 2014-04-04 15:40 UTC (permalink / raw)
To: linux-arm-kernel
On 04/04/2014 11:30 PM, David Miller wrote:
> From: Zhangfei Gao <zhangfei.gao@linaro.org>
> Date: Fri, 4 Apr 2014 23:16:36 +0800
>
>> +config HIP04_ETH
>> + tristate "HISILICON P04 Ethernet support"
>> + select NET_CORE
>> + select PHYLIB
>> + select MARVELL_PHY
>> + select MFD_SYSCON
>> + ---help---
>> + If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
>> + want to use the internal ethernet then you should answer Y to this.
>
> Selecting NET_CORE makes no sense, if it's not set this driver's
> Kconfig file won't even be considered.
>
Thanks Daivd, could I update following this thread first.
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-04-04 15:40 ` zhangfei
@ 2014-04-04 15:43 ` David Miller
-1 siblings, 0 replies; 78+ messages in thread
From: David Miller @ 2014-04-04 15:43 UTC (permalink / raw)
To: zhangfei.gao
Cc: linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet, linux-arm-kernel, netdev, devicetree
From: zhangfei <zhangfei.gao@linaro.org>
Date: Fri, 04 Apr 2014 23:40:24 +0800
> Thanks Daivd, could I update following this thread first.
You will need to address my feedback, as well as that which anyone
else reviewing this series gives to you.
It is not acceptable, ever, to "just fix it later". You must fix
all issues pointed out to you before your code will be integrated.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-04-04 15:43 ` David Miller
0 siblings, 0 replies; 78+ messages in thread
From: David Miller @ 2014-04-04 15:43 UTC (permalink / raw)
To: linux-arm-kernel
From: zhangfei <zhangfei.gao@linaro.org>
Date: Fri, 04 Apr 2014 23:40:24 +0800
> Thanks Daivd, could I update following this thread first.
You will need to address my feedback, as well as that which anyone
else reviewing this series gives to you.
It is not acceptable, ever, to "just fix it later". You must fix
all issues pointed out to you before your code will be integrated.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-04-04 15:16 ` Zhangfei Gao
@ 2014-04-04 15:45 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-04 15:45 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 224 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..628537f
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Hisilicon devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-04-04 15:45 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-04 15:45 UTC (permalink / raw)
To: linux-arm-kernel
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 224 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..628537f
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Hisilicon devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH v5 0/3] add hisilicon hip04 ethernet driver
@ 2014-04-01 13:27 Zhangfei Gao
[not found] ` <1396358832-15828-1-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
0 siblings, 1 reply; 78+ messages in thread
From: Zhangfei Gao @ 2014-04-01 13:27 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
v5:
no big change, fix typo
v4:
Modify accoringly to the suggetion from Arnd, Florian, Eric, David
Use of_parse_phandle_with_fixed_args & syscon_node_to_regmap get ppe info
Add skb_orphan() and tx_timer for reclaim since no tx_finished interrupt
Update timeout, and move of_phy_connect to probe to reuse open/stop
v3:
Suggest from Arnd, use syscon & regmap_write/read to replace static void __iomem *ppebase.
Modify hisilicon-hip04-net.txt accrordingly to suggestion from Florian and Sergei.
v2:
Got many suggestions from Russell, Arnd, Florian, Mark and Sergei
Remove memcpy, use dma_map/unmap_single, use dma_alloc_coherent rather than dma_pool, etc.
Refer property in ethernet.txt, change ppe description, etc.
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 88 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 771 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++
7 files changed, 1084 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH v4 0/3] add hisilicon hip04 ethernet driver
@ 2014-03-28 15:35 Zhangfei Gao
2014-03-28 15:35 ` Zhangfei Gao
0 siblings, 1 reply; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-28 15:35 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
v4:
Modify accoringly to the suggetion from Arnd, Florian, Eric, David
Use of_parse_phandle_with_fixed_args & syscon_node_to_regmap get ppe info
Add skb_orphan() and tx_timer for reclaim since no tx_finished interrupt
Update timeout, and move of_phy_connect to probe to reuse open/stop
v3:
Suggest from Arnd, use syscon & regmap_write/read to replace static void __iomem *ppebase.
Modify hisilicon-hip04-net.txt accrordingly to suggestion from Florian and Sergei.
v2:
Got many suggestions from Russell, Arnd, Florian, Mark and Sergei
Remove memcpy, use dma_map/unmap_single, use dma_alloc_coherent rather than dma_pool, etc.
Refer property in ethernet.txt, change ppe description, etc.
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 88 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 764 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++
7 files changed, 1077 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-28 15:35 [PATCH v4 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
@ 2014-03-28 15:35 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-28 15:35 UTC (permalink / raw)
To: davem, linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 225 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..b0ee739
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,32 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-28 15:35 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-28 15:35 UTC (permalink / raw)
To: linux-arm-kernel
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 32 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 225 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..b0ee739
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,32 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
[parent not found: <1396020960-6170-3-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>]
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-28 15:35 ` Zhangfei Gao
@ 2014-03-30 21:23 ` David Miller
-1 siblings, 0 replies; 78+ messages in thread
From: David Miller @ 2014-03-30 21:23 UTC (permalink / raw)
To: zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A
Cc: linux-lFZ/pmaqli7XmaaqVzeoHQ, arnd-r2nGTMty4D4,
f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8,
mark.rutland-5wv7dgnIgG8, David.Laight-ZS65k/vG3HxXrIkS9f7CXA,
eric.dumazet-Re5JQEeQqe8AvxtiuMwx3w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA
From: Zhangfei Gao <zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Date: Fri, 28 Mar 2014 23:35:59 +0800
> + Note that the answer to this question doesn't directly affect the
> + kernel: saying N will just cause the configurator to skip all
> + the questions about MOXA ART devices. If you say Y, you will be asked
> + for your specific card in the following questions.
Please don't just cut and paste things from unrelated Kconfig entries.
This Kconfig option has nothing to do with MOXA ART devices.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-30 21:23 ` David Miller
0 siblings, 0 replies; 78+ messages in thread
From: David Miller @ 2014-03-30 21:23 UTC (permalink / raw)
To: linux-arm-kernel
From: Zhangfei Gao <zhangfei.gao@linaro.org>
Date: Fri, 28 Mar 2014 23:35:59 +0800
> + Note that the answer to this question doesn't directly affect the
> + kernel: saying N will just cause the configurator to skip all
> + the questions about MOXA ART devices. If you say Y, you will be asked
> + for your specific card in the following questions.
Please don't just cut and paste things from unrelated Kconfig entries.
This Kconfig option has nothing to do with MOXA ART devices.
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-30 21:23 ` David Miller
@ 2014-03-31 2:45 ` zhangfei
-1 siblings, 0 replies; 78+ messages in thread
From: zhangfei @ 2014-03-31 2:45 UTC (permalink / raw)
To: David Miller
Cc: linux, arnd, f.fainelli, sergei.shtylyov, mark.rutland,
David.Laight, eric.dumazet, linux-arm-kernel, netdev, devicetree
On 03/31/2014 05:23 AM, David Miller wrote:
> From: Zhangfei Gao <zhangfei.gao@linaro.org>
> Date: Fri, 28 Mar 2014 23:35:59 +0800
>
>> + Note that the answer to this question doesn't directly affect the
>> + kernel: saying N will just cause the configurator to skip all
>> + the questions about MOXA ART devices. If you say Y, you will be asked
>> + for your specific card in the following questions.
>
> Please don't just cut and paste things from unrelated Kconfig entries.
>
> This Kconfig option has nothing to do with MOXA ART devices.
>
My bad, will update in new version.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-31 2:45 ` zhangfei
0 siblings, 0 replies; 78+ messages in thread
From: zhangfei @ 2014-03-31 2:45 UTC (permalink / raw)
To: linux-arm-kernel
On 03/31/2014 05:23 AM, David Miller wrote:
> From: Zhangfei Gao <zhangfei.gao@linaro.org>
> Date: Fri, 28 Mar 2014 23:35:59 +0800
>
>> + Note that the answer to this question doesn't directly affect the
>> + kernel: saying N will just cause the configurator to skip all
>> + the questions about MOXA ART devices. If you say Y, you will be asked
>> + for your specific card in the following questions.
>
> Please don't just cut and paste things from unrelated Kconfig entries.
>
> This Kconfig option has nothing to do with MOXA ART devices.
>
My bad, will update in new version.
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH v2 0/3] add hisilicon hip04 ethernet driver
@ 2014-03-21 15:09 Zhangfei Gao
2014-03-21 15:09 ` Zhangfei Gao
0 siblings, 1 reply; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-21 15:09 UTC (permalink / raw)
To: David S. Miller, linux-lFZ/pmaqli7XmaaqVzeoHQ, arnd-r2nGTMty4D4,
f.fainelli-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
Zhangfei Gao
V2:
Got many suggestions from Russell, Arnd, Florian, Mark and Sergei
Remove memcpy, use dma_map/unmap_single, use dma_alloc_coherent rather than dma_pool, etc.
Refer property in ethernet.txt, change ppe description, etc.
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 107 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 730 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 190 +++++
7 files changed, 1065 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-21 15:09 [PATCH v2 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
@ 2014-03-21 15:09 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-21 15:09 UTC (permalink / raw)
To: David S. Miller, linux, arnd, f.fainelli, mark.rutland, sergei.shtylyov
Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 224 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..4b1c065
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-21 15:09 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-21 15:09 UTC (permalink / raw)
To: linux-arm-kernel
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 +++++++++++++++++++++++++++
5 files changed, 224 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b024..1c8dc3d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -54,6 +54,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789..da1a435 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..4b1c065
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..e4f67ca
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 0/3] add hisilicon hip04 ethernet driver
@ 2014-03-18 8:40 Zhangfei Gao
2014-03-18 8:40 ` Zhangfei Gao
0 siblings, 1 reply; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-18 8:40 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Zhangfei Gao (3):
Documentation: add Device tree bindings for Hisilicon hip04 ethernet
net: hisilicon: new hip04 MDIO driver
net: hisilicon: new hip04 ethernet driver
.../bindings/net/hisilicon-hip04-net.txt | 74 ++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_eth.c | 717 ++++++++++++++++++++
drivers/net/ethernet/hisilicon/hip04_mdio.c | 190 ++++++
7 files changed, 1019 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_eth.c
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-18 8:40 [PATCH 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
@ 2014-03-18 8:40 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-18 8:40 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-arm-kernel, netdev, devicetree, Zhangfei Gao
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 190 +++++++++++++++++++++++++++
5 files changed, 228 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 39484b5..cef103d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -55,6 +55,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index adf61af..f70b166 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..4b1c065
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..960adc2
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,190 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < 2; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "No SMI register address given\n");
+ return -ENODEV;
+ }
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
+ dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+ priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+ if (!priv->base) {
+ dev_err(&pdev->dev, "Unable to remap SMI register\n");
+ ret = -ENODEV;
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-18 8:40 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-18 8:40 UTC (permalink / raw)
To: linux-arm-kernel
Hisilicon hip04 platform mdio driver
Reuse Marvell phy drivers/net/phy/marvell.c
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
drivers/net/ethernet/hisilicon/Makefile | 5 +
drivers/net/ethernet/hisilicon/hip04_mdio.c | 190 +++++++++++++++++++++++++++
5 files changed, 228 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
create mode 100644 drivers/net/ethernet/hisilicon/Makefile
create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 39484b5..cef103d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -55,6 +55,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index adf61af..f70b166 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644
index 0000000..4b1c065
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -0,0 +1,31 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+ bool "Hisilicon devices"
+ default y
+ depends on ARM
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select NET_CORE
+ select PHYLIB
+ select MARVELL_PHY
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644
index 0000000..1d6eb6e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 0000000..960adc2
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,190 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < 2; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "No SMI register address given\n");
+ return -ENODEV;
+ }
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
+ dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+ priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+ if (!priv->base) {
+ dev_err(&pdev->dev, "Unable to remap SMI register\n");
+ ret = -ENODEV;
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 78+ messages in thread
[parent not found: <1395132017-15928-3-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>]
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-18 8:40 ` Zhangfei Gao
@ 2014-03-18 17:28 ` Florian Fainelli
-1 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-18 17:28 UTC (permalink / raw)
To: Zhangfei Gao
Cc: David S. Miller, netdev,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
2014-03-18 1:40 GMT-07:00 Zhangfei Gao <zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>:
> Hisilicon hip04 platform mdio driver
> Reuse Marvell phy drivers/net/phy/marvell.c
>
> Signed-off-by: Zhangfei Gao <zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> drivers/net/ethernet/Kconfig | 1 +
> drivers/net/ethernet/Makefile | 1 +
> drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
> drivers/net/ethernet/hisilicon/Makefile | 5 +
> drivers/net/ethernet/hisilicon/hip04_mdio.c | 190 +++++++++++++++++++++++++++
> 5 files changed, 228 insertions(+)
> create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
> create mode 100644 drivers/net/ethernet/hisilicon/Makefile
> create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
>
> diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
> index 39484b5..cef103d 100644
> --- a/drivers/net/ethernet/Kconfig
> +++ b/drivers/net/ethernet/Kconfig
> @@ -55,6 +55,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
> source "drivers/net/ethernet/faraday/Kconfig"
> source "drivers/net/ethernet/freescale/Kconfig"
> source "drivers/net/ethernet/fujitsu/Kconfig"
> +source "drivers/net/ethernet/hisilicon/Kconfig"
> source "drivers/net/ethernet/hp/Kconfig"
> source "drivers/net/ethernet/ibm/Kconfig"
> source "drivers/net/ethernet/intel/Kconfig"
> diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
> index adf61af..f70b166 100644
> --- a/drivers/net/ethernet/Makefile
> +++ b/drivers/net/ethernet/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
> obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
> obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
> obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
> +obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
> obj-$(CONFIG_NET_VENDOR_HP) += hp/
> obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
> obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
> diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
> new file mode 100644
> index 0000000..4b1c065
> --- /dev/null
> +++ b/drivers/net/ethernet/hisilicon/Kconfig
> @@ -0,0 +1,31 @@
> +#
> +# HISILICON device configuration
> +#
> +
> +config NET_VENDOR_HISILICON
> + bool "Hisilicon devices"
> + default y
> + depends on ARM
> + ---help---
> + If you have a network (Ethernet) card belonging to this class, say Y
> + and read the Ethernet-HOWTO, available from
> + <http://www.tldp.org/docs.html#howto>.
> +
> + Note that the answer to this question doesn't directly affect the
> + kernel: saying N will just cause the configurator to skip all
> + the questions about MOXA ART devices. If you say Y, you will be asked
> + for your specific card in the following questions.
> +
> +if NET_VENDOR_HISILICON
> +
> +config HIP04_ETH
> + tristate "HISILICON P04 Ethernet support"
> + select NET_CORE
> + select PHYLIB
> + select MARVELL_PHY
> + ---help---
> + If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
> + want to use the internal ethernet then you should answer Y to this.
> +
> +
> +endif # NET_VENDOR_HISILICON
> diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
> new file mode 100644
> index 0000000..1d6eb6e
> --- /dev/null
> +++ b/drivers/net/ethernet/hisilicon/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for the HISILICON network device drivers.
> +#
> +
> +obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
> diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
> new file mode 100644
> index 0000000..960adc2
> --- /dev/null
> +++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
> @@ -0,0 +1,190 @@
> +
> +/* Copyright (c) 2014 Linaro Ltd.
> + * Copyright (c) 2014 Hisilicon Limited.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/of_mdio.h>
> +#include <linux/delay.h>
> +
> +#define MDIO_CMD_REG 0x0
> +#define MDIO_ADDR_REG 0x4
> +#define MDIO_WDATA_REG 0x8
> +#define MDIO_RDATA_REG 0xc
> +#define MDIO_STA_REG 0x10
> +
> +#define MDIO_START BIT(14)
> +#define MDIO_R_VALID BIT(1)
> +#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
> +#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
> +
> +struct hip04_mdio_priv {
> + void __iomem *base;
> +};
> +
> +#define WAIT_TIMEOUT 10
> +static int hip04_mdio_wait_ready(struct mii_bus *bus)
> +{
> + struct hip04_mdio_priv *priv = bus->priv;
> + int i;
> +
> + for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
> + if (i == WAIT_TIMEOUT)
> + return -ETIMEDOUT;
> + msleep(20);
> + }
> +
> + return 0;
> +}
> +
> +static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
> +{
> + struct hip04_mdio_priv *priv = bus->priv;
> + u32 val;
> + int ret;
> +
> + ret = hip04_mdio_wait_ready(bus);
> + if (ret < 0)
> + goto out;
> +
> + val = regnum | (mii_id << 5) | MDIO_READ;
> + writel_relaxed(val, priv->base + MDIO_CMD_REG);
> +
> + ret = hip04_mdio_wait_ready(bus);
> + if (ret < 0)
> + goto out;
> + val = readl_relaxed(priv->base + MDIO_STA_REG);
> + if (val & MDIO_R_VALID) {
> + dev_err(bus->parent, "SMI bus read not valid\n");
> + ret = -ENODEV;
> + goto out;
> + }
> + val = readl_relaxed(priv->base + MDIO_RDATA_REG);
> + ret = val & 0xFFFF;
> +out:
> + return ret;
> +}
> +
> +static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
> + int regnum, u16 value)
> +{
> + struct hip04_mdio_priv *priv = bus->priv;
> + u32 val;
> + int ret;
> +
> + ret = hip04_mdio_wait_ready(bus);
> + if (ret < 0)
> + goto out;
> +
> + writel_relaxed(value, priv->base + MDIO_WDATA_REG);
> + val = regnum | (mii_id << 5) | MDIO_WRITE;
> + writel_relaxed(val, priv->base + MDIO_CMD_REG);
> +out:
> + return ret;
> +}
> +
> +
> +static int hip04_mdio_reset(struct mii_bus *bus)
> +{
> + int temp, err, i;
> +
> + for (i = 0; i < 2; i++) {
> + hip04_mdio_write(bus, i, 22, 0);
> + temp = hip04_mdio_read(bus, i, MII_BMCR);
> + temp |= BMCR_RESET;
> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
> + if (err < 0)
> + return err;
> + }
> +
> + mdelay(500);
This does not look correct, you should iterate over all possible PHYs:
PHY_MAX_ADDR instead of hardcoding the loop to 2.
I think we might want to remove the mdio bus reset callback in general
as the PHY library should already take care of software resetting the
PHY to put it in a sane state, as well as waiting for the appropriate
delay before using, unlike here, where you do not poll for BMCR_RESET
to be cleared by the PHY.
> + return 0;
> +}
> +
> +static int hip04_mdio_probe(struct platform_device *pdev)
> +{
> + struct resource *r;
> + struct mii_bus *bus;
> + struct hip04_mdio_priv *priv;
> + int ret;
> +
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r) {
> + dev_err(&pdev->dev, "No SMI register address given\n");
> + return -ENODEV;
> + }
> +
> + bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
> + if (!bus) {
> + dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
> + return -ENOMEM;
> + }
> +
> + bus->name = "hip04_mdio_bus";
> + bus->read = hip04_mdio_read;
> + bus->write = hip04_mdio_write;
> + bus->reset = hip04_mdio_reset;
> + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
> + dev_name(&pdev->dev));
> + bus->parent = &pdev->dev;
> + priv = bus->priv;
> + priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
> + if (!priv->base) {
> + dev_err(&pdev->dev, "Unable to remap SMI register\n");
> + ret = -ENODEV;
> + goto out_mdio;
> + }
> +
> + ret = of_mdiobus_register(bus, pdev->dev.of_node);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
> + goto out_mdio;
> + }
> +
> + platform_set_drvdata(pdev, bus);
> +
> + return 0;
> +
> +out_mdio:
> + mdiobus_free(bus);
> + return ret;
> +}
> +
> +static int hip04_mdio_remove(struct platform_device *pdev)
> +{
> + struct mii_bus *bus = platform_get_drvdata(pdev);
> +
> + mdiobus_unregister(bus);
> + mdiobus_free(bus);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id hip04_mdio_match[] = {
> + { .compatible = "hisilicon,hip04-mdio" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, hip04_mdio_match);
> +
> +static struct platform_driver hip04_mdio_driver = {
> + .probe = hip04_mdio_probe,
> + .remove = hip04_mdio_remove,
> + .driver = {
> + .name = "hip04-mdio",
> + .owner = THIS_MODULE,
> + .of_match_table = hip04_mdio_match,
> + },
> +};
> +
> +module_platform_driver(hip04_mdio_driver);
> +
> +MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:hip04-mdio");
> --
> 1.7.9.5
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Florian
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-18 17:28 ` Florian Fainelli
0 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-18 17:28 UTC (permalink / raw)
To: linux-arm-kernel
2014-03-18 1:40 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
> Hisilicon hip04 platform mdio driver
> Reuse Marvell phy drivers/net/phy/marvell.c
>
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> ---
> drivers/net/ethernet/Kconfig | 1 +
> drivers/net/ethernet/Makefile | 1 +
> drivers/net/ethernet/hisilicon/Kconfig | 31 +++++
> drivers/net/ethernet/hisilicon/Makefile | 5 +
> drivers/net/ethernet/hisilicon/hip04_mdio.c | 190 +++++++++++++++++++++++++++
> 5 files changed, 228 insertions(+)
> create mode 100644 drivers/net/ethernet/hisilicon/Kconfig
> create mode 100644 drivers/net/ethernet/hisilicon/Makefile
> create mode 100644 drivers/net/ethernet/hisilicon/hip04_mdio.c
>
> diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
> index 39484b5..cef103d 100644
> --- a/drivers/net/ethernet/Kconfig
> +++ b/drivers/net/ethernet/Kconfig
> @@ -55,6 +55,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
> source "drivers/net/ethernet/faraday/Kconfig"
> source "drivers/net/ethernet/freescale/Kconfig"
> source "drivers/net/ethernet/fujitsu/Kconfig"
> +source "drivers/net/ethernet/hisilicon/Kconfig"
> source "drivers/net/ethernet/hp/Kconfig"
> source "drivers/net/ethernet/ibm/Kconfig"
> source "drivers/net/ethernet/intel/Kconfig"
> diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
> index adf61af..f70b166 100644
> --- a/drivers/net/ethernet/Makefile
> +++ b/drivers/net/ethernet/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
> obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
> obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
> obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
> +obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
> obj-$(CONFIG_NET_VENDOR_HP) += hp/
> obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
> obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
> diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
> new file mode 100644
> index 0000000..4b1c065
> --- /dev/null
> +++ b/drivers/net/ethernet/hisilicon/Kconfig
> @@ -0,0 +1,31 @@
> +#
> +# HISILICON device configuration
> +#
> +
> +config NET_VENDOR_HISILICON
> + bool "Hisilicon devices"
> + default y
> + depends on ARM
> + ---help---
> + If you have a network (Ethernet) card belonging to this class, say Y
> + and read the Ethernet-HOWTO, available from
> + <http://www.tldp.org/docs.html#howto>.
> +
> + Note that the answer to this question doesn't directly affect the
> + kernel: saying N will just cause the configurator to skip all
> + the questions about MOXA ART devices. If you say Y, you will be asked
> + for your specific card in the following questions.
> +
> +if NET_VENDOR_HISILICON
> +
> +config HIP04_ETH
> + tristate "HISILICON P04 Ethernet support"
> + select NET_CORE
> + select PHYLIB
> + select MARVELL_PHY
> + ---help---
> + If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
> + want to use the internal ethernet then you should answer Y to this.
> +
> +
> +endif # NET_VENDOR_HISILICON
> diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
> new file mode 100644
> index 0000000..1d6eb6e
> --- /dev/null
> +++ b/drivers/net/ethernet/hisilicon/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for the HISILICON network device drivers.
> +#
> +
> +obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
> diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
> new file mode 100644
> index 0000000..960adc2
> --- /dev/null
> +++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
> @@ -0,0 +1,190 @@
> +
> +/* Copyright (c) 2014 Linaro Ltd.
> + * Copyright (c) 2014 Hisilicon Limited.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/of_mdio.h>
> +#include <linux/delay.h>
> +
> +#define MDIO_CMD_REG 0x0
> +#define MDIO_ADDR_REG 0x4
> +#define MDIO_WDATA_REG 0x8
> +#define MDIO_RDATA_REG 0xc
> +#define MDIO_STA_REG 0x10
> +
> +#define MDIO_START BIT(14)
> +#define MDIO_R_VALID BIT(1)
> +#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
> +#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
> +
> +struct hip04_mdio_priv {
> + void __iomem *base;
> +};
> +
> +#define WAIT_TIMEOUT 10
> +static int hip04_mdio_wait_ready(struct mii_bus *bus)
> +{
> + struct hip04_mdio_priv *priv = bus->priv;
> + int i;
> +
> + for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
> + if (i == WAIT_TIMEOUT)
> + return -ETIMEDOUT;
> + msleep(20);
> + }
> +
> + return 0;
> +}
> +
> +static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
> +{
> + struct hip04_mdio_priv *priv = bus->priv;
> + u32 val;
> + int ret;
> +
> + ret = hip04_mdio_wait_ready(bus);
> + if (ret < 0)
> + goto out;
> +
> + val = regnum | (mii_id << 5) | MDIO_READ;
> + writel_relaxed(val, priv->base + MDIO_CMD_REG);
> +
> + ret = hip04_mdio_wait_ready(bus);
> + if (ret < 0)
> + goto out;
> + val = readl_relaxed(priv->base + MDIO_STA_REG);
> + if (val & MDIO_R_VALID) {
> + dev_err(bus->parent, "SMI bus read not valid\n");
> + ret = -ENODEV;
> + goto out;
> + }
> + val = readl_relaxed(priv->base + MDIO_RDATA_REG);
> + ret = val & 0xFFFF;
> +out:
> + return ret;
> +}
> +
> +static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
> + int regnum, u16 value)
> +{
> + struct hip04_mdio_priv *priv = bus->priv;
> + u32 val;
> + int ret;
> +
> + ret = hip04_mdio_wait_ready(bus);
> + if (ret < 0)
> + goto out;
> +
> + writel_relaxed(value, priv->base + MDIO_WDATA_REG);
> + val = regnum | (mii_id << 5) | MDIO_WRITE;
> + writel_relaxed(val, priv->base + MDIO_CMD_REG);
> +out:
> + return ret;
> +}
> +
> +
> +static int hip04_mdio_reset(struct mii_bus *bus)
> +{
> + int temp, err, i;
> +
> + for (i = 0; i < 2; i++) {
> + hip04_mdio_write(bus, i, 22, 0);
> + temp = hip04_mdio_read(bus, i, MII_BMCR);
> + temp |= BMCR_RESET;
> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
> + if (err < 0)
> + return err;
> + }
> +
> + mdelay(500);
This does not look correct, you should iterate over all possible PHYs:
PHY_MAX_ADDR instead of hardcoding the loop to 2.
I think we might want to remove the mdio bus reset callback in general
as the PHY library should already take care of software resetting the
PHY to put it in a sane state, as well as waiting for the appropriate
delay before using, unlike here, where you do not poll for BMCR_RESET
to be cleared by the PHY.
> + return 0;
> +}
> +
> +static int hip04_mdio_probe(struct platform_device *pdev)
> +{
> + struct resource *r;
> + struct mii_bus *bus;
> + struct hip04_mdio_priv *priv;
> + int ret;
> +
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r) {
> + dev_err(&pdev->dev, "No SMI register address given\n");
> + return -ENODEV;
> + }
> +
> + bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
> + if (!bus) {
> + dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
> + return -ENOMEM;
> + }
> +
> + bus->name = "hip04_mdio_bus";
> + bus->read = hip04_mdio_read;
> + bus->write = hip04_mdio_write;
> + bus->reset = hip04_mdio_reset;
> + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
> + dev_name(&pdev->dev));
> + bus->parent = &pdev->dev;
> + priv = bus->priv;
> + priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
> + if (!priv->base) {
> + dev_err(&pdev->dev, "Unable to remap SMI register\n");
> + ret = -ENODEV;
> + goto out_mdio;
> + }
> +
> + ret = of_mdiobus_register(bus, pdev->dev.of_node);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
> + goto out_mdio;
> + }
> +
> + platform_set_drvdata(pdev, bus);
> +
> + return 0;
> +
> +out_mdio:
> + mdiobus_free(bus);
> + return ret;
> +}
> +
> +static int hip04_mdio_remove(struct platform_device *pdev)
> +{
> + struct mii_bus *bus = platform_get_drvdata(pdev);
> +
> + mdiobus_unregister(bus);
> + mdiobus_free(bus);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id hip04_mdio_match[] = {
> + { .compatible = "hisilicon,hip04-mdio" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, hip04_mdio_match);
> +
> +static struct platform_driver hip04_mdio_driver = {
> + .probe = hip04_mdio_probe,
> + .remove = hip04_mdio_remove,
> + .driver = {
> + .name = "hip04-mdio",
> + .owner = THIS_MODULE,
> + .of_match_table = hip04_mdio_match,
> + },
> +};
> +
> +module_platform_driver(hip04_mdio_driver);
> +
> +MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:hip04-mdio");
> --
> 1.7.9.5
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-18 17:28 ` Florian Fainelli
@ 2014-03-20 10:53 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-20 10:53 UTC (permalink / raw)
To: Florian Fainelli
Cc: Zhangfei Gao, netdev, David S. Miller, linux-arm-kernel, devicetree
Dear Florian
On Wed, Mar 19, 2014 at 1:28 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> 2014-03-18 1:40 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
>> +static int hip04_mdio_reset(struct mii_bus *bus)
>> +{
>> + int temp, err, i;
>> +
>> + for (i = 0; i < 2; i++) {
>> + hip04_mdio_write(bus, i, 22, 0);
>> + temp = hip04_mdio_read(bus, i, MII_BMCR);
>> + temp |= BMCR_RESET;
>> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
>> + if (err < 0)
>> + return err;
>> + }
>> +
>> + mdelay(500);
>
> This does not look correct, you should iterate over all possible PHYs:
> PHY_MAX_ADDR instead of hardcoding the loop to 2.
OK, got it.
Use 2 is since only have 2 phy in the board, will use PHY_MAX_ADDR instead.
>
> I think we might want to remove the mdio bus reset callback in general
> as the PHY library should already take care of software resetting the
> PHY to put it in a sane state, as well as waiting for the appropriate
> delay before using, unlike here, where you do not poll for BMCR_RESET
> to be cleared by the PHY.
>
Do you mean will move BMCR_RESET to common code, that's would be great.
The mdio_reset is added here to get phy_id, otherwise the phy_id can
not be read as well as detection.
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-20 10:53 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-20 10:53 UTC (permalink / raw)
To: linux-arm-kernel
Dear Florian
On Wed, Mar 19, 2014 at 1:28 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> 2014-03-18 1:40 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
>> +static int hip04_mdio_reset(struct mii_bus *bus)
>> +{
>> + int temp, err, i;
>> +
>> + for (i = 0; i < 2; i++) {
>> + hip04_mdio_write(bus, i, 22, 0);
>> + temp = hip04_mdio_read(bus, i, MII_BMCR);
>> + temp |= BMCR_RESET;
>> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
>> + if (err < 0)
>> + return err;
>> + }
>> +
>> + mdelay(500);
>
> This does not look correct, you should iterate over all possible PHYs:
> PHY_MAX_ADDR instead of hardcoding the loop to 2.
OK, got it.
Use 2 is since only have 2 phy in the board, will use PHY_MAX_ADDR instead.
>
> I think we might want to remove the mdio bus reset callback in general
> as the PHY library should already take care of software resetting the
> PHY to put it in a sane state, as well as waiting for the appropriate
> delay before using, unlike here, where you do not poll for BMCR_RESET
> to be cleared by the PHY.
>
Do you mean will move BMCR_RESET to common code, that's would be great.
The mdio_reset is added here to get phy_id, otherwise the phy_id can
not be read as well as detection.
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-20 10:53 ` Zhangfei Gao
@ 2014-03-20 17:59 ` Florian Fainelli
-1 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-20 17:59 UTC (permalink / raw)
To: Zhangfei Gao
Cc: Zhangfei Gao, netdev, David S. Miller, linux-arm-kernel, devicetree
2014-03-20 3:53 GMT-07:00 Zhangfei Gao <zhangfei.gao@gmail.com>:
> Dear Florian
>
> On Wed, Mar 19, 2014 at 1:28 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
>> 2014-03-18 1:40 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
>
>>> +static int hip04_mdio_reset(struct mii_bus *bus)
>>> +{
>>> + int temp, err, i;
>>> +
>>> + for (i = 0; i < 2; i++) {
>>> + hip04_mdio_write(bus, i, 22, 0);
>>> + temp = hip04_mdio_read(bus, i, MII_BMCR);
>>> + temp |= BMCR_RESET;
>>> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
>>> + if (err < 0)
>>> + return err;
>>> + }
>>> +
>>> + mdelay(500);
>>
>> This does not look correct, you should iterate over all possible PHYs:
>> PHY_MAX_ADDR instead of hardcoding the loop to 2.
>
> OK, got it.
> Use 2 is since only have 2 phy in the board, will use PHY_MAX_ADDR instead.
>>
>> I think we might want to remove the mdio bus reset callback in general
>> as the PHY library should already take care of software resetting the
>> PHY to put it in a sane state, as well as waiting for the appropriate
>> delay before using, unlike here, where you do not poll for BMCR_RESET
>> to be cleared by the PHY.
>>
> Do you mean will move BMCR_RESET to common code, that's would be great.
> The mdio_reset is added here to get phy_id, otherwise the phy_id can
> not be read as well as detection.
Oh I see, thanks for mentioning that, this is not properly covered
here today as we need to get the PHY id before we assign it a
phy_device structure, let me cook a patch for this.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-20 17:59 ` Florian Fainelli
0 siblings, 0 replies; 78+ messages in thread
From: Florian Fainelli @ 2014-03-20 17:59 UTC (permalink / raw)
To: linux-arm-kernel
2014-03-20 3:53 GMT-07:00 Zhangfei Gao <zhangfei.gao@gmail.com>:
> Dear Florian
>
> On Wed, Mar 19, 2014 at 1:28 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
>> 2014-03-18 1:40 GMT-07:00 Zhangfei Gao <zhangfei.gao@linaro.org>:
>
>>> +static int hip04_mdio_reset(struct mii_bus *bus)
>>> +{
>>> + int temp, err, i;
>>> +
>>> + for (i = 0; i < 2; i++) {
>>> + hip04_mdio_write(bus, i, 22, 0);
>>> + temp = hip04_mdio_read(bus, i, MII_BMCR);
>>> + temp |= BMCR_RESET;
>>> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
>>> + if (err < 0)
>>> + return err;
>>> + }
>>> +
>>> + mdelay(500);
>>
>> This does not look correct, you should iterate over all possible PHYs:
>> PHY_MAX_ADDR instead of hardcoding the loop to 2.
>
> OK, got it.
> Use 2 is since only have 2 phy in the board, will use PHY_MAX_ADDR instead.
>>
>> I think we might want to remove the mdio bus reset callback in general
>> as the PHY library should already take care of software resetting the
>> PHY to put it in a sane state, as well as waiting for the appropriate
>> delay before using, unlike here, where you do not poll for BMCR_RESET
>> to be cleared by the PHY.
>>
> Do you mean will move BMCR_RESET to common code, that's would be great.
> The mdio_reset is added here to get phy_id, otherwise the phy_id can
> not be read as well as detection.
Oh I see, thanks for mentioning that, this is not properly covered
here today as we need to get the PHY id before we assign it a
phy_device structure, let me cook a patch for this.
--
Florian
^ permalink raw reply [flat|nested] 78+ messages in thread
* Re: [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
2014-03-20 17:59 ` Florian Fainelli
@ 2014-03-21 5:27 ` Zhangfei Gao
-1 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-21 5:27 UTC (permalink / raw)
To: Florian Fainelli
Cc: Zhangfei Gao, netdev, David S. Miller, linux-arm-kernel, devicetree
On Fri, Mar 21, 2014 at 1:59 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> 2014-03-20 3:53 GMT-07:00 Zhangfei Gao <zhangfei.gao@gmail.com>:
>>>> +static int hip04_mdio_reset(struct mii_bus *bus)
>>>> +{
>>>> + int temp, err, i;
>>>> +
>>>> + for (i = 0; i < 2; i++) {
>>>> + hip04_mdio_write(bus, i, 22, 0);
>>>> + temp = hip04_mdio_read(bus, i, MII_BMCR);
>>>> + temp |= BMCR_RESET;
>>>> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
>>>> + if (err < 0)
>>>> + return err;
>>>> + }
>>>> +
>>>> + mdelay(500);
>>>
>>> This does not look correct, you should iterate over all possible PHYs:
>>> PHY_MAX_ADDR instead of hardcoding the loop to 2.
>>
>> OK, got it.
>> Use 2 is since only have 2 phy in the board, will use PHY_MAX_ADDR instead.
>>>
>>> I think we might want to remove the mdio bus reset callback in general
>>> as the PHY library should already take care of software resetting the
>>> PHY to put it in a sane state, as well as waiting for the appropriate
>>> delay before using, unlike here, where you do not poll for BMCR_RESET
>>> to be cleared by the PHY.
>>>
>> Do you mean will move BMCR_RESET to common code, that's would be great.
>> The mdio_reset is added here to get phy_id, otherwise the phy_id can
>> not be read as well as detection.
>
> Oh I see, thanks for mentioning that, this is not properly covered
> here today as we need to get the PHY id before we assign it a
> phy_device structure, let me cook a patch for this.
> --
Should I keep hip04_mdio_reset in the next submit or remove it directly?
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
* [PATCH 2/3] net: hisilicon: new hip04 MDIO driver
@ 2014-03-21 5:27 ` Zhangfei Gao
0 siblings, 0 replies; 78+ messages in thread
From: Zhangfei Gao @ 2014-03-21 5:27 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Mar 21, 2014 at 1:59 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> 2014-03-20 3:53 GMT-07:00 Zhangfei Gao <zhangfei.gao@gmail.com>:
>>>> +static int hip04_mdio_reset(struct mii_bus *bus)
>>>> +{
>>>> + int temp, err, i;
>>>> +
>>>> + for (i = 0; i < 2; i++) {
>>>> + hip04_mdio_write(bus, i, 22, 0);
>>>> + temp = hip04_mdio_read(bus, i, MII_BMCR);
>>>> + temp |= BMCR_RESET;
>>>> + err = hip04_mdio_write(bus, i, MII_BMCR, temp);
>>>> + if (err < 0)
>>>> + return err;
>>>> + }
>>>> +
>>>> + mdelay(500);
>>>
>>> This does not look correct, you should iterate over all possible PHYs:
>>> PHY_MAX_ADDR instead of hardcoding the loop to 2.
>>
>> OK, got it.
>> Use 2 is since only have 2 phy in the board, will use PHY_MAX_ADDR instead.
>>>
>>> I think we might want to remove the mdio bus reset callback in general
>>> as the PHY library should already take care of software resetting the
>>> PHY to put it in a sane state, as well as waiting for the appropriate
>>> delay before using, unlike here, where you do not poll for BMCR_RESET
>>> to be cleared by the PHY.
>>>
>> Do you mean will move BMCR_RESET to common code, that's would be great.
>> The mdio_reset is added here to get phy_id, otherwise the phy_id can
>> not be read as well as detection.
>
> Oh I see, thanks for mentioning that, this is not properly covered
> here today as we need to get the PHY id before we assign it a
> phy_device structure, let me cook a patch for this.
> --
Should I keep hip04_mdio_reset in the next submit or remove it directly?
Thanks
^ permalink raw reply [flat|nested] 78+ messages in thread
end of thread, other threads:[~2014-04-07 18:47 UTC | newest]
Thread overview: 78+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-24 14:14 [PATCH v3 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
2014-03-24 14:14 ` Zhangfei Gao
2014-03-24 14:14 ` [PATCH 1/3] Documentation: add Device tree bindings for Hisilicon hip04 ethernet Zhangfei Gao
2014-03-24 14:14 ` Zhangfei Gao
[not found] ` <1395670496-17381-1-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-03-24 14:14 ` [PATCH 2/3] net: hisilicon: new hip04 MDIO driver Zhangfei Gao
2014-03-24 14:14 ` Zhangfei Gao
2014-03-24 14:14 ` [PATCH 3/3] net: hisilicon: new hip04 ethernet driver Zhangfei Gao
2014-03-24 14:14 ` Zhangfei Gao
[not found] ` <1395670496-17381-4-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-03-24 15:18 ` Arnd Bergmann
2014-03-24 15:18 ` Arnd Bergmann
2014-03-25 4:06 ` Zhangfei Gao
2014-03-25 4:06 ` Zhangfei Gao
2014-03-25 8:12 ` Arnd Bergmann
2014-03-25 8:12 ` Arnd Bergmann
2014-03-25 17:00 ` Florian Fainelli
2014-03-25 17:00 ` Florian Fainelli
2014-03-25 17:05 ` Arnd Bergmann
2014-03-25 17:05 ` Arnd Bergmann
2014-03-25 17:16 ` Florian Fainelli
2014-03-25 17:16 ` Florian Fainelli
2014-03-25 17:57 ` Arnd Bergmann
2014-03-25 17:57 ` Arnd Bergmann
2014-03-26 9:55 ` David Laight
2014-03-26 9:55 ` David Laight
2014-03-25 17:17 ` David Laight
2014-03-25 17:17 ` David Laight
2014-03-25 17:21 ` Eric Dumazet
2014-03-25 17:21 ` Eric Dumazet
2014-03-25 17:54 ` Arnd Bergmann
2014-03-25 17:54 ` Arnd Bergmann
2014-03-27 12:53 ` zhangfei
2014-03-27 12:53 ` zhangfei
2014-03-24 16:32 ` Florian Fainelli
2014-03-24 16:32 ` Florian Fainelli
2014-03-24 17:23 ` Arnd Bergmann
2014-03-24 17:23 ` Arnd Bergmann
2014-03-24 17:35 ` Florian Fainelli
2014-03-24 17:35 ` Florian Fainelli
2014-03-27 6:27 ` Zhangfei Gao
2014-03-27 6:27 ` Zhangfei Gao
-- strict thread matches above, loose matches on Subject: below --
2014-04-05 4:35 [PATCH v7 0/3] add hisilicon " Zhangfei Gao
2014-04-05 4:35 ` [PATCH 2/3] net: hisilicon: new hip04 MDIO driver Zhangfei Gao
2014-04-05 4:35 ` Zhangfei Gao
2014-04-07 18:47 ` David Miller
2014-04-07 18:47 ` David Miller
2014-04-04 15:16 [PATCH v6 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
2014-04-04 15:16 ` [PATCH 2/3] net: hisilicon: new hip04 MDIO driver Zhangfei Gao
2014-04-04 15:16 ` Zhangfei Gao
2014-04-04 15:30 ` David Miller
2014-04-04 15:30 ` David Miller
2014-04-04 15:40 ` zhangfei
2014-04-04 15:40 ` zhangfei
2014-04-04 15:43 ` David Miller
2014-04-04 15:43 ` David Miller
2014-04-04 15:45 ` Zhangfei Gao
2014-04-04 15:45 ` Zhangfei Gao
2014-04-01 13:27 [PATCH v5 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
[not found] ` <1396358832-15828-1-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-04-01 13:27 ` [PATCH 2/3] net: hisilicon: new hip04 MDIO driver Zhangfei Gao
2014-04-01 13:27 ` Zhangfei Gao
2014-04-04 15:42 ` Zhangfei Gao
2014-04-04 15:42 ` Zhangfei Gao
2014-04-04 17:48 ` David Miller
2014-04-04 17:48 ` David Miller
2014-03-28 15:35 [PATCH v4 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
2014-03-28 15:35 ` [PATCH 2/3] net: hisilicon: new hip04 MDIO driver Zhangfei Gao
2014-03-28 15:35 ` Zhangfei Gao
[not found] ` <1396020960-6170-3-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-03-30 21:23 ` David Miller
2014-03-30 21:23 ` David Miller
2014-03-31 2:45 ` zhangfei
2014-03-31 2:45 ` zhangfei
2014-03-21 15:09 [PATCH v2 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
2014-03-21 15:09 ` [PATCH 2/3] net: hisilicon: new hip04 MDIO driver Zhangfei Gao
2014-03-21 15:09 ` Zhangfei Gao
2014-03-18 8:40 [PATCH 0/3] add hisilicon hip04 ethernet driver Zhangfei Gao
2014-03-18 8:40 ` [PATCH 2/3] net: hisilicon: new hip04 MDIO driver Zhangfei Gao
2014-03-18 8:40 ` Zhangfei Gao
[not found] ` <1395132017-15928-3-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-03-18 17:28 ` Florian Fainelli
2014-03-18 17:28 ` Florian Fainelli
2014-03-20 10:53 ` Zhangfei Gao
2014-03-20 10:53 ` Zhangfei Gao
2014-03-20 17:59 ` Florian Fainelli
2014-03-20 17:59 ` Florian Fainelli
2014-03-21 5:27 ` Zhangfei Gao
2014-03-21 5:27 ` Zhangfei Gao
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.