* Re: [PATCH] mdio-mux-gpio: Use GPIO descriptor interface and new gpiod_set_array function
From: Rojhalat Ibrahim @ 2014-11-25 8:13 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Florian Fainelli, netdev, David Daney, linux-gpio@vger.kernel.org,
Linus Walleij, Alexandre Courbot
In-Reply-To: <CAAVeFuKTmQi3vK5vALbT=hseRm9mn=-cb-7hiHwg7Zbczmz_WA@mail.gmail.com>
On Tuesday 25 November 2014 12:39:13 Alexandre Courbot wrote:
> On Thu, Nov 20, 2014 at 9:24 PM, Rojhalat Ibrahim <imr@rtschenk.de> wrote:
> > @@ -70,20 +64,17 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
> > s->num_gpios = num_gpios;
> >
> > for (n = 0; n < num_gpios; ) {
> > - int gpio = of_get_gpio_flags(pdev->dev.of_node, n, &f);
> > - if (gpio < 0) {
> > - r = (gpio == -ENODEV) ? -EPROBE_DEFER : gpio;
> > + struct gpio_desc *gpio = gpiod_get_index(&pdev->dev,
> > + "mdio-mux-gpio", n);
>
> Doesn't this change introduce some incompatibility against the current
> DT bindings? of_get_gpio_flags() looks for a "gpios" property, while
> your call to gpiod_get_index() will look for "mdio-mux-gpio-gpios". It
> should probably be changed to gpiod_get_index(&pdev->dev, NULL, n).
>
You are right, of course. I'll fix it and post a new patch version.
> ... or even be changed to gpiod_get_index(&pdev->dev, NULL, n,
> GPIOD_OUT_LOW) as the calls to gpiod_get() functions are being updated
> to take an initial state (both variants are currently usable but the
> former one will disappear in the future). This will also allow you to
> get rid of your call to gpiod_direction_output().
>
Will do that.
> Side-note: it would be nice to have a gpiod_get_array() call that does
> exactly what this loop does, and returns an array of gpios directly
> usable by gpiod_set_array*(). And a matching gpiod_put_array() of
> course.
>
I'll add that to my to-do-list.
Thanks
Rojhalat
^ permalink raw reply
* Re: [PATCH] net-timestamp: Fix a documentation typo
From: Richard Cochran @ 2014-11-25 8:33 UTC (permalink / raw)
To: Willem de Bruijn; +Cc: Andy Lutomirski, Network Development, David S. Miller
In-Reply-To: <CA+FuTSe8pjpUUpStfEHsuZ2Row9=MHVPH0S-MjUxiauBSNMOsg@mail.gmail.com>
On Mon, Nov 24, 2014 at 05:38:21PM -0500, Willem de Bruijn wrote:
> The datagram feature existed before I added the counter and stream
> support, so returning the entire packet in that case is legacy
> behavior, I suppose. I did not intend to expose network headers for
> the new stream socket interface, though.
I suppose that idea (not mine) behind returning the entire frame was
to allow the user to send multiple frames, and then figure out which
time stamp goes with which sent frame. The whole thing is weak for two
reasons, firstly you aren't able to send two identical frames, and
secondly there are quite a few time stamping cards out there which
only handle one transmit time stamp at a time. Thus the ptp4l
application always waits for the hardware time stamp before sending
another frame and never looks at the returned frame data.
Thanks,
Richard
^ permalink raw reply
* [PATCH 0/4] support GMAC driver for RK3288
From: Roger Chen @ 2014-11-25 9:06 UTC (permalink / raw)
To: heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, kever.yang,
mark.yao, eddie.cai, Roger Chen
Roger Chen (4):
patch1: add driver for Rockchip RK3288 SoCs integrated GMAC
patch2: modify CRU config for Rockchip RK3288 SoCs integrated GMAC
patch3: dts: add gmac info for rk3288
patch4: dts: enable gmac on RK3288 evb board
tested on rk3288 SDK board:
busybox ifconfig eth0 up
busybox ifconfig eth0 192.168.1.111
ping 192.168.1.1
--
1.7.9.5
^ permalink raw reply
* [PATCH 1/4] GMAC: add driver for Rockchip RK3288 SoCs integrated GMAC
From: Roger Chen @ 2014-11-25 9:06 UTC (permalink / raw)
To: heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, kever.yang,
mark.yao, eddie.cai, Roger Chen
In-Reply-To: <1416906418-26034-1-git-send-email-roger.chen@rock-chips.com>
This driver is based on stmmac driver.
Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
---
drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +-
drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 629 ++++++++++++++++++++
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 +
.../net/ethernet/stmicro/stmmac/stmmac_platform.h | 1 +
4 files changed, 634 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index ac4d562..73c2715 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \
- dwmac-sti.o dwmac-socfpga.o
+ dwmac-sti.o dwmac-socfpga.o dwmac-rk.o
obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
stmmac-pci-objs:= stmmac_pci.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
new file mode 100644
index 0000000..9e50b37
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -0,0 +1,629 @@
+/**
+ * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer
+ *
+ * Copyright (C) 2014 Chen-Zhi (Roger Chen)
+ *
+ * Chen-Zhi (Roger Chen) <roger.chen@rock-chips.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/stmmac.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/of_net.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+struct rk_priv_data {
+ struct platform_device *pdev;
+ int phy_iface;
+ bool power_ctrl_by_pmu;
+ char pmu_regulator[32];
+ int pmu_enable_level;
+
+ int power_io;
+ int power_io_level;
+ int reset_io;
+ int reset_io_level;
+ int phyirq_io;
+ int phyirq_io_level;
+
+ bool clk_enabled;
+ bool clock_input;
+
+ struct clk *clk_mac;
+ struct clk *clk_mac_pll;
+ struct clk *gmac_clkin;
+ struct clk *mac_clk_rx;
+ struct clk *mac_clk_tx;
+ struct clk *clk_mac_ref;
+ struct clk *clk_mac_refout;
+ struct clk *aclk_mac;
+ struct clk *pclk_mac;
+
+ int tx_delay;
+ int rx_delay;
+
+ struct regmap *grf;
+};
+
+#define RK3288_GRF_SOC_CON1 0x0248
+#define RK3288_GRF_SOC_CON3 0x0250
+#define RK3288_GRF_GPIO3D_E 0x01ec
+#define RK3288_GRF_GPIO4A_E 0x01f0
+#define RK3288_GRF_GPIO4B_E 0x01f4
+
+/*RK3288_GRF_SOC_CON1*/
+#define GMAC_PHY_INTF_SEL_RGMII ((0x01C0 << 16) | (0x0040))
+#define GMAC_PHY_INTF_SEL_RMII ((0x01C0 << 16) | (0x0100))
+#define GMAC_FLOW_CTRL ((0x0200 << 16) | (0x0200))
+#define GMAC_FLOW_CTRL_CLR ((0x0200 << 16) | (0x0000))
+#define GMAC_SPEED_10M ((0x0400 << 16) | (0x0000))
+#define GMAC_SPEED_100M ((0x0400 << 16) | (0x0400))
+#define GMAC_RMII_CLK_25M ((0x0800 << 16) | (0x0800))
+#define GMAC_RMII_CLK_2_5M ((0x0800 << 16) | (0x0000))
+#define GMAC_CLK_125M ((0x3000 << 16) | (0x0000))
+#define GMAC_CLK_25M ((0x3000 << 16) | (0x3000))
+#define GMAC_CLK_2_5M ((0x3000 << 16) | (0x2000))
+#define GMAC_RMII_MODE ((0x4000 << 16) | (0x4000))
+#define GMAC_RMII_MODE_CLR ((0x4000 << 16) | (0x0000))
+
+/*RK3288_GRF_SOC_CON3*/
+#define GMAC_TXCLK_DLY_ENABLE ((0x4000 << 16) | (0x4000))
+#define GMAC_TXCLK_DLY_DISABLE ((0x4000 << 16) | (0x0000))
+#define GMAC_RXCLK_DLY_ENABLE ((0x8000 << 16) | (0x8000))
+#define GMAC_RXCLK_DLY_DISABLE ((0x8000 << 16) | (0x0000))
+#define GMAC_CLK_RX_DL_CFG(val) ((0x3F80 << 16) | (val<<7))
+#define GMAC_CLK_TX_DL_CFG(val) ((0x007F << 16) | (val))
+
+static void set_to_rgmii(struct rk_priv_data *bsp_priv,
+ int tx_delay, int rx_delay)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_PHY_INTF_SEL_RGMII);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_MODE_CLR);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_RXCLK_DLY_ENABLE);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_TXCLK_DLY_ENABLE);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_CLK_RX_DL_CFG(rx_delay));
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_CLK_TX_DL_CFG(tx_delay));
+
+ regmap_write(bsp_priv->grf, RK3288_GRF_GPIO3D_E, 0xFFFFFFFF);
+ regmap_write(bsp_priv->grf, RK3288_GRF_GPIO4B_E,
+ 0x3<<2<<16 | 0x3<<2);
+ regmap_write(bsp_priv->grf, RK3288_GRF_GPIO4A_E, 0xFFFFFFFF);
+
+ pr_info("%s: tx delay=0x%x; rx delay=0x%x;\n",
+ __func__, tx_delay, rx_delay);
+}
+
+static void set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_PHY_INTF_SEL_RMII);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_MODE);
+}
+
+static void set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ if (speed == 10)
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_2_5M);
+ else if (speed == 100)
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_25M);
+ else if (speed == 1000)
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_125M);
+ else
+ pr_err("unknown speed value for RGMII! speed=%d", speed);
+}
+
+static void set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ if (speed == 10) {
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_CLK_2_5M);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_SPEED_10M);
+ } else if (speed == 100) {
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_CLK_25M);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_SPEED_100M);
+ } else {
+ pr_err("unknown speed value for RMII! speed=%d", speed);
+ }
+}
+
+#define MAC_CLK_RX "mac_clk_rx"
+#define MAC_CLK_TX "mac_clk_tx"
+#define CLK_MAC_REF "clk_mac_ref"
+#define CLK_MAC_REF_OUT "clk_mac_refout"
+#define CLK_MAC_PLL "clk_mac_pll"
+#define ACLK_MAC "aclk_mac"
+#define PCLK_MAC "pclk_mac"
+#define MAC_CLKIN "ext_gmac"
+#define CLK_MAC "stmmaceth"
+
+int gmac_clk_init(struct rk_priv_data *bsp_priv)
+{
+ struct device *dev = &bsp_priv->pdev->dev;
+
+ bsp_priv->clk_enabled = false;
+
+ bsp_priv->mac_clk_rx = clk_get(dev, MAC_CLK_RX);
+ if (IS_ERR(bsp_priv->mac_clk_rx))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, MAC_CLK_RX);
+
+ bsp_priv->mac_clk_tx = clk_get(dev, MAC_CLK_TX);
+ if (IS_ERR(bsp_priv->mac_clk_tx))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, MAC_CLK_TX);
+
+ bsp_priv->clk_mac_ref = clk_get(dev, CLK_MAC_REF);
+ if (IS_ERR(bsp_priv->clk_mac_ref))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, CLK_MAC_REF);
+
+ bsp_priv->clk_mac_refout = clk_get(dev, CLK_MAC_REF_OUT);
+ if (IS_ERR(bsp_priv->clk_mac_refout))
+ pr_warn("%s: warning:cannot get clock %s\n",
+ __func__, CLK_MAC_REF_OUT);
+
+ bsp_priv->aclk_mac = clk_get(dev, ACLK_MAC);
+ if (IS_ERR(bsp_priv->aclk_mac))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, ACLK_MAC);
+
+ bsp_priv->pclk_mac = clk_get(dev, PCLK_MAC);
+ if (IS_ERR(bsp_priv->pclk_mac))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, PCLK_MAC);
+
+ bsp_priv->clk_mac_pll = clk_get(dev, CLK_MAC_PLL);
+ if (IS_ERR(bsp_priv->clk_mac_pll))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, CLK_MAC_PLL);
+
+ bsp_priv->gmac_clkin = clk_get(dev, MAC_CLKIN);
+ if (IS_ERR(bsp_priv->gmac_clkin))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, MAC_CLKIN);
+
+ bsp_priv->clk_mac = clk_get(dev, CLK_MAC);
+ if (IS_ERR(bsp_priv->clk_mac))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, CLK_MAC);
+
+ if (bsp_priv->clock_input) {
+ pr_info("%s: clock input from PHY\n", __func__);
+ } else {
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
+ clk_set_rate(bsp_priv->clk_mac_pll, 50000000);
+
+ clk_set_parent(bsp_priv->clk_mac, bsp_priv->clk_mac_pll);
+ }
+
+ return 0;
+}
+
+static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
+{
+ int phy_iface = phy_iface = bsp_priv->phy_iface;
+
+ if (enable) {
+ if (!bsp_priv->clk_enabled) {
+ if (phy_iface == PHY_INTERFACE_MODE_RMII) {
+ if (!IS_ERR(bsp_priv->mac_clk_rx))
+ clk_prepare_enable(
+ bsp_priv->mac_clk_rx);
+
+ if (!IS_ERR(bsp_priv->clk_mac_ref))
+ clk_prepare_enable(
+ bsp_priv->clk_mac_ref);
+
+ if (!IS_ERR(bsp_priv->clk_mac_refout))
+ clk_prepare_enable(
+ bsp_priv->clk_mac_refout);
+ }
+
+ if (!IS_ERR(bsp_priv->aclk_mac))
+ clk_prepare_enable(bsp_priv->aclk_mac);
+
+ if (!IS_ERR(bsp_priv->pclk_mac))
+ clk_prepare_enable(bsp_priv->pclk_mac);
+
+ if (!IS_ERR(bsp_priv->mac_clk_tx))
+ clk_prepare_enable(bsp_priv->mac_clk_tx);
+
+ /**
+ * if (!IS_ERR(bsp_priv->clk_mac))
+ * clk_prepare_enable(bsp_priv->clk_mac);
+ */
+ mdelay(5);
+ bsp_priv->clk_enabled = true;
+ }
+ } else {
+ if (bsp_priv->clk_enabled) {
+ if (phy_iface == PHY_INTERFACE_MODE_RMII) {
+ if (!IS_ERR(bsp_priv->mac_clk_rx))
+ clk_disable_unprepare(
+ bsp_priv->mac_clk_rx);
+
+ if (!IS_ERR(bsp_priv->clk_mac_ref))
+ clk_disable_unprepare(
+ bsp_priv->clk_mac_ref);
+
+ if (!IS_ERR(bsp_priv->clk_mac_refout))
+ clk_disable_unprepare(
+ bsp_priv->clk_mac_refout);
+ }
+
+ if (!IS_ERR(bsp_priv->aclk_mac))
+ clk_disable_unprepare(bsp_priv->aclk_mac);
+
+ if (!IS_ERR(bsp_priv->pclk_mac))
+ clk_disable_unprepare(bsp_priv->pclk_mac);
+
+ if (!IS_ERR(bsp_priv->mac_clk_tx))
+ clk_disable_unprepare(bsp_priv->mac_clk_tx);
+ /**
+ * if (!IS_ERR(bsp_priv->clk_mac))
+ * clk_disable_unprepare(bsp_priv->clk_mac);
+ */
+ bsp_priv->clk_enabled = false;
+ }
+ }
+
+ return 0;
+}
+
+static int power_on_by_pmu(struct rk_priv_data *bsp_priv, bool enable)
+{
+ struct regulator *ldo;
+ char *ldostr = bsp_priv->pmu_regulator;
+ int ret;
+
+ if (!ldostr) {
+ pr_err("%s: no ldo found\n", __func__);
+ return -1;
+ }
+
+ ldo = regulator_get(NULL, ldostr);
+ if (!ldo) {
+ pr_err("\n%s get ldo %s failed\n", __func__, ldostr);
+ } else {
+ if (enable) {
+ if (!regulator_is_enabled(ldo)) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if (ret != 0)
+ pr_err("%s: fail to enable %s\n",
+ __func__, ldostr);
+ else
+ pr_info("turn on ldo done.\n");
+ } else {
+ pr_warn("%s is enabled before enable", ldostr);
+ }
+ } else {
+ if (regulator_is_enabled(ldo)) {
+ ret = regulator_disable(ldo);
+ if (ret != 0)
+ pr_err("%s: fail to disable %s\n",
+ __func__, ldostr);
+ else
+ pr_info("turn off ldo done.\n");
+ } else {
+ pr_warn("%s is disabled before disable",
+ ldostr);
+ }
+ }
+ regulator_put(ldo);
+ }
+
+ return 0;
+}
+
+static int power_on_by_gpio(struct rk_priv_data *bsp_priv, bool enable)
+{
+ if (enable) {
+ /*power on*/
+ if (gpio_is_valid(bsp_priv->power_io))
+ gpio_direction_output(bsp_priv->power_io,
+ bsp_priv->power_io_level);
+ } else {
+ /*power off*/
+ if (gpio_is_valid(bsp_priv->power_io))
+ gpio_direction_output(bsp_priv->power_io,
+ !bsp_priv->power_io_level);
+ }
+
+ return 0;
+}
+
+static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable)
+{
+ int ret = -1;
+
+ pr_info("Ethernet PHY power %s\n", enable == 1 ? "on" : "off");
+
+ if (bsp_priv->power_ctrl_by_pmu)
+ ret = power_on_by_pmu(bsp_priv, enable);
+ else
+ ret = power_on_by_gpio(bsp_priv, enable);
+
+ if (enable) {
+ /*reset*/
+ if (gpio_is_valid(bsp_priv->reset_io)) {
+ gpio_direction_output(bsp_priv->reset_io,
+ bsp_priv->reset_io_level);
+ mdelay(5);
+ gpio_direction_output(bsp_priv->reset_io,
+ !bsp_priv->reset_io_level);
+ }
+ mdelay(30);
+
+ } else {
+ /*pull down reset*/
+ if (gpio_is_valid(bsp_priv->reset_io)) {
+ gpio_direction_output(bsp_priv->reset_io,
+ bsp_priv->reset_io_level);
+ }
+ }
+
+ return ret;
+}
+
+#define GPIO_PHY_POWER "gmac_phy_power"
+#define GPIO_PHY_RESET "gmac_phy_reset"
+#define GPIO_PHY_IRQ "gmac_phy_irq"
+
+static void *rk_gmac_setup(struct platform_device *pdev)
+{
+ struct rk_priv_data *bsp_priv;
+ struct device *dev = &pdev->dev;
+ enum of_gpio_flags flags;
+ int ret;
+ const char *strings = NULL;
+ int value;
+ int irq;
+
+ bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL);
+ if (!bsp_priv)
+ return ERR_PTR(-ENOMEM);
+
+ bsp_priv->phy_iface = of_get_phy_mode(dev->of_node);
+
+ ret = of_property_read_string(dev->of_node, "pmu_regulator", &strings);
+ if (ret) {
+ pr_err("%s: Can not read property: pmu_regulator.\n", __func__);
+ bsp_priv->power_ctrl_by_pmu = false;
+ } else {
+ pr_info("%s: ethernet phy power controlled by pmu(%s).\n",
+ __func__, strings);
+ bsp_priv->power_ctrl_by_pmu = true;
+ strcpy(bsp_priv->pmu_regulator, strings);
+ }
+
+ ret = of_property_read_u32(dev->of_node, "pmu_enable_level", &value);
+ if (ret) {
+ pr_err("%s: Can not read property: pmu_enable_level.\n",
+ __func__);
+ bsp_priv->power_ctrl_by_pmu = false;
+ } else {
+ pr_info("%s: PHY power controlled by pmu(level = %s).\n",
+ __func__, (value == 1) ? "HIGH" : "LOW");
+ bsp_priv->power_ctrl_by_pmu = true;
+ bsp_priv->pmu_enable_level = value;
+ }
+
+ ret = of_property_read_string(dev->of_node, "clock_in_out", &strings);
+ if (ret) {
+ pr_err("%s: Can not read property: clock_in_out.\n", __func__);
+ bsp_priv->clock_input = true;
+ } else {
+ pr_info("%s: clock input or output? (%s).\n",
+ __func__, strings);
+ if (!strcmp(strings, "input"))
+ bsp_priv->clock_input = true;
+ else
+ bsp_priv->clock_input = false;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "tx_delay", &value);
+ if (ret) {
+ bsp_priv->tx_delay = 0x30;
+ pr_err("%s: Can not read property: tx_delay.", __func__);
+ pr_err("%s: set tx_delay to 0x%x\n",
+ __func__, bsp_priv->tx_delay);
+ } else {
+ pr_info("%s: TX delay(0x%x).\n", __func__, value);
+ bsp_priv->tx_delay = value;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "rx_delay", &value);
+ if (ret) {
+ bsp_priv->rx_delay = 0x10;
+ pr_err("%s: Can not read property: rx_delay.", __func__);
+ pr_err("%s: set rx_delay to 0x%x\n",
+ __func__, bsp_priv->rx_delay);
+ } else {
+ pr_info("%s: RX delay(0x%x).\n", __func__, value);
+ bsp_priv->rx_delay = value;
+ }
+
+ bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(bsp_priv->grf))
+ dev_err(&pdev->dev, "Missing rockchip,grf property\n");
+
+ bsp_priv->phyirq_io =
+ of_get_named_gpio_flags(dev->of_node,
+ "phyirq-gpio", 0, &flags);
+ bsp_priv->phyirq_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ bsp_priv->reset_io =
+ of_get_named_gpio_flags(dev->of_node,
+ "reset-gpio", 0, &flags);
+ bsp_priv->reset_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ bsp_priv->power_io =
+ of_get_named_gpio_flags(dev->of_node, "power-gpio", 0, &flags);
+ bsp_priv->power_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ /*power*/
+ if (!gpio_is_valid(bsp_priv->power_io)) {
+ pr_err("%s: Failed to get GPIO %s.\n",
+ __func__, "power-gpio");
+ } else {
+ ret = gpio_request(bsp_priv->power_io, GPIO_PHY_POWER);
+ if (ret)
+ pr_err("%s: ERROR: Failed to request GPIO %s.\n",
+ __func__, GPIO_PHY_POWER);
+ }
+
+ if (!gpio_is_valid(bsp_priv->reset_io)) {
+ pr_err("%s: ERROR: Get reset-gpio failed.\n", __func__);
+ } else {
+ ret = gpio_request(bsp_priv->reset_io, GPIO_PHY_RESET);
+ if (ret)
+ pr_err("%s: ERROR: Failed to request GPIO %s.\n",
+ __func__, GPIO_PHY_RESET);
+ }
+
+ if (bsp_priv->phyirq_io > 0) {
+ struct plat_stmmacenet_data *plat_dat;
+
+ pr_info("PHY irq in use\n");
+ ret = gpio_request(bsp_priv->phyirq_io, GPIO_PHY_IRQ);
+ if (ret < 0) {
+ pr_warn("%s: Failed to request GPIO %s\n",
+ __func__, GPIO_PHY_IRQ);
+ goto goon;
+ }
+
+ ret = gpio_direction_input(bsp_priv->phyirq_io);
+ if (ret < 0) {
+ pr_err("%s, Failed to set input for GPIO %s\n",
+ __func__, GPIO_PHY_IRQ);
+ gpio_free(bsp_priv->phyirq_io);
+ goto goon;
+ }
+
+ irq = gpio_to_irq(bsp_priv->phyirq_io);
+ if (irq < 0) {
+ ret = irq;
+ pr_err("Failed to set irq for %s\n",
+ GPIO_PHY_IRQ);
+ gpio_free(bsp_priv->phyirq_io);
+ goto goon;
+ }
+
+ plat_dat = dev_get_platdata(&pdev->dev);
+ if (plat_dat)
+ plat_dat->mdio_bus_data->probed_phy_irq = irq;
+ else
+ pr_err("%s: plat_data is NULL\n", __func__);
+ }
+
+goon:
+ /*rmii or rgmii*/
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) {
+ pr_info("%s: init for RGMII\n", __func__);
+ set_to_rgmii(bsp_priv, bsp_priv->tx_delay, bsp_priv->rx_delay);
+ } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) {
+ pr_info("%s: init for RMII\n", __func__);
+ set_to_rmii(bsp_priv);
+ } else {
+ pr_err("%s: ERROR: NO interface defined!\n", __func__);
+ }
+
+ bsp_priv->pdev = pdev;
+
+ gmac_clk_init(bsp_priv);
+
+ return bsp_priv;
+}
+
+static int rk_gmac_init(struct platform_device *pdev, void *priv)
+{
+ struct rk_priv_data *bsp_priv = priv;
+ int ret;
+
+ ret = phy_power_on(bsp_priv, true);
+ if (ret)
+ return ret;
+
+ ret = gmac_clk_enable(bsp_priv, true);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void rk_gmac_exit(struct platform_device *pdev, void *priv)
+{
+ struct rk_priv_data *gmac = priv;
+
+ phy_power_on(gmac, false);
+ gmac_clk_enable(gmac, false);
+}
+
+static void rk_fix_speed(void *priv, unsigned int speed)
+{
+ struct rk_priv_data *bsp_priv = priv;
+
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII)
+ set_rgmii_speed(bsp_priv, speed);
+ else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
+ set_rmii_speed(bsp_priv, speed);
+ else
+ pr_err("unsupported interface %d", bsp_priv->phy_iface);
+}
+
+const struct stmmac_of_data rk_gmac_data = {
+ .has_gmac = 1,
+ .tx_coe = 1,
+ .fix_mac_speed = rk_fix_speed,
+ .setup = rk_gmac_setup,
+ .init = rk_gmac_init,
+ .exit = rk_gmac_exit,
+};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 15814b7..b4dee96 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -33,6 +33,7 @@
static const struct of_device_id stmmac_dt_ids[] = {
/* SoC specific glue layers should come before generic bindings */
+ { .compatible = "rockchip,rk3288-gmac", .data = &rk_gmac_data},
{ .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data},
{ .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},
{ .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data},
@@ -291,6 +292,8 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ pdev->dev.platform_data = plat_dat;
+
ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
if (ret) {
pr_err("%s: main dt probe failed", __func__);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
index 25dd1f7..32a0516 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
@@ -24,5 +24,6 @@ extern const struct stmmac_of_data sun7i_gmac_data;
extern const struct stmmac_of_data stih4xx_dwmac_data;
extern const struct stmmac_of_data stid127_dwmac_data;
extern const struct stmmac_of_data socfpga_gmac_data;
+extern const struct stmmac_of_data rk_gmac_data;
#endif /* __STMMAC_PLATFORM_H__ */
--
1.7.9.5
^ permalink raw reply related
* [PATCH 0/4] support GMAC driver for RK3288
From: Roger Chen @ 2014-11-25 9:07 UTC (permalink / raw)
To: heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, kever.yang,
mark.yao, eddie.cai, roger.chen
Roger Chen (4):
patch1: add driver for Rockchip RK3288 SoCs integrated GMAC
patch2: modify CRU config for Rockchip RK3288 SoCs integrated GMAC
patch3: dts: add gmac info for rk3288
patch4: dts: enable gmac on RK3288 evb board
tested on rk3288 SDK board:
busybox ifconfig eth0 up
busybox ifconfig eth0 192.168.1.111
ping 192.168.1.1
--
1.7.9.5
^ permalink raw reply
* [PATCH 1/4] GMAC: add driver for Rockchip RK3288 SoCs integrated GMAC
From: Roger Chen @ 2014-11-25 9:07 UTC (permalink / raw)
To: heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, kever.yang,
mark.yao, eddie.cai, roger.chen
In-Reply-To: <1416906460-26078-1-git-send-email-roger.chen@rock-chips.com>
This driver is based on stmmac driver.
Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
---
drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +-
drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 629 ++++++++++++++++++++
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 +
.../net/ethernet/stmicro/stmmac/stmmac_platform.h | 1 +
4 files changed, 634 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index ac4d562..73c2715 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \
- dwmac-sti.o dwmac-socfpga.o
+ dwmac-sti.o dwmac-socfpga.o dwmac-rk.o
obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
stmmac-pci-objs:= stmmac_pci.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
new file mode 100644
index 0000000..9e50b37
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -0,0 +1,629 @@
+/**
+ * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer
+ *
+ * Copyright (C) 2014 Chen-Zhi (Roger Chen)
+ *
+ * Chen-Zhi (Roger Chen) <roger.chen@rock-chips.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/stmmac.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/of_net.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+struct rk_priv_data {
+ struct platform_device *pdev;
+ int phy_iface;
+ bool power_ctrl_by_pmu;
+ char pmu_regulator[32];
+ int pmu_enable_level;
+
+ int power_io;
+ int power_io_level;
+ int reset_io;
+ int reset_io_level;
+ int phyirq_io;
+ int phyirq_io_level;
+
+ bool clk_enabled;
+ bool clock_input;
+
+ struct clk *clk_mac;
+ struct clk *clk_mac_pll;
+ struct clk *gmac_clkin;
+ struct clk *mac_clk_rx;
+ struct clk *mac_clk_tx;
+ struct clk *clk_mac_ref;
+ struct clk *clk_mac_refout;
+ struct clk *aclk_mac;
+ struct clk *pclk_mac;
+
+ int tx_delay;
+ int rx_delay;
+
+ struct regmap *grf;
+};
+
+#define RK3288_GRF_SOC_CON1 0x0248
+#define RK3288_GRF_SOC_CON3 0x0250
+#define RK3288_GRF_GPIO3D_E 0x01ec
+#define RK3288_GRF_GPIO4A_E 0x01f0
+#define RK3288_GRF_GPIO4B_E 0x01f4
+
+/*RK3288_GRF_SOC_CON1*/
+#define GMAC_PHY_INTF_SEL_RGMII ((0x01C0 << 16) | (0x0040))
+#define GMAC_PHY_INTF_SEL_RMII ((0x01C0 << 16) | (0x0100))
+#define GMAC_FLOW_CTRL ((0x0200 << 16) | (0x0200))
+#define GMAC_FLOW_CTRL_CLR ((0x0200 << 16) | (0x0000))
+#define GMAC_SPEED_10M ((0x0400 << 16) | (0x0000))
+#define GMAC_SPEED_100M ((0x0400 << 16) | (0x0400))
+#define GMAC_RMII_CLK_25M ((0x0800 << 16) | (0x0800))
+#define GMAC_RMII_CLK_2_5M ((0x0800 << 16) | (0x0000))
+#define GMAC_CLK_125M ((0x3000 << 16) | (0x0000))
+#define GMAC_CLK_25M ((0x3000 << 16) | (0x3000))
+#define GMAC_CLK_2_5M ((0x3000 << 16) | (0x2000))
+#define GMAC_RMII_MODE ((0x4000 << 16) | (0x4000))
+#define GMAC_RMII_MODE_CLR ((0x4000 << 16) | (0x0000))
+
+/*RK3288_GRF_SOC_CON3*/
+#define GMAC_TXCLK_DLY_ENABLE ((0x4000 << 16) | (0x4000))
+#define GMAC_TXCLK_DLY_DISABLE ((0x4000 << 16) | (0x0000))
+#define GMAC_RXCLK_DLY_ENABLE ((0x8000 << 16) | (0x8000))
+#define GMAC_RXCLK_DLY_DISABLE ((0x8000 << 16) | (0x0000))
+#define GMAC_CLK_RX_DL_CFG(val) ((0x3F80 << 16) | (val<<7))
+#define GMAC_CLK_TX_DL_CFG(val) ((0x007F << 16) | (val))
+
+static void set_to_rgmii(struct rk_priv_data *bsp_priv,
+ int tx_delay, int rx_delay)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_PHY_INTF_SEL_RGMII);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_MODE_CLR);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_RXCLK_DLY_ENABLE);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_TXCLK_DLY_ENABLE);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_CLK_RX_DL_CFG(rx_delay));
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
+ GMAC_CLK_TX_DL_CFG(tx_delay));
+
+ regmap_write(bsp_priv->grf, RK3288_GRF_GPIO3D_E, 0xFFFFFFFF);
+ regmap_write(bsp_priv->grf, RK3288_GRF_GPIO4B_E,
+ 0x3<<2<<16 | 0x3<<2);
+ regmap_write(bsp_priv->grf, RK3288_GRF_GPIO4A_E, 0xFFFFFFFF);
+
+ pr_info("%s: tx delay=0x%x; rx delay=0x%x;\n",
+ __func__, tx_delay, rx_delay);
+}
+
+static void set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_PHY_INTF_SEL_RMII);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_MODE);
+}
+
+static void set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ if (speed == 10)
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_2_5M);
+ else if (speed == 100)
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_25M);
+ else if (speed == 1000)
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_125M);
+ else
+ pr_err("unknown speed value for RGMII! speed=%d", speed);
+}
+
+static void set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+ if (IS_ERR(bsp_priv->grf)) {
+ pr_err("%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ if (speed == 10) {
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_CLK_2_5M);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_SPEED_10M);
+ } else if (speed == 100) {
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_RMII_CLK_25M);
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
+ GMAC_SPEED_100M);
+ } else {
+ pr_err("unknown speed value for RMII! speed=%d", speed);
+ }
+}
+
+#define MAC_CLK_RX "mac_clk_rx"
+#define MAC_CLK_TX "mac_clk_tx"
+#define CLK_MAC_REF "clk_mac_ref"
+#define CLK_MAC_REF_OUT "clk_mac_refout"
+#define CLK_MAC_PLL "clk_mac_pll"
+#define ACLK_MAC "aclk_mac"
+#define PCLK_MAC "pclk_mac"
+#define MAC_CLKIN "ext_gmac"
+#define CLK_MAC "stmmaceth"
+
+int gmac_clk_init(struct rk_priv_data *bsp_priv)
+{
+ struct device *dev = &bsp_priv->pdev->dev;
+
+ bsp_priv->clk_enabled = false;
+
+ bsp_priv->mac_clk_rx = clk_get(dev, MAC_CLK_RX);
+ if (IS_ERR(bsp_priv->mac_clk_rx))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, MAC_CLK_RX);
+
+ bsp_priv->mac_clk_tx = clk_get(dev, MAC_CLK_TX);
+ if (IS_ERR(bsp_priv->mac_clk_tx))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, MAC_CLK_TX);
+
+ bsp_priv->clk_mac_ref = clk_get(dev, CLK_MAC_REF);
+ if (IS_ERR(bsp_priv->clk_mac_ref))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, CLK_MAC_REF);
+
+ bsp_priv->clk_mac_refout = clk_get(dev, CLK_MAC_REF_OUT);
+ if (IS_ERR(bsp_priv->clk_mac_refout))
+ pr_warn("%s: warning:cannot get clock %s\n",
+ __func__, CLK_MAC_REF_OUT);
+
+ bsp_priv->aclk_mac = clk_get(dev, ACLK_MAC);
+ if (IS_ERR(bsp_priv->aclk_mac))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, ACLK_MAC);
+
+ bsp_priv->pclk_mac = clk_get(dev, PCLK_MAC);
+ if (IS_ERR(bsp_priv->pclk_mac))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, PCLK_MAC);
+
+ bsp_priv->clk_mac_pll = clk_get(dev, CLK_MAC_PLL);
+ if (IS_ERR(bsp_priv->clk_mac_pll))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, CLK_MAC_PLL);
+
+ bsp_priv->gmac_clkin = clk_get(dev, MAC_CLKIN);
+ if (IS_ERR(bsp_priv->gmac_clkin))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, MAC_CLKIN);
+
+ bsp_priv->clk_mac = clk_get(dev, CLK_MAC);
+ if (IS_ERR(bsp_priv->clk_mac))
+ pr_warn("%s: warning: cannot get clock %s\n",
+ __func__, CLK_MAC);
+
+ if (bsp_priv->clock_input) {
+ pr_info("%s: clock input from PHY\n", __func__);
+ } else {
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
+ clk_set_rate(bsp_priv->clk_mac_pll, 50000000);
+
+ clk_set_parent(bsp_priv->clk_mac, bsp_priv->clk_mac_pll);
+ }
+
+ return 0;
+}
+
+static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
+{
+ int phy_iface = phy_iface = bsp_priv->phy_iface;
+
+ if (enable) {
+ if (!bsp_priv->clk_enabled) {
+ if (phy_iface == PHY_INTERFACE_MODE_RMII) {
+ if (!IS_ERR(bsp_priv->mac_clk_rx))
+ clk_prepare_enable(
+ bsp_priv->mac_clk_rx);
+
+ if (!IS_ERR(bsp_priv->clk_mac_ref))
+ clk_prepare_enable(
+ bsp_priv->clk_mac_ref);
+
+ if (!IS_ERR(bsp_priv->clk_mac_refout))
+ clk_prepare_enable(
+ bsp_priv->clk_mac_refout);
+ }
+
+ if (!IS_ERR(bsp_priv->aclk_mac))
+ clk_prepare_enable(bsp_priv->aclk_mac);
+
+ if (!IS_ERR(bsp_priv->pclk_mac))
+ clk_prepare_enable(bsp_priv->pclk_mac);
+
+ if (!IS_ERR(bsp_priv->mac_clk_tx))
+ clk_prepare_enable(bsp_priv->mac_clk_tx);
+
+ /**
+ * if (!IS_ERR(bsp_priv->clk_mac))
+ * clk_prepare_enable(bsp_priv->clk_mac);
+ */
+ mdelay(5);
+ bsp_priv->clk_enabled = true;
+ }
+ } else {
+ if (bsp_priv->clk_enabled) {
+ if (phy_iface == PHY_INTERFACE_MODE_RMII) {
+ if (!IS_ERR(bsp_priv->mac_clk_rx))
+ clk_disable_unprepare(
+ bsp_priv->mac_clk_rx);
+
+ if (!IS_ERR(bsp_priv->clk_mac_ref))
+ clk_disable_unprepare(
+ bsp_priv->clk_mac_ref);
+
+ if (!IS_ERR(bsp_priv->clk_mac_refout))
+ clk_disable_unprepare(
+ bsp_priv->clk_mac_refout);
+ }
+
+ if (!IS_ERR(bsp_priv->aclk_mac))
+ clk_disable_unprepare(bsp_priv->aclk_mac);
+
+ if (!IS_ERR(bsp_priv->pclk_mac))
+ clk_disable_unprepare(bsp_priv->pclk_mac);
+
+ if (!IS_ERR(bsp_priv->mac_clk_tx))
+ clk_disable_unprepare(bsp_priv->mac_clk_tx);
+ /**
+ * if (!IS_ERR(bsp_priv->clk_mac))
+ * clk_disable_unprepare(bsp_priv->clk_mac);
+ */
+ bsp_priv->clk_enabled = false;
+ }
+ }
+
+ return 0;
+}
+
+static int power_on_by_pmu(struct rk_priv_data *bsp_priv, bool enable)
+{
+ struct regulator *ldo;
+ char *ldostr = bsp_priv->pmu_regulator;
+ int ret;
+
+ if (!ldostr) {
+ pr_err("%s: no ldo found\n", __func__);
+ return -1;
+ }
+
+ ldo = regulator_get(NULL, ldostr);
+ if (!ldo) {
+ pr_err("\n%s get ldo %s failed\n", __func__, ldostr);
+ } else {
+ if (enable) {
+ if (!regulator_is_enabled(ldo)) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if (ret != 0)
+ pr_err("%s: fail to enable %s\n",
+ __func__, ldostr);
+ else
+ pr_info("turn on ldo done.\n");
+ } else {
+ pr_warn("%s is enabled before enable", ldostr);
+ }
+ } else {
+ if (regulator_is_enabled(ldo)) {
+ ret = regulator_disable(ldo);
+ if (ret != 0)
+ pr_err("%s: fail to disable %s\n",
+ __func__, ldostr);
+ else
+ pr_info("turn off ldo done.\n");
+ } else {
+ pr_warn("%s is disabled before disable",
+ ldostr);
+ }
+ }
+ regulator_put(ldo);
+ }
+
+ return 0;
+}
+
+static int power_on_by_gpio(struct rk_priv_data *bsp_priv, bool enable)
+{
+ if (enable) {
+ /*power on*/
+ if (gpio_is_valid(bsp_priv->power_io))
+ gpio_direction_output(bsp_priv->power_io,
+ bsp_priv->power_io_level);
+ } else {
+ /*power off*/
+ if (gpio_is_valid(bsp_priv->power_io))
+ gpio_direction_output(bsp_priv->power_io,
+ !bsp_priv->power_io_level);
+ }
+
+ return 0;
+}
+
+static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable)
+{
+ int ret = -1;
+
+ pr_info("Ethernet PHY power %s\n", enable == 1 ? "on" : "off");
+
+ if (bsp_priv->power_ctrl_by_pmu)
+ ret = power_on_by_pmu(bsp_priv, enable);
+ else
+ ret = power_on_by_gpio(bsp_priv, enable);
+
+ if (enable) {
+ /*reset*/
+ if (gpio_is_valid(bsp_priv->reset_io)) {
+ gpio_direction_output(bsp_priv->reset_io,
+ bsp_priv->reset_io_level);
+ mdelay(5);
+ gpio_direction_output(bsp_priv->reset_io,
+ !bsp_priv->reset_io_level);
+ }
+ mdelay(30);
+
+ } else {
+ /*pull down reset*/
+ if (gpio_is_valid(bsp_priv->reset_io)) {
+ gpio_direction_output(bsp_priv->reset_io,
+ bsp_priv->reset_io_level);
+ }
+ }
+
+ return ret;
+}
+
+#define GPIO_PHY_POWER "gmac_phy_power"
+#define GPIO_PHY_RESET "gmac_phy_reset"
+#define GPIO_PHY_IRQ "gmac_phy_irq"
+
+static void *rk_gmac_setup(struct platform_device *pdev)
+{
+ struct rk_priv_data *bsp_priv;
+ struct device *dev = &pdev->dev;
+ enum of_gpio_flags flags;
+ int ret;
+ const char *strings = NULL;
+ int value;
+ int irq;
+
+ bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL);
+ if (!bsp_priv)
+ return ERR_PTR(-ENOMEM);
+
+ bsp_priv->phy_iface = of_get_phy_mode(dev->of_node);
+
+ ret = of_property_read_string(dev->of_node, "pmu_regulator", &strings);
+ if (ret) {
+ pr_err("%s: Can not read property: pmu_regulator.\n", __func__);
+ bsp_priv->power_ctrl_by_pmu = false;
+ } else {
+ pr_info("%s: ethernet phy power controlled by pmu(%s).\n",
+ __func__, strings);
+ bsp_priv->power_ctrl_by_pmu = true;
+ strcpy(bsp_priv->pmu_regulator, strings);
+ }
+
+ ret = of_property_read_u32(dev->of_node, "pmu_enable_level", &value);
+ if (ret) {
+ pr_err("%s: Can not read property: pmu_enable_level.\n",
+ __func__);
+ bsp_priv->power_ctrl_by_pmu = false;
+ } else {
+ pr_info("%s: PHY power controlled by pmu(level = %s).\n",
+ __func__, (value == 1) ? "HIGH" : "LOW");
+ bsp_priv->power_ctrl_by_pmu = true;
+ bsp_priv->pmu_enable_level = value;
+ }
+
+ ret = of_property_read_string(dev->of_node, "clock_in_out", &strings);
+ if (ret) {
+ pr_err("%s: Can not read property: clock_in_out.\n", __func__);
+ bsp_priv->clock_input = true;
+ } else {
+ pr_info("%s: clock input or output? (%s).\n",
+ __func__, strings);
+ if (!strcmp(strings, "input"))
+ bsp_priv->clock_input = true;
+ else
+ bsp_priv->clock_input = false;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "tx_delay", &value);
+ if (ret) {
+ bsp_priv->tx_delay = 0x30;
+ pr_err("%s: Can not read property: tx_delay.", __func__);
+ pr_err("%s: set tx_delay to 0x%x\n",
+ __func__, bsp_priv->tx_delay);
+ } else {
+ pr_info("%s: TX delay(0x%x).\n", __func__, value);
+ bsp_priv->tx_delay = value;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "rx_delay", &value);
+ if (ret) {
+ bsp_priv->rx_delay = 0x10;
+ pr_err("%s: Can not read property: rx_delay.", __func__);
+ pr_err("%s: set rx_delay to 0x%x\n",
+ __func__, bsp_priv->rx_delay);
+ } else {
+ pr_info("%s: RX delay(0x%x).\n", __func__, value);
+ bsp_priv->rx_delay = value;
+ }
+
+ bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(bsp_priv->grf))
+ dev_err(&pdev->dev, "Missing rockchip,grf property\n");
+
+ bsp_priv->phyirq_io =
+ of_get_named_gpio_flags(dev->of_node,
+ "phyirq-gpio", 0, &flags);
+ bsp_priv->phyirq_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ bsp_priv->reset_io =
+ of_get_named_gpio_flags(dev->of_node,
+ "reset-gpio", 0, &flags);
+ bsp_priv->reset_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ bsp_priv->power_io =
+ of_get_named_gpio_flags(dev->of_node, "power-gpio", 0, &flags);
+ bsp_priv->power_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ /*power*/
+ if (!gpio_is_valid(bsp_priv->power_io)) {
+ pr_err("%s: Failed to get GPIO %s.\n",
+ __func__, "power-gpio");
+ } else {
+ ret = gpio_request(bsp_priv->power_io, GPIO_PHY_POWER);
+ if (ret)
+ pr_err("%s: ERROR: Failed to request GPIO %s.\n",
+ __func__, GPIO_PHY_POWER);
+ }
+
+ if (!gpio_is_valid(bsp_priv->reset_io)) {
+ pr_err("%s: ERROR: Get reset-gpio failed.\n", __func__);
+ } else {
+ ret = gpio_request(bsp_priv->reset_io, GPIO_PHY_RESET);
+ if (ret)
+ pr_err("%s: ERROR: Failed to request GPIO %s.\n",
+ __func__, GPIO_PHY_RESET);
+ }
+
+ if (bsp_priv->phyirq_io > 0) {
+ struct plat_stmmacenet_data *plat_dat;
+
+ pr_info("PHY irq in use\n");
+ ret = gpio_request(bsp_priv->phyirq_io, GPIO_PHY_IRQ);
+ if (ret < 0) {
+ pr_warn("%s: Failed to request GPIO %s\n",
+ __func__, GPIO_PHY_IRQ);
+ goto goon;
+ }
+
+ ret = gpio_direction_input(bsp_priv->phyirq_io);
+ if (ret < 0) {
+ pr_err("%s, Failed to set input for GPIO %s\n",
+ __func__, GPIO_PHY_IRQ);
+ gpio_free(bsp_priv->phyirq_io);
+ goto goon;
+ }
+
+ irq = gpio_to_irq(bsp_priv->phyirq_io);
+ if (irq < 0) {
+ ret = irq;
+ pr_err("Failed to set irq for %s\n",
+ GPIO_PHY_IRQ);
+ gpio_free(bsp_priv->phyirq_io);
+ goto goon;
+ }
+
+ plat_dat = dev_get_platdata(&pdev->dev);
+ if (plat_dat)
+ plat_dat->mdio_bus_data->probed_phy_irq = irq;
+ else
+ pr_err("%s: plat_data is NULL\n", __func__);
+ }
+
+goon:
+ /*rmii or rgmii*/
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) {
+ pr_info("%s: init for RGMII\n", __func__);
+ set_to_rgmii(bsp_priv, bsp_priv->tx_delay, bsp_priv->rx_delay);
+ } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) {
+ pr_info("%s: init for RMII\n", __func__);
+ set_to_rmii(bsp_priv);
+ } else {
+ pr_err("%s: ERROR: NO interface defined!\n", __func__);
+ }
+
+ bsp_priv->pdev = pdev;
+
+ gmac_clk_init(bsp_priv);
+
+ return bsp_priv;
+}
+
+static int rk_gmac_init(struct platform_device *pdev, void *priv)
+{
+ struct rk_priv_data *bsp_priv = priv;
+ int ret;
+
+ ret = phy_power_on(bsp_priv, true);
+ if (ret)
+ return ret;
+
+ ret = gmac_clk_enable(bsp_priv, true);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void rk_gmac_exit(struct platform_device *pdev, void *priv)
+{
+ struct rk_priv_data *gmac = priv;
+
+ phy_power_on(gmac, false);
+ gmac_clk_enable(gmac, false);
+}
+
+static void rk_fix_speed(void *priv, unsigned int speed)
+{
+ struct rk_priv_data *bsp_priv = priv;
+
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII)
+ set_rgmii_speed(bsp_priv, speed);
+ else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
+ set_rmii_speed(bsp_priv, speed);
+ else
+ pr_err("unsupported interface %d", bsp_priv->phy_iface);
+}
+
+const struct stmmac_of_data rk_gmac_data = {
+ .has_gmac = 1,
+ .tx_coe = 1,
+ .fix_mac_speed = rk_fix_speed,
+ .setup = rk_gmac_setup,
+ .init = rk_gmac_init,
+ .exit = rk_gmac_exit,
+};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 15814b7..b4dee96 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -33,6 +33,7 @@
static const struct of_device_id stmmac_dt_ids[] = {
/* SoC specific glue layers should come before generic bindings */
+ { .compatible = "rockchip,rk3288-gmac", .data = &rk_gmac_data},
{ .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data},
{ .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},
{ .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data},
@@ -291,6 +292,8 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ pdev->dev.platform_data = plat_dat;
+
ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
if (ret) {
pr_err("%s: main dt probe failed", __func__);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
index 25dd1f7..32a0516 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
@@ -24,5 +24,6 @@ extern const struct stmmac_of_data sun7i_gmac_data;
extern const struct stmmac_of_data stih4xx_dwmac_data;
extern const struct stmmac_of_data stid127_dwmac_data;
extern const struct stmmac_of_data socfpga_gmac_data;
+extern const struct stmmac_of_data rk_gmac_data;
#endif /* __STMMAC_PLATFORM_H__ */
--
1.7.9.5
^ permalink raw reply related
* [PATCH 2/4] GMAC: modify CRU config for Rockchip RK3288 SoCs integrated GMAC
From: Roger Chen @ 2014-11-25 9:08 UTC (permalink / raw)
To: heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, kever.yang,
mark.yao, eddie.cai, roger.chen
modify CRU config for GMAC driver
Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
---
drivers/clk/rockchip/clk-rk3288.c | 14 +++++++-------
include/dt-bindings/clock/rk3288-cru.h | 4 ++++
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 2327829..60237dc 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -187,7 +187,7 @@ PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" };
PNAME(mux_uart3_p) = { "uart3_src", "uart3_frac", "xin24m" };
PNAME(mux_uart4_p) = { "uart4_src", "uart4_frac", "xin24m" };
PNAME(mux_cif_out_p) = { "cif_src", "xin24m" };
-PNAME(mux_macref_p) = { "mac_src", "ext_gmac" };
+PNAME(mux_mac_p) = { "mac_pll_src", "ext_gmac" };
PNAME(mux_hsadcout_p) = { "hsadc_src", "ext_hsadc" };
PNAME(mux_edp_24m_p) = { "ext_edp_24m", "xin24m" };
PNAME(mux_tspout_p) = { "cpll", "gpll", "npll", "xin27m" };
@@ -560,18 +560,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, 0,
RK3288_CLKSEL_CON(3), 8, 2, MFLAGS),
- COMPOSITE(0, "mac_src", mux_pll_src_npll_cpll_gpll_p, 0,
+ COMPOSITE(SCLK_MAC_PLL, "mac_pll_src", mux_pll_src_npll_cpll_gpll_p, 0,
RK3288_CLKSEL_CON(21), 0, 2, MFLAGS, 8, 5, DFLAGS,
RK3288_CLKGATE_CON(2), 5, GFLAGS),
- MUX(0, "macref", mux_macref_p, 0,
+ MUX(SCLK_MAC, "mac_clk", mux_mac_p, 0,
RK3288_CLKSEL_CON(21), 4, 1, MFLAGS),
- GATE(0, "sclk_macref_out", "macref", 0,
+ GATE(SCLK_MACREF_OUT, "sclk_macref_out", "mac_clk", 0,
RK3288_CLKGATE_CON(5), 3, GFLAGS),
- GATE(SCLK_MACREF, "sclk_macref", "macref", 0,
+ GATE(SCLK_MACREF, "sclk_macref", "mac_clk", 0,
RK3288_CLKGATE_CON(5), 2, GFLAGS),
- GATE(SCLK_MAC_RX, "sclk_mac_rx", "macref", 0,
+ GATE(SCLK_MAC_RX, "sclk_mac_rx", "mac_clk", 0,
RK3288_CLKGATE_CON(5), 0, GFLAGS),
- GATE(SCLK_MAC_TX, "sclk_mac_tx", "macref", 0,
+ GATE(SCLK_MAC_TX, "sclk_mac_tx", "mac_clk", 0,
RK3288_CLKGATE_CON(5), 1, GFLAGS),
COMPOSITE(0, "hsadc_src", mux_pll_src_cpll_gpll_p, 0,
diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h
index 100a08c..f9496f5 100644
--- a/include/dt-bindings/clock/rk3288-cru.h
+++ b/include/dt-bindings/clock/rk3288-cru.h
@@ -72,6 +72,10 @@
#define SCLK_HEVC_CABAC 111
#define SCLK_HEVC_CORE 112
+#define SCLK_MAC_PLL 150
+#define SCLK_MAC 151
+#define SCLK_MACREF_OUT 152
+
#define DCLK_VOP0 190
#define DCLK_VOP1 191
--
1.7.9.5
^ permalink raw reply related
* [PATCH 3/4] GMAC: dts: add gmac info for rk3288
From: Roger Chen @ 2014-11-25 9:08 UTC (permalink / raw)
To: heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, kever.yang,
mark.yao, eddie.cai, roger.chen
add gmac info in rk3288.dtsi for GMAC driver
Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
---
arch/arm/boot/dts/rk3288.dtsi | 59 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 0f50d5d..949675d 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -137,6 +137,13 @@
#clock-cells = <0>;
};
+ ext_gmac: external-gmac-clock {
+ compatible = "fixed-clock";
+ clock-frequency = <125000000>;
+ clock-output-names = "ext_gmac";
+ #clock-cells = <0>;
+ };
+
timer {
compatible = "arm,armv7-timer";
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
@@ -490,6 +497,25 @@
reg = <0xff740000 0x1000>;
};
+ gmac: eth@ff290000 {
+ compatible = "rockchip,rk3288-gmac";
+ reg = <0xff290000 0x10000>;
+ interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; /*irq=59*/
+ interrupt-names = "macirq";
+ rockchip,grf = <&grf>;
+ clocks = <&cru SCLK_MAC>, <&cru SCLK_MAC_PLL>,
+ <&cru SCLK_MAC_RX>, <&cru SCLK_MAC_TX>,
+ <&cru SCLK_MACREF>, <&cru SCLK_MACREF_OUT>,
+ <&cru ACLK_GMAC>, <&cru PCLK_GMAC>;
+ clock-names = "stmmaceth", "clk_mac_pll",
+ "mac_clk_rx", "mac_clk_tx",
+ "clk_mac_ref", "clk_mac_refout",
+ "aclk_mac", "pclk_mac";
+ phy-mode = "rgmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&rgmii_pin /*&rmii_pin*/>;
+ };
+
cru: clock-controller@ff760000 {
compatible = "rockchip,rk3288-cru";
reg = <0xff760000 0x1000>;
@@ -1040,5 +1066,38 @@
rockchip,pins = <7 23 3 &pcfg_pull_none>;
};
};
+
+ gmac {
+ rgmii_pin: rgmii-pins {
+ rockchip,pins = <3 30 3 &pcfg_pull_none>,
+ <3 31 3 &pcfg_pull_none>,
+ <3 26 3 &pcfg_pull_none>,
+ <3 27 3 &pcfg_pull_none>,
+ <3 28 3 &pcfg_pull_none>,
+ <3 29 3 &pcfg_pull_none>,
+ <3 24 3 &pcfg_pull_none>,
+ <3 25 3 &pcfg_pull_none>,
+ <4 0 3 &pcfg_pull_none>,
+ <4 5 3 &pcfg_pull_none>,
+ <4 6 3 &pcfg_pull_none>,
+ <4 9 3 &pcfg_pull_none>,
+ <4 4 3 &pcfg_pull_none>,
+ <4 1 3 &pcfg_pull_none>,
+ <4 3 3 &pcfg_pull_none>;
+ };
+
+ rmii_pin: rmii-pins {
+ rockchip,pins = <3 30 3 &pcfg_pull_none>,
+ <3 31 3 &pcfg_pull_none>,
+ <3 28 3 &pcfg_pull_none>,
+ <3 29 3 &pcfg_pull_none>,
+ <4 0 3 &pcfg_pull_none>,
+ <4 5 3 &pcfg_pull_none>,
+ <4 4 3 &pcfg_pull_none>,
+ <4 1 3 &pcfg_pull_none>,
+ <4 2 3 &pcfg_pull_none>,
+ <4 3 3 &pcfg_pull_none>;
+ };
+ };
};
};
--
1.7.9.5
^ permalink raw reply related
* [PATCH 4/4] GMAC: dts: enable gmac on RK3288 evb board
From: Roger Chen @ 2014-11-25 9:09 UTC (permalink / raw)
To: heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, kever.yang,
mark.yao, eddie.cai, roger.chen
enable gmac in rk3288-evb-rk808.dts
Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
---
arch/arm/boot/dts/rk3288-evb-rk808.dts | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-evb-rk808.dts b/arch/arm/boot/dts/rk3288-evb-rk808.dts
index d8c775e6..168b4fa 100644
--- a/arch/arm/boot/dts/rk3288-evb-rk808.dts
+++ b/arch/arm/boot/dts/rk3288-evb-rk808.dts
@@ -152,3 +152,25 @@
};
};
};
+
+&ext_gmac {
+ clock-frequency = <125000000>;
+ clock-output-names = "ext_gmac";
+};
+
+&gmac {
+ //pmu_regulator = "act_ldo5";
+ //pmu_enable_level = <1>; //1->HIGH, 0->LOW
+ power-gpio = <&gpio0 6 GPIO_ACTIVE_HIGH>;
+ reset-gpio = <&gpio4 7 GPIO_ACTIVE_LOW>;
+ //phyirq-gpio = <&gpio4 2 GPIO_ACTIVE_LOW>;
+ phy-mode = "rgmii";
+ clock_in_out = "input";
+ assigned-clocks = <&cru SCLK_MAC>;
+ assigned-clock-parents = <&ext_gmac>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&rgmii_pin>; /*&rmii_pin*/
+ tx_delay = <0x30>;
+ rx_delay = <0x10>;
+ status = "ok";
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCH][v2] mdio-mux-gpio: Use GPIO descriptor interface and new gpiod_set_array function
From: Rojhalat Ibrahim @ 2014-11-25 9:31 UTC (permalink / raw)
To: linux-gpio@vger.kernel.org, netdev
Cc: Linus Walleij, Alexandre Courbot, David Miller, Florian Fainelli,
David Daney
Convert mdio-mux-gpio to the GPIO descriptor interface and use the new
gpiod_set_array function to set all output signals simultaneously.
Signed-off-by: Rojhalat Ibrahim <imr@rtschenk.de>
Acked-by: David S. Miller <davem@davemloft.net>
--
This patch depends on the gpiod_set_array function, which is available in
the linux-gpio devel tree.
v2: fix gpiod_get_index usage
drivers/net/phy/mdio-mux-gpio.c | 38 ++++++++++++++------------------------
1 file changed, 14 insertions(+), 24 deletions(-)
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index 0966951..1167c5b 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -14,13 +14,13 @@
#include <linux/mdio-mux.h>
#include <linux/of_gpio.h>
-#define DRV_VERSION "1.0"
+#define DRV_VERSION "1.1"
#define DRV_DESCRIPTION "GPIO controlled MDIO bus multiplexer driver"
#define MDIO_MUX_GPIO_MAX_BITS 8
struct mdio_mux_gpio_state {
- int gpio[MDIO_MUX_GPIO_MAX_BITS];
+ struct gpio_desc *gpio[MDIO_MUX_GPIO_MAX_BITS];
unsigned int num_gpios;
void *mux_handle;
};
@@ -28,29 +28,23 @@ struct mdio_mux_gpio_state {
static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
void *data)
{
- int change;
+ int values[MDIO_MUX_GPIO_MAX_BITS];
unsigned int n;
struct mdio_mux_gpio_state *s = data;
if (current_child == desired_child)
return 0;
- change = current_child == -1 ? -1 : current_child ^ desired_child;
-
for (n = 0; n < s->num_gpios; n++) {
- if (change & 1)
- gpio_set_value_cansleep(s->gpio[n],
- (desired_child & 1) != 0);
- change >>= 1;
- desired_child >>= 1;
+ values[n] = (desired_child >> n) & 1;
}
+ gpiod_set_array_cansleep(s->num_gpios, s->gpio, values);
return 0;
}
static int mdio_mux_gpio_probe(struct platform_device *pdev)
{
- enum of_gpio_flags f;
struct mdio_mux_gpio_state *s;
int num_gpios;
unsigned int n;
@@ -70,22 +64,14 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
s->num_gpios = num_gpios;
for (n = 0; n < num_gpios; ) {
- int gpio = of_get_gpio_flags(pdev->dev.of_node, n, &f);
- if (gpio < 0) {
- r = (gpio == -ENODEV) ? -EPROBE_DEFER : gpio;
+ struct gpio_desc *gpio = gpiod_get_index(&pdev->dev, NULL, n,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(gpio)) {
+ r = PTR_ERR(gpio);
goto err;
}
s->gpio[n] = gpio;
-
n++;
-
- r = gpio_request(gpio, "mdio_mux_gpio");
- if (r)
- goto err;
-
- r = gpio_direction_output(gpio, 0);
- if (r)
- goto err;
}
r = mdio_mux_init(&pdev->dev,
@@ -98,15 +84,19 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
err:
while (n) {
n--;
- gpio_free(s->gpio[n]);
+ gpiod_put(s->gpio[n]);
}
return r;
}
static int mdio_mux_gpio_remove(struct platform_device *pdev)
{
+ unsigned int n;
struct mdio_mux_gpio_state *s = dev_get_platdata(&pdev->dev);
mdio_mux_uninit(s->mux_handle);
+ for (n = 0; n < s->num_gpios; n++) {
+ gpiod_put(s->gpio[n]);
+ }
return 0;
}
--
2.0.4
^ permalink raw reply related
* Re: [PATCH 2/4] GMAC: modify CRU config for Rockchip RK3288 SoCs integrated GMAC
From: Kever Yang @ 2014-11-25 9:39 UTC (permalink / raw)
To: Roger Chen, heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, mark.yao,
eddie.cai
In-Reply-To: <1416906512-26120-1-git-send-email-roger.chen@rock-chips.com>
Hi Roger,
This patch should be split into two patch, one for clock ID definition,
one for mac related clocks update.
The Subject for clk-rk3288.c should use below prefix:
clk: rockchip: modify clock for mac ...
On 11/25/2014 05:08 PM, Roger Chen wrote:
> modify CRU config for GMAC driver
Could you detail the reason for this patch in commit message?
> Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
> ---
> drivers/clk/rockchip/clk-rk3288.c | 14 +++++++-------
> include/dt-bindings/clock/rk3288-cru.h | 4 ++++
> 2 files changed, 11 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
> index 2327829..60237dc 100644
> --- a/drivers/clk/rockchip/clk-rk3288.c
> +++ b/drivers/clk/rockchip/clk-rk3288.c
> @@ -187,7 +187,7 @@ PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" };
> PNAME(mux_uart3_p) = { "uart3_src", "uart3_frac", "xin24m" };
> PNAME(mux_uart4_p) = { "uart4_src", "uart4_frac", "xin24m" };
> PNAME(mux_cif_out_p) = { "cif_src", "xin24m" };
> -PNAME(mux_macref_p) = { "mac_src", "ext_gmac" };
> +PNAME(mux_mac_p) = { "mac_pll_src", "ext_gmac" };
> PNAME(mux_hsadcout_p) = { "hsadc_src", "ext_hsadc" };
> PNAME(mux_edp_24m_p) = { "ext_edp_24m", "xin24m" };
> PNAME(mux_tspout_p) = { "cpll", "gpll", "npll", "xin27m" };
> @@ -560,18 +560,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
> MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, 0,
> RK3288_CLKSEL_CON(3), 8, 2, MFLAGS),
>
> - COMPOSITE(0, "mac_src", mux_pll_src_npll_cpll_gpll_p, 0,
> + COMPOSITE(SCLK_MAC_PLL, "mac_pll_src", mux_pll_src_npll_cpll_gpll_p, 0,
> RK3288_CLKSEL_CON(21), 0, 2, MFLAGS, 8, 5, DFLAGS,
> RK3288_CLKGATE_CON(2), 5, GFLAGS),
> - MUX(0, "macref", mux_macref_p, 0,
> + MUX(SCLK_MAC, "mac_clk", mux_mac_p, 0,
> RK3288_CLKSEL_CON(21), 4, 1, MFLAGS),
> - GATE(0, "sclk_macref_out", "macref", 0,
> + GATE(SCLK_MACREF_OUT, "sclk_macref_out", "mac_clk", 0,
> RK3288_CLKGATE_CON(5), 3, GFLAGS),
> - GATE(SCLK_MACREF, "sclk_macref", "macref", 0,
> + GATE(SCLK_MACREF, "sclk_macref", "mac_clk", 0,
> RK3288_CLKGATE_CON(5), 2, GFLAGS),
> - GATE(SCLK_MAC_RX, "sclk_mac_rx", "macref", 0,
> + GATE(SCLK_MAC_RX, "sclk_mac_rx", "mac_clk", 0,
> RK3288_CLKGATE_CON(5), 0, GFLAGS),
> - GATE(SCLK_MAC_TX, "sclk_mac_tx", "macref", 0,
> + GATE(SCLK_MAC_TX, "sclk_mac_tx", "mac_clk", 0,
> RK3288_CLKGATE_CON(5), 1, GFLAGS),
>
> COMPOSITE(0, "hsadc_src", mux_pll_src_cpll_gpll_p, 0,
> diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h
> index 100a08c..f9496f5 100644
> --- a/include/dt-bindings/clock/rk3288-cru.h
> +++ b/include/dt-bindings/clock/rk3288-cru.h
> @@ -72,6 +72,10 @@
> #define SCLK_HEVC_CABAC 111
> #define SCLK_HEVC_CORE 112
>
> +#define SCLK_MAC_PLL 150
> +#define SCLK_MAC 151
> +#define SCLK_MACREF_OUT 152
> +
> #define DCLK_VOP0 190
> #define DCLK_VOP1 191
>
^ permalink raw reply
* [PATCH net] net/mlx4_core: Limit count field to 24 bits in qp_alloc_res
From: Or Gerlitz @ 2014-11-25 9:54 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Matan Barak, Amir Vadai, Jack Morgenstein, Or Gerlitz
From: Jack Morgenstein <jackm@dev.mellanox.co.il>
Some VF drivers use the upper byte of "param1" (the qp count field)
in mlx4_qp_reserve_range() to pass flags which are used to optimize
the range allocation.
Under the current code, if any of these flags are set, the 32-bit
count field yields a count greater than 2^24, which is out of range,
and this VF fails.
As these flags represent a "best-effort" allocation hint anyway, they may
safely be ignored. Therefore, the PF driver may simply mask out the bits.
Fixes: c82e9aa0a8 "mlx4_core: resource tracking for HCA resources used by guests"
Signed-off-by: Jack Morgenstein <jackm@dev.mellanox.co.il>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
---
Hi Dave,
This fix goes way backi down to kernel 3.3... I think we will be good
if it pushed to -stable of >= 3.10
.../net/ethernet/mellanox/mlx4/resource_tracker.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 5d2498d..cd5cf6d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -1546,7 +1546,7 @@ static int qp_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
switch (op) {
case RES_OP_RESERVE:
- count = get_param_l(&in_param);
+ count = get_param_l(&in_param) & 0xffffff;
align = get_param_h(&in_param);
err = mlx4_grant_resource(dev, slave, RES_QP, count, 0);
if (err)
--
1.7.1
^ permalink raw reply related
* Re: [PATCH 3/4] GMAC: dts: add gmac info for rk3288
From: Kever Yang @ 2014-11-25 9:55 UTC (permalink / raw)
To: Roger Chen, heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, mark.yao,
eddie.cai
In-Reply-To: <1416906536-26158-1-git-send-email-roger.chen@rock-chips.com>
Hi Roger,
The Subject should use below prefix:
ARM: dts: rockchip: add gmac info for rk3288
On 11/25/2014 05:08 PM, Roger Chen wrote:
> add gmac info in rk3288.dtsi for GMAC driver
>
> Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
> ---
> arch/arm/boot/dts/rk3288.dtsi | 59 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 59 insertions(+)
>
> diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
> index 0f50d5d..949675d 100644
> --- a/arch/arm/boot/dts/rk3288.dtsi
> +++ b/arch/arm/boot/dts/rk3288.dtsi
> @@ -137,6 +137,13 @@
> #clock-cells = <0>;
> };
>
> + ext_gmac: external-gmac-clock {
> + compatible = "fixed-clock";
> + clock-frequency = <125000000>;
> + clock-output-names = "ext_gmac";
> + #clock-cells = <0>;
> + };
> +
I'm not sure Heiko will happy with both add the new clock source and
gmac node in these patch.
> timer {
> compatible = "arm,armv7-timer";
> interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
> @@ -490,6 +497,25 @@
> reg = <0xff740000 0x1000>;
> };
>
> + gmac: eth@ff290000 {
> + compatible = "rockchip,rk3288-gmac";
> + reg = <0xff290000 0x10000>;
> + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; /*irq=59*/
> + interrupt-names = "macirq";
> + rockchip,grf = <&grf>;
> + clocks = <&cru SCLK_MAC>, <&cru SCLK_MAC_PLL>,
> + <&cru SCLK_MAC_RX>, <&cru SCLK_MAC_TX>,
> + <&cru SCLK_MACREF>, <&cru SCLK_MACREF_OUT>,
> + <&cru ACLK_GMAC>, <&cru PCLK_GMAC>;
> + clock-names = "stmmaceth", "clk_mac_pll",
> + "mac_clk_rx", "mac_clk_tx",
> + "clk_mac_ref", "clk_mac_refout",
> + "aclk_mac", "pclk_mac";
> + phy-mode = "rgmii";
> + pinctrl-names = "default";
> + pinctrl-0 = <&rgmii_pin /*&rmii_pin*/>;
> + };
> +
The controller dts node should be sort by the base address, I'm sure
this is in a
wrong place.
- Kever
> cru: clock-controller@ff760000 {
> compatible = "rockchip,rk3288-cru";
> reg = <0xff760000 0x1000>;
> @@ -1040,5 +1066,38 @@
> rockchip,pins = <7 23 3 &pcfg_pull_none>;
> };
> };
> +
> + gmac {
> + rgmii_pin: rgmii-pins {
> + rockchip,pins = <3 30 3 &pcfg_pull_none>,
> + <3 31 3 &pcfg_pull_none>,
> + <3 26 3 &pcfg_pull_none>,
> + <3 27 3 &pcfg_pull_none>,
> + <3 28 3 &pcfg_pull_none>,
> + <3 29 3 &pcfg_pull_none>,
> + <3 24 3 &pcfg_pull_none>,
> + <3 25 3 &pcfg_pull_none>,
> + <4 0 3 &pcfg_pull_none>,
> + <4 5 3 &pcfg_pull_none>,
> + <4 6 3 &pcfg_pull_none>,
> + <4 9 3 &pcfg_pull_none>,
> + <4 4 3 &pcfg_pull_none>,
> + <4 1 3 &pcfg_pull_none>,
> + <4 3 3 &pcfg_pull_none>;
> + };
> +
> + rmii_pin: rmii-pins {
> + rockchip,pins = <3 30 3 &pcfg_pull_none>,
> + <3 31 3 &pcfg_pull_none>,
> + <3 28 3 &pcfg_pull_none>,
> + <3 29 3 &pcfg_pull_none>,
> + <4 0 3 &pcfg_pull_none>,
> + <4 5 3 &pcfg_pull_none>,
> + <4 4 3 &pcfg_pull_none>,
> + <4 1 3 &pcfg_pull_none>,
> + <4 2 3 &pcfg_pull_none>,
> + <4 3 3 &pcfg_pull_none>;
> + };
> + };
> };
> };
^ permalink raw reply
* Re: [PATCH 4/4] GMAC: dts: enable gmac on RK3288 evb board
From: Kever Yang @ 2014-11-25 9:59 UTC (permalink / raw)
To: Roger Chen, heiko
Cc: peppe.cavallaro, netdev, linux-kernel, linux-rockchip, mark.yao,
eddie.cai
In-Reply-To: <1416906549-26196-1-git-send-email-roger.chen@rock-chips.com>
Hi Roger,
The subject should prefix:
Arm: dts: rockchip: enable ...
On 11/25/2014 05:09 PM, Roger Chen wrote:
> enable gmac in rk3288-evb-rk808.dts
>
> Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
> ---
> arch/arm/boot/dts/rk3288-evb-rk808.dts | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/arch/arm/boot/dts/rk3288-evb-rk808.dts b/arch/arm/boot/dts/rk3288-evb-rk808.dts
> index d8c775e6..168b4fa 100644
> --- a/arch/arm/boot/dts/rk3288-evb-rk808.dts
> +++ b/arch/arm/boot/dts/rk3288-evb-rk808.dts
> @@ -152,3 +152,25 @@
> };
> };
> };
> +
> +&ext_gmac {
> + clock-frequency = <125000000>;
> + clock-output-names = "ext_gmac";
> +};
> +
> +&gmac {
> + //pmu_regulator = "act_ldo5";
> + //pmu_enable_level = <1>; //1->HIGH, 0->LOW
remove these unused code.
> + power-gpio = <&gpio0 6 GPIO_ACTIVE_HIGH>;
> + reset-gpio = <&gpio4 7 GPIO_ACTIVE_LOW>;
> + //phyirq-gpio = <&gpio4 2 GPIO_ACTIVE_LOW>;
same.
> + phy-mode = "rgmii";
> + clock_in_out = "input";
> + assigned-clocks = <&cru SCLK_MAC>;
> + assigned-clock-parents = <&ext_gmac>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&rgmii_pin>; /*&rmii_pin*/
same.
- Kever
> + tx_delay = <0x30>;
> + rx_delay = <0x10>;
> + status = "ok";
> +};
^ permalink raw reply
* Re: [PATCH 1/4] GMAC: add driver for Rockchip RK3288 SoCs integrated GMAC
From: Giuseppe CAVALLARO @ 2014-11-25 10:05 UTC (permalink / raw)
To: Roger Chen, heiko
Cc: netdev, linux-kernel, linux-rockchip, kever.yang, mark.yao,
eddie.cai
In-Reply-To: <1416906460-26078-2-git-send-email-roger.chen@rock-chips.com>
Hello Roger
thx for these patches, my comments inline below
On 11/25/2014 10:07 AM, Roger Chen wrote:
> This driver is based on stmmac driver.
>
> Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
> ---
> drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +-
> drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 629 ++++++++++++++++++++
> .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 +
> .../net/ethernet/stmicro/stmmac/stmmac_platform.h | 1 +
> 4 files changed, 634 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
> index ac4d562..73c2715 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/Makefile
> +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
> @@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
>
> obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
> stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \
> - dwmac-sti.o dwmac-socfpga.o
> + dwmac-sti.o dwmac-socfpga.o dwmac-rk.o
>
> obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
> stmmac-pci-objs:= stmmac_pci.o
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> new file mode 100644
> index 0000000..9e50b37
> --- /dev/null
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> @@ -0,0 +1,629 @@
> +/**
> + * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer
> + *
> + * Copyright (C) 2014 Chen-Zhi (Roger Chen)
> + *
> + * Chen-Zhi (Roger Chen) <roger.chen@rock-chips.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/stmmac.h>
> +#include <linux/clk.h>
> +#include <linux/phy.h>
> +#include <linux/of_net.h>
> +#include <linux/gpio.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/delay.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +
> +struct rk_priv_data {
> + struct platform_device *pdev;
> + int phy_iface;
> + bool power_ctrl_by_pmu;
> + char pmu_regulator[32];
> + int pmu_enable_level;
> +
> + int power_io;
> + int power_io_level;
> + int reset_io;
> + int reset_io_level;
> + int phyirq_io;
> + int phyirq_io_level;
> +
> + bool clk_enabled;
> + bool clock_input;
> +
> + struct clk *clk_mac;
> + struct clk *clk_mac_pll;
> + struct clk *gmac_clkin;
> + struct clk *mac_clk_rx;
> + struct clk *mac_clk_tx;
> + struct clk *clk_mac_ref;
> + struct clk *clk_mac_refout;
> + struct clk *aclk_mac;
> + struct clk *pclk_mac;
> +
> + int tx_delay;
> + int rx_delay;
> +
> + struct regmap *grf;
> +};
> +
> +#define RK3288_GRF_SOC_CON1 0x0248
> +#define RK3288_GRF_SOC_CON3 0x0250
> +#define RK3288_GRF_GPIO3D_E 0x01ec
> +#define RK3288_GRF_GPIO4A_E 0x01f0
> +#define RK3288_GRF_GPIO4B_E 0x01f4
> +
> +/*RK3288_GRF_SOC_CON1*/
> +#define GMAC_PHY_INTF_SEL_RGMII ((0x01C0 << 16) | (0x0040))
> +#define GMAC_PHY_INTF_SEL_RMII ((0x01C0 << 16) | (0x0100))
> +#define GMAC_FLOW_CTRL ((0x0200 << 16) | (0x0200))
> +#define GMAC_FLOW_CTRL_CLR ((0x0200 << 16) | (0x0000))
> +#define GMAC_SPEED_10M ((0x0400 << 16) | (0x0000))
> +#define GMAC_SPEED_100M ((0x0400 << 16) | (0x0400))
> +#define GMAC_RMII_CLK_25M ((0x0800 << 16) | (0x0800))
> +#define GMAC_RMII_CLK_2_5M ((0x0800 << 16) | (0x0000))
> +#define GMAC_CLK_125M ((0x3000 << 16) | (0x0000))
> +#define GMAC_CLK_25M ((0x3000 << 16) | (0x3000))
> +#define GMAC_CLK_2_5M ((0x3000 << 16) | (0x2000))
> +#define GMAC_RMII_MODE ((0x4000 << 16) | (0x4000))
> +#define GMAC_RMII_MODE_CLR ((0x4000 << 16) | (0x0000))
> +
> +/*RK3288_GRF_SOC_CON3*/
> +#define GMAC_TXCLK_DLY_ENABLE ((0x4000 << 16) | (0x4000))
> +#define GMAC_TXCLK_DLY_DISABLE ((0x4000 << 16) | (0x0000))
> +#define GMAC_RXCLK_DLY_ENABLE ((0x8000 << 16) | (0x8000))
> +#define GMAC_RXCLK_DLY_DISABLE ((0x8000 << 16) | (0x0000))
> +#define GMAC_CLK_RX_DL_CFG(val) ((0x3F80 << 16) | (val<<7))
> +#define GMAC_CLK_TX_DL_CFG(val) ((0x007F << 16) | (val))
why do not use BIT and BIT_MASK where possible?
> +static void set_to_rgmii(struct rk_priv_data *bsp_priv,
> + int tx_delay, int rx_delay)
> +{
> + if (IS_ERR(bsp_priv->grf)) {
> + pr_err("%s: Missing rockchip,grf property\n", __func__);
> + return;
> + }
> +
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_PHY_INTF_SEL_RGMII);
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_RMII_MODE_CLR);
maybe you could perform just one write unless there is some HW
constraint.
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
> + GMAC_RXCLK_DLY_ENABLE);
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
> + GMAC_TXCLK_DLY_ENABLE);
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
> + GMAC_CLK_RX_DL_CFG(rx_delay));
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
> + GMAC_CLK_TX_DL_CFG(tx_delay));
ditto
> +
> + regmap_write(bsp_priv->grf, RK3288_GRF_GPIO3D_E, 0xFFFFFFFF);
> + regmap_write(bsp_priv->grf, RK3288_GRF_GPIO4B_E,
> + 0x3<<2<<16 | 0x3<<2);
pls use macros, these shift sequence is really help to decode
> + regmap_write(bsp_priv->grf, RK3288_GRF_GPIO4A_E, 0xFFFFFFFF);
> +
> + pr_info("%s: tx delay=0x%x; rx delay=0x%x;\n",
> + __func__, tx_delay, rx_delay);
may I suggest pr_debug?
> +}
> +
> +static void set_to_rmii(struct rk_priv_data *bsp_priv)
> +{
> + if (IS_ERR(bsp_priv->grf)) {
> + pr_err("%s: Missing rockchip,grf property\n", __func__);
> + return;
> + }
> +
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_PHY_INTF_SEL_RMII);
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_RMII_MODE);
> +}
> +
> +static void set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed)
> +{
> + if (IS_ERR(bsp_priv->grf)) {
> + pr_err("%s: Missing rockchip,grf property\n", __func__);
> + return;
> + }
> +
> + if (speed == 10)
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_2_5M);
> + else if (speed == 100)
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_25M);
> + else if (speed == 1000)
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_125M);
> + else
> + pr_err("unknown speed value for RGMII! speed=%d", speed);
> +}
> +
> +static void set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
> +{
> + if (IS_ERR(bsp_priv->grf)) {
> + pr_err("%s: Missing rockchip,grf property\n", __func__);
> + return;
> + }
> +
> + if (speed == 10) {
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_RMII_CLK_2_5M);
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_SPEED_10M);
> + } else if (speed == 100) {
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_RMII_CLK_25M);
> + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
> + GMAC_SPEED_100M);
> + } else {
> + pr_err("unknown speed value for RMII! speed=%d", speed);
> + }
> +}
> +
> +#define MAC_CLK_RX "mac_clk_rx"
> +#define MAC_CLK_TX "mac_clk_tx"
> +#define CLK_MAC_REF "clk_mac_ref"
> +#define CLK_MAC_REF_OUT "clk_mac_refout"
> +#define CLK_MAC_PLL "clk_mac_pll"
> +#define ACLK_MAC "aclk_mac"
> +#define PCLK_MAC "pclk_mac"
> +#define MAC_CLKIN "ext_gmac"
> +#define CLK_MAC "stmmaceth"
Concerning the clocks, the "stmmaceth" is actually used for the main
clock so maybe you should use another name.
See: Documentation/devicetree/bindings/net/stmmac.txt
For St platforms, it has been used: sti-ethclk but we could
used a common and better name: stmmac-phyclk
And I could also propose this change on STi glue-logic.
Then, I think that all the clock macros above should be documented
and maybe managed at DT level, what do you think?
> +
> +int gmac_clk_init(struct rk_priv_data *bsp_priv)
> +{
> + struct device *dev = &bsp_priv->pdev->dev;
> +
> + bsp_priv->clk_enabled = false;
> +
> + bsp_priv->mac_clk_rx = clk_get(dev, MAC_CLK_RX);
> + if (IS_ERR(bsp_priv->mac_clk_rx))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, MAC_CLK_RX);
> +
> + bsp_priv->mac_clk_tx = clk_get(dev, MAC_CLK_TX);
> + if (IS_ERR(bsp_priv->mac_clk_tx))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, MAC_CLK_TX);
> +
> + bsp_priv->clk_mac_ref = clk_get(dev, CLK_MAC_REF);
> + if (IS_ERR(bsp_priv->clk_mac_ref))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, CLK_MAC_REF);
> +
> + bsp_priv->clk_mac_refout = clk_get(dev, CLK_MAC_REF_OUT);
> + if (IS_ERR(bsp_priv->clk_mac_refout))
> + pr_warn("%s: warning:cannot get clock %s\n",
> + __func__, CLK_MAC_REF_OUT);
> +
> + bsp_priv->aclk_mac = clk_get(dev, ACLK_MAC);
> + if (IS_ERR(bsp_priv->aclk_mac))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, ACLK_MAC);
> +
> + bsp_priv->pclk_mac = clk_get(dev, PCLK_MAC);
> + if (IS_ERR(bsp_priv->pclk_mac))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, PCLK_MAC);
> +
> + bsp_priv->clk_mac_pll = clk_get(dev, CLK_MAC_PLL);
> + if (IS_ERR(bsp_priv->clk_mac_pll))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, CLK_MAC_PLL);
> +
> + bsp_priv->gmac_clkin = clk_get(dev, MAC_CLKIN);
> + if (IS_ERR(bsp_priv->gmac_clkin))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, MAC_CLKIN);
> +
> + bsp_priv->clk_mac = clk_get(dev, CLK_MAC);
> + if (IS_ERR(bsp_priv->clk_mac))
> + pr_warn("%s: warning: cannot get clock %s\n",
> + __func__, CLK_MAC);
> +
> + if (bsp_priv->clock_input) {
> + pr_info("%s: clock input from PHY\n", __func__);
> + } else {
> + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
> + clk_set_rate(bsp_priv->clk_mac_pll, 50000000);
> +
> + clk_set_parent(bsp_priv->clk_mac, bsp_priv->clk_mac_pll);
> + }
> +
> + return 0;
> +}
> +
> +static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
> +{
> + int phy_iface = phy_iface = bsp_priv->phy_iface;
> +
> + if (enable) {
> + if (!bsp_priv->clk_enabled) {
> + if (phy_iface == PHY_INTERFACE_MODE_RMII) {
> + if (!IS_ERR(bsp_priv->mac_clk_rx))
> + clk_prepare_enable(
> + bsp_priv->mac_clk_rx);
> +
> + if (!IS_ERR(bsp_priv->clk_mac_ref))
> + clk_prepare_enable(
> + bsp_priv->clk_mac_ref);
> +
> + if (!IS_ERR(bsp_priv->clk_mac_refout))
> + clk_prepare_enable(
> + bsp_priv->clk_mac_refout);
> + }
> +
> + if (!IS_ERR(bsp_priv->aclk_mac))
> + clk_prepare_enable(bsp_priv->aclk_mac);
> +
> + if (!IS_ERR(bsp_priv->pclk_mac))
> + clk_prepare_enable(bsp_priv->pclk_mac);
> +
> + if (!IS_ERR(bsp_priv->mac_clk_tx))
> + clk_prepare_enable(bsp_priv->mac_clk_tx);
> +
> + /**
> + * if (!IS_ERR(bsp_priv->clk_mac))
> + * clk_prepare_enable(bsp_priv->clk_mac);
> + */
why do you need to comment this? could it be related to the stmmaceth
clk that is used by the main?
> + mdelay(5);
hmm, do you actually need this mdelay? Is it to stabilize PLL sources?
> + bsp_priv->clk_enabled = true;
> + }
> + } else {
> + if (bsp_priv->clk_enabled) {
> + if (phy_iface == PHY_INTERFACE_MODE_RMII) {
> + if (!IS_ERR(bsp_priv->mac_clk_rx))
> + clk_disable_unprepare(
> + bsp_priv->mac_clk_rx);
> +
> + if (!IS_ERR(bsp_priv->clk_mac_ref))
> + clk_disable_unprepare(
> + bsp_priv->clk_mac_ref);
> +
> + if (!IS_ERR(bsp_priv->clk_mac_refout))
> + clk_disable_unprepare(
> + bsp_priv->clk_mac_refout);
> + }
> +
> + if (!IS_ERR(bsp_priv->aclk_mac))
> + clk_disable_unprepare(bsp_priv->aclk_mac);
> +
> + if (!IS_ERR(bsp_priv->pclk_mac))
> + clk_disable_unprepare(bsp_priv->pclk_mac);
> +
> + if (!IS_ERR(bsp_priv->mac_clk_tx))
> + clk_disable_unprepare(bsp_priv->mac_clk_tx);
> + /**
> + * if (!IS_ERR(bsp_priv->clk_mac))
> + * clk_disable_unprepare(bsp_priv->clk_mac);
> + */
> + bsp_priv->clk_enabled = false;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int power_on_by_pmu(struct rk_priv_data *bsp_priv, bool enable)
> +{
> + struct regulator *ldo;
> + char *ldostr = bsp_priv->pmu_regulator;
> + int ret;
> +
> + if (!ldostr) {
> + pr_err("%s: no ldo found\n", __func__);
> + return -1;
> + }
> +
> + ldo = regulator_get(NULL, ldostr);
> + if (!ldo) {
> + pr_err("\n%s get ldo %s failed\n", __func__, ldostr);
> + } else {
> + if (enable) {
> + if (!regulator_is_enabled(ldo)) {
> + regulator_set_voltage(ldo, 3300000, 3300000);
> + ret = regulator_enable(ldo);
> + if (ret != 0)
> + pr_err("%s: fail to enable %s\n",
> + __func__, ldostr);
> + else
> + pr_info("turn on ldo done.\n");
> + } else {
> + pr_warn("%s is enabled before enable", ldostr);
> + }
> + } else {
> + if (regulator_is_enabled(ldo)) {
> + ret = regulator_disable(ldo);
> + if (ret != 0)
> + pr_err("%s: fail to disable %s\n",
> + __func__, ldostr);
> + else
> + pr_info("turn off ldo done.\n");
> + } else {
> + pr_warn("%s is disabled before disable",
> + ldostr);
> + }
> + }
> + regulator_put(ldo);
> + }
> +
> + return 0;
> +}
> +
> +static int power_on_by_gpio(struct rk_priv_data *bsp_priv, bool enable)
> +{
> + if (enable) {
> + /*power on*/
> + if (gpio_is_valid(bsp_priv->power_io))
> + gpio_direction_output(bsp_priv->power_io,
> + bsp_priv->power_io_level);
> + } else {
> + /*power off*/
> + if (gpio_is_valid(bsp_priv->power_io))
> + gpio_direction_output(bsp_priv->power_io,
> + !bsp_priv->power_io_level);
> + }
> +
> + return 0;
> +}
> +
> +static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable)
> +{
> + int ret = -1;
> +
> + pr_info("Ethernet PHY power %s\n", enable == 1 ? "on" : "off");
> +
> + if (bsp_priv->power_ctrl_by_pmu)
> + ret = power_on_by_pmu(bsp_priv, enable);
> + else
> + ret = power_on_by_gpio(bsp_priv, enable);
> +
> + if (enable) {
> + /*reset*/
> + if (gpio_is_valid(bsp_priv->reset_io)) {
> + gpio_direction_output(bsp_priv->reset_io,
> + bsp_priv->reset_io_level);
> + mdelay(5);
> + gpio_direction_output(bsp_priv->reset_io,
> + !bsp_priv->reset_io_level);
> + }
> + mdelay(30);
> +
> + } else {
> + /*pull down reset*/
> + if (gpio_is_valid(bsp_priv->reset_io)) {
> + gpio_direction_output(bsp_priv->reset_io,
> + bsp_priv->reset_io_level);
> + }
> + }
> +
> + return ret;
> +}
> +
> +#define GPIO_PHY_POWER "gmac_phy_power"
> +#define GPIO_PHY_RESET "gmac_phy_reset"
> +#define GPIO_PHY_IRQ "gmac_phy_irq"
> +
> +static void *rk_gmac_setup(struct platform_device *pdev)
> +{
> + struct rk_priv_data *bsp_priv;
> + struct device *dev = &pdev->dev;
> + enum of_gpio_flags flags;
> + int ret;
> + const char *strings = NULL;
> + int value;
> + int irq;
> +
> + bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL);
> + if (!bsp_priv)
> + return ERR_PTR(-ENOMEM);
> +
> + bsp_priv->phy_iface = of_get_phy_mode(dev->of_node);
> +
> + ret = of_property_read_string(dev->of_node, "pmu_regulator", &strings);
> + if (ret) {
> + pr_err("%s: Can not read property: pmu_regulator.\n", __func__);
> + bsp_priv->power_ctrl_by_pmu = false;
> + } else {
> + pr_info("%s: ethernet phy power controlled by pmu(%s).\n",
> + __func__, strings);
> + bsp_priv->power_ctrl_by_pmu = true;
> + strcpy(bsp_priv->pmu_regulator, strings);
> + }
> +
> + ret = of_property_read_u32(dev->of_node, "pmu_enable_level", &value);
> + if (ret) {
> + pr_err("%s: Can not read property: pmu_enable_level.\n",
> + __func__);
> + bsp_priv->power_ctrl_by_pmu = false;
> + } else {
> + pr_info("%s: PHY power controlled by pmu(level = %s).\n",
> + __func__, (value == 1) ? "HIGH" : "LOW");
> + bsp_priv->power_ctrl_by_pmu = true;
> + bsp_priv->pmu_enable_level = value;
> + }
> +
> + ret = of_property_read_string(dev->of_node, "clock_in_out", &strings);
> + if (ret) {
> + pr_err("%s: Can not read property: clock_in_out.\n", __func__);
> + bsp_priv->clock_input = true;
> + } else {
> + pr_info("%s: clock input or output? (%s).\n",
> + __func__, strings);
> + if (!strcmp(strings, "input"))
> + bsp_priv->clock_input = true;
> + else
> + bsp_priv->clock_input = false;
> + }
> +
> + ret = of_property_read_u32(dev->of_node, "tx_delay", &value);
> + if (ret) {
> + bsp_priv->tx_delay = 0x30;
> + pr_err("%s: Can not read property: tx_delay.", __func__);
> + pr_err("%s: set tx_delay to 0x%x\n",
> + __func__, bsp_priv->tx_delay);
> + } else {
> + pr_info("%s: TX delay(0x%x).\n", __func__, value);
> + bsp_priv->tx_delay = value;
> + }
> +
> + ret = of_property_read_u32(dev->of_node, "rx_delay", &value);
> + if (ret) {
> + bsp_priv->rx_delay = 0x10;
> + pr_err("%s: Can not read property: rx_delay.", __func__);
> + pr_err("%s: set rx_delay to 0x%x\n",
> + __func__, bsp_priv->rx_delay);
> + } else {
> + pr_info("%s: RX delay(0x%x).\n", __func__, value);
> + bsp_priv->rx_delay = value;
> + }
> +
> + bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,grf");
> + if (IS_ERR(bsp_priv->grf))
> + dev_err(&pdev->dev, "Missing rockchip,grf property\n");
I wonder if you can fail on here and save all the check in
set_rgmii_speed etc.
Maybe this can be considered a mandatory property for the glue-logic.
> +
> + bsp_priv->phyirq_io =
> + of_get_named_gpio_flags(dev->of_node,
> + "phyirq-gpio", 0, &flags);
> + bsp_priv->phyirq_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
Sorry, did you document all in Documentation/devicetree/bindings/net/ ?
> +
> + bsp_priv->reset_io =
> + of_get_named_gpio_flags(dev->of_node,
> + "reset-gpio", 0, &flags);
> + bsp_priv->reset_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
> +
> + bsp_priv->power_io =
> + of_get_named_gpio_flags(dev->of_node, "power-gpio", 0, &flags);
> + bsp_priv->power_io_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
> +
> + /*power*/
> + if (!gpio_is_valid(bsp_priv->power_io)) {
> + pr_err("%s: Failed to get GPIO %s.\n",
> + __func__, "power-gpio");
> + } else {
> + ret = gpio_request(bsp_priv->power_io, GPIO_PHY_POWER);
> + if (ret)
> + pr_err("%s: ERROR: Failed to request GPIO %s.\n",
> + __func__, GPIO_PHY_POWER);
> + }
> +
> + if (!gpio_is_valid(bsp_priv->reset_io)) {
> + pr_err("%s: ERROR: Get reset-gpio failed.\n", __func__);
> + } else {
> + ret = gpio_request(bsp_priv->reset_io, GPIO_PHY_RESET);
> + if (ret)
> + pr_err("%s: ERROR: Failed to request GPIO %s.\n",
> + __func__, GPIO_PHY_RESET);
> + }
> +
> + if (bsp_priv->phyirq_io > 0) {
> + struct plat_stmmacenet_data *plat_dat;
> +
> + pr_info("PHY irq in use\n");
> + ret = gpio_request(bsp_priv->phyirq_io, GPIO_PHY_IRQ);
> + if (ret < 0) {
> + pr_warn("%s: Failed to request GPIO %s\n",
> + __func__, GPIO_PHY_IRQ);
> + goto goon;
> + }
> +
> + ret = gpio_direction_input(bsp_priv->phyirq_io);
> + if (ret < 0) {
> + pr_err("%s, Failed to set input for GPIO %s\n",
> + __func__, GPIO_PHY_IRQ);
> + gpio_free(bsp_priv->phyirq_io);
> + goto goon;
> + }
> +
> + irq = gpio_to_irq(bsp_priv->phyirq_io);
> + if (irq < 0) {
> + ret = irq;
> + pr_err("Failed to set irq for %s\n",
> + GPIO_PHY_IRQ);
> + gpio_free(bsp_priv->phyirq_io);
> + goto goon;
> + }
> +
> + plat_dat = dev_get_platdata(&pdev->dev);
> + if (plat_dat)
> + plat_dat->mdio_bus_data->probed_phy_irq = irq;
> + else
> + pr_err("%s: plat_data is NULL\n", __func__);
> + }
> +
> +goon:
> + /*rmii or rgmii*/
> + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) {
> + pr_info("%s: init for RGMII\n", __func__);
> + set_to_rgmii(bsp_priv, bsp_priv->tx_delay, bsp_priv->rx_delay);
> + } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) {
> + pr_info("%s: init for RMII\n", __func__);
> + set_to_rmii(bsp_priv);
> + } else {
> + pr_err("%s: ERROR: NO interface defined!\n", __func__);
> + }
> +
> + bsp_priv->pdev = pdev;
> +
> + gmac_clk_init(bsp_priv);
> +
> + return bsp_priv;
> +}
> +
> +static int rk_gmac_init(struct platform_device *pdev, void *priv)
> +{
> + struct rk_priv_data *bsp_priv = priv;
> + int ret;
> +
> + ret = phy_power_on(bsp_priv, true);
> + if (ret)
> + return ret;
> +
> + ret = gmac_clk_enable(bsp_priv, true);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static void rk_gmac_exit(struct platform_device *pdev, void *priv)
> +{
> + struct rk_priv_data *gmac = priv;
> +
> + phy_power_on(gmac, false);
> + gmac_clk_enable(gmac, false);
> +}
> +
> +static void rk_fix_speed(void *priv, unsigned int speed)
> +{
> + struct rk_priv_data *bsp_priv = priv;
> +
> + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII)
> + set_rgmii_speed(bsp_priv, speed);
> + else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
> + set_rmii_speed(bsp_priv, speed);
> + else
> + pr_err("unsupported interface %d", bsp_priv->phy_iface);
> +}
> +
> +const struct stmmac_of_data rk_gmac_data = {
> + .has_gmac = 1,
> + .tx_coe = 1,
FYI, on new gmac there is the HW capability register to dinamically
provide you if coe is supported.
IMO you should add the OF "compatible" string and in case of mac
newer than the 3.50a you can remove coe.
BR
Peppe
> + .fix_mac_speed = rk_fix_speed,
> + .setup = rk_gmac_setup,
> + .init = rk_gmac_init,
> + .exit = rk_gmac_exit,
> +};
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
> index 15814b7..b4dee96 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
> @@ -33,6 +33,7 @@
>
> static const struct of_device_id stmmac_dt_ids[] = {
> /* SoC specific glue layers should come before generic bindings */
> + { .compatible = "rockchip,rk3288-gmac", .data = &rk_gmac_data},
> { .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data},
> { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},
> { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data},
> @@ -291,6 +292,8 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + pdev->dev.platform_data = plat_dat;
> +
> ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
> if (ret) {
> pr_err("%s: main dt probe failed", __func__);
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
> index 25dd1f7..32a0516 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
> @@ -24,5 +24,6 @@ extern const struct stmmac_of_data sun7i_gmac_data;
> extern const struct stmmac_of_data stih4xx_dwmac_data;
> extern const struct stmmac_of_data stid127_dwmac_data;
> extern const struct stmmac_of_data socfpga_gmac_data;
> +extern const struct stmmac_of_data rk_gmac_data;
>
> #endif /* __STMMAC_PLATFORM_H__ */
>
^ permalink raw reply
* Re: [PATCH 3/4] GMAC: dts: add gmac info for rk3288
From: Heiko Stübner @ 2014-11-25 10:12 UTC (permalink / raw)
To: Kever Yang
Cc: Roger Chen, peppe.cavallaro, netdev, linux-kernel, linux-rockchip,
mark.yao, eddie.cai
In-Reply-To: <54745229.2040503@rock-chips.com>
Hi Roger, Kever,
Am Dienstag, 25. November 2014, 17:55:53 schrieb Kever Yang:
> The Subject should use below prefix:
> ARM: dts: rockchip: add gmac info for rk3288
>
> On 11/25/2014 05:08 PM, Roger Chen wrote:
> > add gmac info in rk3288.dtsi for GMAC driver
> >
> > Signed-off-by: Roger Chen <roger.chen@rock-chips.com>
> > ---
> >
> > arch/arm/boot/dts/rk3288.dtsi | 59
> > +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59
> > insertions(+)
> >
> > diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
> > index 0f50d5d..949675d 100644
> > --- a/arch/arm/boot/dts/rk3288.dtsi
> > +++ b/arch/arm/boot/dts/rk3288.dtsi
> > @@ -137,6 +137,13 @@
> >
> > #clock-cells = <0>;
> >
> > };
> >
> > + ext_gmac: external-gmac-clock {
> > + compatible = "fixed-clock";
> > + clock-frequency = <125000000>;
> > + clock-output-names = "ext_gmac";
> > + #clock-cells = <0>;
> > + };
> > +
>
> I'm not sure Heiko will happy with both add the new clock source and
> gmac node in these patch.
correct. The ext_gmac is an external clock input providing a special clock to
the gmac. This makes it a board-specific property, which should also only be
added on boards having this external source - and of course in the board-
specific dts.
>
> > timer {
> >
> > compatible = "arm,armv7-timer";
> > interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
> > IRQ_TYPE_LEVEL_HIGH)>,
> >
> > @@ -490,6 +497,25 @@
> >
> > reg = <0xff740000 0x1000>;
> >
> > };
> >
> > + gmac: eth@ff290000 {
> > + compatible = "rockchip,rk3288-gmac";
> > + reg = <0xff290000 0x10000>;
> > + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; /*irq=59*/
please remote the "irq=59" comment, as it is not necessary
> > + interrupt-names = "macirq";
> > + rockchip,grf = <&grf>;
> > + clocks = <&cru SCLK_MAC>, <&cru SCLK_MAC_PLL>,
> > + <&cru SCLK_MAC_RX>, <&cru SCLK_MAC_TX>,
> > + <&cru SCLK_MACREF>, <&cru SCLK_MACREF_OUT>,
> > + <&cru ACLK_GMAC>, <&cru PCLK_GMAC>;
> > + clock-names = "stmmaceth", "clk_mac_pll",
> > + "mac_clk_rx", "mac_clk_tx",
> > + "clk_mac_ref", "clk_mac_refout",
> > + "aclk_mac", "pclk_mac";
> > + phy-mode = "rgmii";
> > + pinctrl-names = "default";
> > + pinctrl-0 = <&rgmii_pin /*&rmii_pin*/>;
> > + };
> > +
>
> The controller dts node should be sort by the base address, I'm sure
> this is in a
> wrong place.
correct, it is on the wrong place
Heiko
^ permalink raw reply
* [patch net-next v3 00/17] introduce rocker switch driver with hardware accelerated datapath api - phase 1: bridge fdb offload
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
Hi all.
This patchset is just the first phase of switch and switch-ish device
support api in kernel. Note that the api will extend.
So what this patchset includes:
- introduce switchdev api skeleton for implementing switch drivers
- introduce rocker switch driver which implements switchdev api fdb and
bridge set/get link ndos
As to the discussion if there is need to have specific class of device
representing the switch itself, so far we found no need to introduce that.
But we are generally ok with the idea and when the time comes and it will
be needed, it can be easily introduced without any disturbance.
This patchset introduces switch id export through rtnetlink and sysfs,
which is similar to what we have for port id in SR-IOV. I will send iproute2
patchset for showing the switch id for port netdevs once this is applied.
This applies also for the PF_BRIDGE and fdb iproute2 patches.
iproute2 patches are now available here:
https://github.com/jpirko/iproute2-rocker
For detailed description and version history, please see individual patches.
Jiri Pirko (8):
bridge: rename fdb_*_hw to fdb_*_hw_addr to avoid confusion
net: make vid as a parameter for ndo_fdb_add/ndo_fdb_del
net: rename netdev_phys_port_id to more generic name
net: introduce generic switch devices support
rtnl: expose physical switch id for particular device
net-sysfs: expose physical switch id for particular device
rocker: introduce rocker switch driver
rocker: implement ndo_fdb_dump
Scott Feldman (9):
bridge: call netdev_sw_port_stp_update when bridge port STP status
changes
bridge: add API to notify bridge driver of learned FBD on offloaded
device
rocker: implement rocker ofdpa flow table manipulation
rocker: implement L2 bridge offloading
bridge: move private brport flags to if_bridge.h so port drivers can
use flags
bridge: add new brport flag LEARNING_SYNC
bridge: add new hwmode swdev
bridge: add brport flags to dflt bridge_getlink
rocker: add ndo_bridge_setlnk/getlink support for learning policy
Documentation/networking/switchdev.txt | 59 +
MAINTAINERS | 14 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 2 +-
drivers/net/ethernet/emulex/benet/be_main.c | 3 +-
drivers/net/ethernet/intel/i40e/i40e_main.c | 4 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 6 +-
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 11 +-
drivers/net/ethernet/rocker/Kconfig | 27 +
drivers/net/ethernet/rocker/Makefile | 5 +
drivers/net/ethernet/rocker/rocker.c | 4363 ++++++++++++++++++++++
drivers/net/ethernet/rocker/rocker.h | 428 +++
drivers/net/macvlan.c | 4 +-
drivers/net/vxlan.c | 4 +-
include/linux/if_bridge.h | 30 +
include/linux/netdevice.h | 39 +-
include/linux/rtnetlink.h | 9 +-
include/net/switchdev.h | 37 +
include/uapi/linux/if_bridge.h | 1 +
include/uapi/linux/if_link.h | 2 +
include/uapi/linux/neighbour.h | 1 +
net/Kconfig | 1 +
net/Makefile | 3 +
net/bridge/br_fdb.c | 144 +-
net/bridge/br_private.h | 15 +-
net/bridge/br_stp.c | 2 +
net/core/dev.c | 2 +-
net/core/net-sysfs.c | 26 +-
net/core/rtnetlink.c | 119 +-
net/switchdev/Kconfig | 13 +
net/switchdev/Makefile | 5 +
net/switchdev/switchdev.c | 52 +
34 files changed, 5336 insertions(+), 99 deletions(-)
create mode 100644 Documentation/networking/switchdev.txt
create mode 100644 drivers/net/ethernet/rocker/Kconfig
create mode 100644 drivers/net/ethernet/rocker/Makefile
create mode 100644 drivers/net/ethernet/rocker/rocker.c
create mode 100644 drivers/net/ethernet/rocker/rocker.h
create mode 100644 include/net/switchdev.h
create mode 100644 net/switchdev/Kconfig
create mode 100644 net/switchdev/Makefile
create mode 100644 net/switchdev/switchdev.c
--
1.9.3
^ permalink raw reply
* [patch net-next v3 01/17] bridge: rename fdb_*_hw to fdb_*_hw_addr to avoid confusion
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
The current name might seem that this actually offloads the fdb entry to
hw. So rename it to clearly present that this for hardware address
addition/removal.
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
new in v2 as suggested by DaveM
v2->v3:
-moved the patch to the patchset head
---
net/bridge/br_fdb.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 6f6c95c..08ef4e7 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -90,7 +90,7 @@ static void fdb_rcu_free(struct rcu_head *head)
* are then updated with the new information.
* Called under RTNL.
*/
-static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr)
+static void fdb_add_hw_addr(struct net_bridge *br, const unsigned char *addr)
{
int err;
struct net_bridge_port *p;
@@ -118,7 +118,7 @@ undo:
* the ports with needed information.
* Called under RTNL.
*/
-static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)
+static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
{
struct net_bridge_port *p;
@@ -133,7 +133,7 @@ static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)
static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
{
if (f->is_static)
- fdb_del_hw(br, f->addr.addr);
+ fdb_del_hw_addr(br, f->addr.addr);
hlist_del_rcu(&f->hlist);
fdb_notify(br, f, RTM_DELNEIGH);
@@ -514,7 +514,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
return -ENOMEM;
fdb->is_local = fdb->is_static = 1;
- fdb_add_hw(br, addr);
+ fdb_add_hw_addr(br, addr);
fdb_notify(br, fdb, RTM_NEWNEIGH);
return 0;
}
@@ -754,19 +754,19 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
fdb->is_local = 1;
if (!fdb->is_static) {
fdb->is_static = 1;
- fdb_add_hw(br, addr);
+ fdb_add_hw_addr(br, addr);
}
} else if (state & NUD_NOARP) {
fdb->is_local = 0;
if (!fdb->is_static) {
fdb->is_static = 1;
- fdb_add_hw(br, addr);
+ fdb_add_hw_addr(br, addr);
}
} else {
fdb->is_local = 0;
if (fdb->is_static) {
fdb->is_static = 0;
- fdb_del_hw(br, addr);
+ fdb_del_hw_addr(br, addr);
}
}
--
1.9.3
^ permalink raw reply related
* [patch net-next v3 02/17] net: make vid as a parameter for ndo_fdb_add/ndo_fdb_del
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
Do the work of parsing NDA_VLAN directly in rtnetlink code, pass simple
u16 vid to drivers from there.
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
new in v3
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 9 +++--
drivers/net/macvlan.c | 4 +-
drivers/net/vxlan.c | 4 +-
include/linux/netdevice.h | 8 ++--
include/linux/rtnetlink.h | 6 ++-
net/bridge/br_fdb.c | 39 ++----------------
net/bridge/br_private.h | 4 +-
net/core/rtnetlink.c | 50 ++++++++++++++++++++----
10 files changed, 70 insertions(+), 60 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 7262077..5ed5e40 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -7536,7 +7536,7 @@ static int i40e_get_phys_port_id(struct net_device *netdev,
*/
static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr,
+ const unsigned char *addr, u16 vid,
u16 flags)
{
struct i40e_netdev_priv *np = netdev_priv(dev);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 932f779..1bad9f4 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7708,7 +7708,7 @@ static int ixgbe_set_features(struct net_device *netdev,
static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr,
+ const unsigned char *addr, u16 vid,
u16 flags)
{
/* guarantee we can provide a unique filter for the unicast address */
@@ -7717,7 +7717,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
return -ENOMEM;
}
- return ndo_dflt_fdb_add(ndm, tb, dev, addr, flags);
+ return ndo_dflt_fdb_add(ndm, tb, dev, addr, vid, flags);
}
static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index a913b3a..3227c80 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -376,13 +376,14 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p)
}
static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
- struct net_device *netdev, const unsigned char *addr)
+ struct net_device *netdev,
+ const unsigned char *addr, u16 vid)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
int err = -EOPNOTSUPP;
if (!adapter->fdb_mac_learn)
- return ndo_dflt_fdb_del(ndm, tb, netdev, addr);
+ return ndo_dflt_fdb_del(ndm, tb, netdev, addr, vid);
if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
qlcnic_sriov_check(adapter)) {
@@ -401,13 +402,13 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *netdev,
- const unsigned char *addr, u16 flags)
+ const unsigned char *addr, u16 vid, u16 flags)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
int err = 0;
if (!adapter->fdb_mac_learn)
- return ndo_dflt_fdb_add(ndm, tb, netdev, addr, flags);
+ return ndo_dflt_fdb_add(ndm, tb, netdev, addr, vid, flags);
if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) &&
!qlcnic_sriov_check(adapter)) {
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index bfb0b6e..a1a3e3e 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -872,7 +872,7 @@ static int macvlan_vlan_rx_kill_vid(struct net_device *dev,
static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr,
+ const unsigned char *addr, u16 vid,
u16 flags)
{
struct macvlan_dev *vlan = netdev_priv(dev);
@@ -897,7 +897,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr)
+ const unsigned char *addr, u16 vid)
{
struct macvlan_dev *vlan = netdev_priv(dev);
int err = -EINVAL;
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index e9f81d4..7d8013d 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -849,7 +849,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
/* Add static entry (via netlink) */
static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr, u16 flags)
+ const unsigned char *addr, u16 vid, u16 flags)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
/* struct net *net = dev_net(vxlan->dev); */
@@ -885,7 +885,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
/* Delete entry (via netlink) */
static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr)
+ const unsigned char *addr, u16 vid)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_fdb *f;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 5cd5087..fab074e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -951,11 +951,11 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
*
* int (*ndo_fdb_add)(struct ndmsg *ndm, struct nlattr *tb[],
* struct net_device *dev,
- * const unsigned char *addr, u16 flags)
+ * const unsigned char *addr, u16 vid, u16 flags)
* Adds an FDB entry to dev for addr.
* int (*ndo_fdb_del)(struct ndmsg *ndm, struct nlattr *tb[],
* struct net_device *dev,
- * const unsigned char *addr)
+ * const unsigned char *addr, u16 vid)
* Deletes the FDB entry from dev coresponding to addr.
* int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
* struct net_device *dev, struct net_device *filter_dev,
@@ -1128,11 +1128,13 @@ struct net_device_ops {
struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr,
+ u16 vid,
u16 flags);
int (*ndo_fdb_del)(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr);
+ const unsigned char *addr,
+ u16 vid);
int (*ndo_fdb_dump)(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 6cacbce..063f0f5 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -94,11 +94,13 @@ extern int ndo_dflt_fdb_add(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr,
- u16 flags);
+ u16 vid,
+ u16 flags);
extern int ndo_dflt_fdb_del(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr);
+ const unsigned char *addr,
+ u16 vid);
extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u16 mode);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 08ef4e7..b1be971 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -805,33 +805,17 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p,
/* Add new permanent fdb entry with RTM_NEWNEIGH */
int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr, u16 nlh_flags)
+ const unsigned char *addr, u16 vid, u16 nlh_flags)
{
struct net_bridge_port *p;
int err = 0;
struct net_port_vlans *pv;
- unsigned short vid = VLAN_N_VID;
if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
return -EINVAL;
}
- if (tb[NDA_VLAN]) {
- if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
- return -EINVAL;
- }
-
- vid = nla_get_u16(tb[NDA_VLAN]);
-
- if (!vid || vid >= VLAN_VID_MASK) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
- vid);
- return -EINVAL;
- }
- }
-
if (is_zero_ether_addr(addr)) {
pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n");
return -EINVAL;
@@ -845,7 +829,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
}
pv = nbp_get_vlan_info(p);
- if (vid != VLAN_N_VID) {
+ if (vid) {
if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
pr_info("bridge: RTM_NEWNEIGH with unconfigured "
"vlan %d on port %s\n", vid, dev->name);
@@ -903,27 +887,12 @@ static int __br_fdb_delete(struct net_bridge_port *p,
/* Remove neighbor entry with RTM_DELNEIGH */
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr)
+ const unsigned char *addr, u16 vid)
{
struct net_bridge_port *p;
int err;
struct net_port_vlans *pv;
- unsigned short vid = VLAN_N_VID;
-
- if (tb[NDA_VLAN]) {
- if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
- return -EINVAL;
- }
- vid = nla_get_u16(tb[NDA_VLAN]);
-
- if (!vid || vid >= VLAN_VID_MASK) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
- vid);
- return -EINVAL;
- }
- }
p = br_port_get_rtnl(dev);
if (p == NULL) {
pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
@@ -932,7 +901,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
}
pv = nbp_get_vlan_info(p);
- if (vid != VLAN_N_VID) {
+ if (vid) {
if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
pr_info("bridge: RTM_DELNEIGH with unconfigured "
"vlan %d on port %s\n", vid, dev->name);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 8f3f081..4f577c4 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -404,9 +404,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr, u16 vid, bool added_by_user);
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
- struct net_device *dev, const unsigned char *addr);
+ struct net_device *dev, const unsigned char *addr, u16 vid);
int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
- const unsigned char *addr, u16 nlh_flags);
+ const unsigned char *addr, u16 vid, u16 nlh_flags);
int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev, struct net_device *fdev, int idx);
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index a688268..f2a4b38 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -36,6 +36,7 @@
#include <linux/mutex.h>
#include <linux/if_addr.h>
#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
@@ -2312,7 +2313,7 @@ errout:
int ndo_dflt_fdb_add(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr,
+ const unsigned char *addr, u16 vid,
u16 flags)
{
int err = -EINVAL;
@@ -2338,6 +2339,28 @@ int ndo_dflt_fdb_add(struct ndmsg *ndm,
}
EXPORT_SYMBOL(ndo_dflt_fdb_add);
+static int fbd_vid_parse(struct nlattr *vlan_attr, u16 *p_vid)
+{
+ u16 vid = 0;
+
+ if (vlan_attr) {
+ if (nla_len(vlan_attr) != sizeof(u16)) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid vlan\n");
+ return -EINVAL;
+ }
+
+ vid = nla_get_u16(vlan_attr);
+
+ if (!vid || vid >= VLAN_VID_MASK) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid vlan id %d\n",
+ vid);
+ return -EINVAL;
+ }
+ }
+ *p_vid = vid;
+ return 0;
+}
+
static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(skb->sk);
@@ -2345,6 +2368,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
struct nlattr *tb[NDA_MAX+1];
struct net_device *dev;
u8 *addr;
+ u16 vid;
int err;
err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
@@ -2370,6 +2394,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
addr = nla_data(tb[NDA_LLADDR]);
+ err = fbd_vid_parse(tb[NDA_VLAN], &vid);
+ if (err)
+ return err;
+
err = -EOPNOTSUPP;
/* Support fdb on master device the net/bridge default case */
@@ -2378,7 +2406,8 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
const struct net_device_ops *ops = br_dev->netdev_ops;
- err = ops->ndo_fdb_add(ndm, tb, dev, addr, nlh->nlmsg_flags);
+ err = ops->ndo_fdb_add(ndm, tb, dev, addr, vid,
+ nlh->nlmsg_flags);
if (err)
goto out;
else
@@ -2389,9 +2418,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
if ((ndm->ndm_flags & NTF_SELF)) {
if (dev->netdev_ops->ndo_fdb_add)
err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr,
+ vid,
nlh->nlmsg_flags);
else
- err = ndo_dflt_fdb_add(ndm, tb, dev, addr,
+ err = ndo_dflt_fdb_add(ndm, tb, dev, addr, vid,
nlh->nlmsg_flags);
if (!err) {
@@ -2409,7 +2439,7 @@ out:
int ndo_dflt_fdb_del(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr)
+ const unsigned char *addr, u16 vid)
{
int err = -EINVAL;
@@ -2438,6 +2468,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
struct net_device *dev;
int err = -EINVAL;
__u8 *addr;
+ u16 vid;
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;
@@ -2465,6 +2496,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
addr = nla_data(tb[NDA_LLADDR]);
+ err = fbd_vid_parse(tb[NDA_VLAN], &vid);
+ if (err)
+ return err;
+
err = -EOPNOTSUPP;
/* Support fdb on master device the net/bridge default case */
@@ -2474,7 +2509,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
const struct net_device_ops *ops = br_dev->netdev_ops;
if (ops->ndo_fdb_del)
- err = ops->ndo_fdb_del(ndm, tb, dev, addr);
+ err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid);
if (err)
goto out;
@@ -2485,9 +2520,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
/* Embedded bridge, macvlan, and any other device support */
if (ndm->ndm_flags & NTF_SELF) {
if (dev->netdev_ops->ndo_fdb_del)
- err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
+ err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr,
+ vid);
else
- err = ndo_dflt_fdb_del(ndm, tb, dev, addr);
+ err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
if (!err) {
rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
--
1.9.3
^ permalink raw reply related
* [patch net-next v3 03/17] net: rename netdev_phys_port_id to more generic name
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
So this can be reused for identification of other "items" as well.
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Reviewed-by: Thomas Graf <tgraf@suug.ch>
Acked-by: John Fastabend <john.r.fastabend@intel.com>
---
v1->v2->v3:
-no change
---
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 2 +-
drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +-
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +-
include/linux/netdevice.h | 16 ++++++++--------
net/core/dev.c | 2 +-
net/core/net-sysfs.c | 2 +-
net/core/rtnetlink.c | 6 +++---
8 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index c4bd025..336ef3c 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -12537,7 +12537,7 @@ static int bnx2x_validate_addr(struct net_device *dev)
}
static int bnx2x_get_phys_port_id(struct net_device *netdev,
- struct netdev_phys_port_id *ppid)
+ struct netdev_phys_item_id *ppid)
{
struct bnx2x *bp = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 5ed5e40..9ae4270 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -7511,7 +7511,7 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
#endif
static int i40e_get_phys_port_id(struct net_device *netdev,
- struct netdev_phys_port_id *ppid)
+ struct netdev_phys_item_id *ppid)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index b7c9978..1597fb0 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -2259,7 +2259,7 @@ static int mlx4_en_set_vf_link_state(struct net_device *dev, int vf, int link_st
#define PORT_ID_BYTE_LEN 8
static int mlx4_en_get_phys_port_id(struct net_device *dev,
- struct netdev_phys_port_id *ppid)
+ struct netdev_phys_item_id *ppid)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_dev *mdev = priv->mdev->dev;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 3227c80..1aa25b1 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -461,7 +461,7 @@ static void qlcnic_82xx_cancel_idc_work(struct qlcnic_adapter *adapter)
}
static int qlcnic_get_phys_port_id(struct net_device *netdev,
- struct netdev_phys_port_id *ppid)
+ struct netdev_phys_item_id *ppid)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_hardware_context *ahw = adapter->ahw;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index fab074e..5b491b3 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -754,13 +754,13 @@ struct netdev_fcoe_hbainfo {
};
#endif
-#define MAX_PHYS_PORT_ID_LEN 32
+#define MAX_PHYS_ITEM_ID_LEN 32
-/* This structure holds a unique identifier to identify the
- * physical port used by a netdevice.
+/* This structure holds a unique identifier to identify some
+ * physical item (port for example) used by a netdevice.
*/
-struct netdev_phys_port_id {
- unsigned char id[MAX_PHYS_PORT_ID_LEN];
+struct netdev_phys_item_id {
+ unsigned char id[MAX_PHYS_ITEM_ID_LEN];
unsigned char id_len;
};
@@ -976,7 +976,7 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* USB_CDC_NOTIFY_NETWORK_CONNECTION) should NOT implement this function.
*
* int (*ndo_get_phys_port_id)(struct net_device *dev,
- * struct netdev_phys_port_id *ppid);
+ * struct netdev_phys_item_id *ppid);
* Called to get ID of physical port of this device. If driver does
* not implement this, it is assumed that the hw is not able to have
* multiple net devices on single physical port.
@@ -1152,7 +1152,7 @@ struct net_device_ops {
int (*ndo_change_carrier)(struct net_device *dev,
bool new_carrier);
int (*ndo_get_phys_port_id)(struct net_device *dev,
- struct netdev_phys_port_id *ppid);
+ struct netdev_phys_item_id *ppid);
void (*ndo_add_vxlan_port)(struct net_device *dev,
sa_family_t sa_family,
__be16 port);
@@ -2866,7 +2866,7 @@ void dev_set_group(struct net_device *, int);
int dev_set_mac_address(struct net_device *, struct sockaddr *);
int dev_change_carrier(struct net_device *, bool new_carrier);
int dev_get_phys_port_id(struct net_device *dev,
- struct netdev_phys_port_id *ppid);
+ struct netdev_phys_item_id *ppid);
struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev);
struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret);
diff --git a/net/core/dev.c b/net/core/dev.c
index ac48362..0814a56 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5846,7 +5846,7 @@ EXPORT_SYMBOL(dev_change_carrier);
* Get device physical port ID
*/
int dev_get_phys_port_id(struct net_device *dev,
- struct netdev_phys_port_id *ppid)
+ struct netdev_phys_item_id *ppid)
{
const struct net_device_ops *ops = dev->netdev_ops;
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 1a24602..26c46f4 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -404,7 +404,7 @@ static ssize_t phys_port_id_show(struct device *dev,
return restart_syscall();
if (dev_isalive(netdev)) {
- struct netdev_phys_port_id ppid;
+ struct netdev_phys_item_id ppid;
ret = dev_get_phys_port_id(netdev, &ppid);
if (!ret)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index f2a4b38..0fb3a68 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -869,7 +869,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
+ rtnl_link_get_size(dev) /* IFLA_LINKINFO */
+ rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */
- + nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */
+ + nla_total_size(MAX_PHYS_ITEM_ID_LEN); /* IFLA_PHYS_PORT_ID */
}
static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev)
@@ -953,7 +953,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev,
static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev)
{
int err;
- struct netdev_phys_port_id ppid;
+ struct netdev_phys_item_id ppid;
err = dev_get_phys_port_id(dev, &ppid);
if (err) {
@@ -1197,7 +1197,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_PROMISCUITY] = { .type = NLA_U32 },
[IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 },
[IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 },
- [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
+ [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
[IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */
};
--
1.9.3
^ permalink raw reply related
* [patch net-next v3 04/17] net: introduce generic switch devices support
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
The goal of this is to provide a possibility to support various switch
chips. Drivers should implement relevant ndos to do so. Now there is
only one ndo defined:
- for getting physical switch id is in place.
Note that user can use random port netdevice to access the switch.
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Reviewed-by: Thomas Graf <tgraf@suug.ch>
---
v2->v3:
-fixed documentation typo pointed out by M. Braun
-changed "sw" string to "switch" to avoid confusion
v1->v2:
-no change
---
Documentation/networking/switchdev.txt | 59 ++++++++++++++++++++++++++++++++++
MAINTAINERS | 7 ++++
include/linux/netdevice.h | 10 ++++++
include/net/switchdev.h | 30 +++++++++++++++++
net/Kconfig | 1 +
net/Makefile | 3 ++
net/switchdev/Kconfig | 13 ++++++++
net/switchdev/Makefile | 5 +++
net/switchdev/switchdev.c | 33 +++++++++++++++++++
9 files changed, 161 insertions(+)
create mode 100644 Documentation/networking/switchdev.txt
create mode 100644 include/net/switchdev.h
create mode 100644 net/switchdev/Kconfig
create mode 100644 net/switchdev/Makefile
create mode 100644 net/switchdev/switchdev.c
diff --git a/Documentation/networking/switchdev.txt b/Documentation/networking/switchdev.txt
new file mode 100644
index 0000000..f981a92
--- /dev/null
+++ b/Documentation/networking/switchdev.txt
@@ -0,0 +1,59 @@
+Switch (and switch-ish) device drivers HOWTO
+===========================
+
+Please note that the word "switch" is here used in very generic meaning.
+This include devices supporting L2/L3 but also various flow offloading chips,
+including switches embedded into SR-IOV NICs.
+
+Lets describe a topology a bit. Imagine the following example:
+
+ +----------------------------+ +---------------+
+ | SOME switch chip | | CPU |
+ +----------------------------+ +---------------+
+ port1 port2 port3 port4 MNGMNT | PCI-E |
+ | | | | | +---------------+
+ PHY PHY | | | | NIC0 NIC1
+ | | | | | |
+ | | +- PCI-E -+ | |
+ | +------- MII -------+ |
+ +------------- MII ------------+
+
+In this example, there are two independent lines between the switch silicon
+and CPU. NIC0 and NIC1 drivers are not aware of a switch presence. They are
+separate from the switch driver. SOME switch chip is by managed by a driver
+via PCI-E device MNGMNT. Note that MNGMNT device, NIC0 and NIC1 may be
+connected to some other type of bus.
+
+Now, for the previous example show the representation in kernel:
+
+ +----------------------------+ +---------------+
+ | SOME switch chip | | CPU |
+ +----------------------------+ +---------------+
+ sw0p0 sw0p1 sw0p2 sw0p3 MNGMNT | PCI-E |
+ | | | | | +---------------+
+ PHY PHY | | | | eth0 eth1
+ | | | | | |
+ | | +- PCI-E -+ | |
+ | +------- MII -------+ |
+ +------------- MII ------------+
+
+Lets call the example switch driver for SOME switch chip "SOMEswitch". This
+driver takes care of PCI-E device MNGMNT. There is a netdevice instance sw0pX
+created for each port of a switch. These netdevices are instances
+of "SOMEswitch" driver. sw0pX netdevices serve as a "representation"
+of the switch chip. eth0 and eth1 are instances of some other existing driver.
+
+The only difference of the switch-port netdevice from the ordinary netdevice
+is that is implements couple more NDOs:
+
+ ndo_switch_parent_id_get - This returns the same ID for two port netdevices
+ of the same physical switch chip. This is
+ mandatory to be implemented by all switch drivers
+ and serves the caller for recognition of a port
+ netdevice.
+ ndo_switch_parent_* - Functions that serve for a manipulation of the switch
+ chip itself (it can be though of as a "parent" of the
+ port, therefore the name). They are not port-specific.
+ Caller might use arbitrary port netdevice of the same
+ switch and it will make no difference.
+ ndo_switch_port_* - Functions that serve for a port-specific manipulation.
diff --git a/MAINTAINERS b/MAINTAINERS
index a545d68..05addb6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9058,6 +9058,13 @@ F: lib/swiotlb.c
F: arch/*/kernel/pci-swiotlb.c
F: include/linux/swiotlb.h
+SWITCHDEV
+M: Jiri Pirko <jiri@resnulli.us>
+L: netdev@vger.kernel.org
+S: Supported
+F: net/switchdev/
+F: include/net/switchdev.h
+
SYNOPSYS ARC ARCHITECTURE
M: Vineet Gupta <vgupta@synopsys.com>
S: Supported
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 5b491b3..ce096dc 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1018,6 +1018,12 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* performing GSO on a packet. The device returns true if it is
* able to GSO the packet, false otherwise. If the return value is
* false the stack will do software GSO.
+ *
+ * int (*ndo_switch_parent_id_get)(struct net_device *dev,
+ * struct netdev_phys_item_id *psid);
+ * Called to get an ID of the switch chip this port is part of.
+ * If driver implements this, it indicates that it represents a port
+ * of a switch chip.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
@@ -1171,6 +1177,10 @@ struct net_device_ops {
int (*ndo_get_lock_subclass)(struct net_device *dev);
bool (*ndo_gso_check) (struct sk_buff *skb,
struct net_device *dev);
+#ifdef CONFIG_NET_SWITCHDEV
+ int (*ndo_switch_parent_id_get)(struct net_device *dev,
+ struct netdev_phys_item_id *psid);
+#endif
};
/**
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
new file mode 100644
index 0000000..7a52360
--- /dev/null
+++ b/include/net/switchdev.h
@@ -0,0 +1,30 @@
+/*
+ * include/net/switchdev.h - Switch device API
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ * 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.
+ */
+#ifndef _LINUX_SWITCHDEV_H_
+#define _LINUX_SWITCHDEV_H_
+
+#include <linux/netdevice.h>
+
+#ifdef CONFIG_NET_SWITCHDEV
+
+int netdev_switch_parent_id_get(struct net_device *dev,
+ struct netdev_phys_item_id *psid);
+
+#else
+
+static inline int netdev_switch_parent_id_get(struct net_device *dev,
+ struct netdev_phys_item_id *psid)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif
+
+#endif /* _LINUX_SWITCHDEV_H_ */
diff --git a/net/Kconfig b/net/Kconfig
index 99815b5..ff9ffc1 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -228,6 +228,7 @@ source "net/vmw_vsock/Kconfig"
source "net/netlink/Kconfig"
source "net/mpls/Kconfig"
source "net/hsr/Kconfig"
+source "net/switchdev/Kconfig"
config RPS
boolean
diff --git a/net/Makefile b/net/Makefile
index 7ed1970..95fc694 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -73,3 +73,6 @@ obj-$(CONFIG_OPENVSWITCH) += openvswitch/
obj-$(CONFIG_VSOCKETS) += vmw_vsock/
obj-$(CONFIG_NET_MPLS_GSO) += mpls/
obj-$(CONFIG_HSR) += hsr/
+ifneq ($(CONFIG_NET_SWITCHDEV),)
+obj-y += switchdev/
+endif
diff --git a/net/switchdev/Kconfig b/net/switchdev/Kconfig
new file mode 100644
index 0000000..1557545
--- /dev/null
+++ b/net/switchdev/Kconfig
@@ -0,0 +1,13 @@
+#
+# Configuration for Switch device support
+#
+
+config NET_SWITCHDEV
+ boolean "Switch (and switch-ish) device support (EXPERIMENTAL)"
+ depends on INET
+ ---help---
+ This module provides glue between core networking code and device
+ drivers in order to support hardware switch chips in very generic
+ meaning of the word "switch". This include devices supporting L2/L3 but
+ also various flow offloading chips, including switches embedded into
+ SR-IOV NICs.
diff --git a/net/switchdev/Makefile b/net/switchdev/Makefile
new file mode 100644
index 0000000..5ed63ed
--- /dev/null
+++ b/net/switchdev/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Switch device API
+#
+
+obj-$(CONFIG_NET_SWITCHDEV) += switchdev.o
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
new file mode 100644
index 0000000..66973de
--- /dev/null
+++ b/net/switchdev/switchdev.c
@@ -0,0 +1,33 @@
+/*
+ * net/switchdev/switchdev.c - Switch device API
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <net/switchdev.h>
+
+/**
+ * netdev_switch_parent_id_get - Get ID of a switch
+ * @dev: port device
+ * @psid: switch ID
+ *
+ * Get ID of a switch this port is part of.
+ */
+int netdev_switch_parent_id_get(struct net_device *dev,
+ struct netdev_phys_item_id *psid)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if (!ops->ndo_switch_parent_id_get)
+ return -EOPNOTSUPP;
+ return ops->ndo_switch_parent_id_get(dev, psid);
+}
+EXPORT_SYMBOL(netdev_switch_parent_id_get);
--
1.9.3
^ permalink raw reply related
* [patch net-next v3 06/17] net-sysfs: expose physical switch id for particular device
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Reviewed-by: Thomas Graf <tgraf@suug.ch>
Acked-by: John Fastabend <john.r.fastabend@intel.com>
---
v2->v3:
-changed "sw" string to "switch" to avoid confusion
v1->v2:
-no change
---
net/core/net-sysfs.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 26c46f4..9993412 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -12,6 +12,7 @@
#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
+#include <net/switchdev.h>
#include <linux/if_arp.h>
#include <linux/slab.h>
#include <linux/nsproxy.h>
@@ -416,6 +417,28 @@ static ssize_t phys_port_id_show(struct device *dev,
}
static DEVICE_ATTR_RO(phys_port_id);
+static ssize_t phys_switch_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ ssize_t ret = -EINVAL;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ if (dev_isalive(netdev)) {
+ struct netdev_phys_item_id ppid;
+
+ ret = netdev_switch_parent_id_get(netdev, &ppid);
+ if (!ret)
+ ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id);
+ }
+ rtnl_unlock();
+
+ return ret;
+}
+static DEVICE_ATTR_RO(phys_switch_id);
+
static struct attribute *net_class_attrs[] = {
&dev_attr_netdev_group.attr,
&dev_attr_type.attr,
@@ -441,6 +464,7 @@ static struct attribute *net_class_attrs[] = {
&dev_attr_tx_queue_len.attr,
&dev_attr_gro_flush_timeout.attr,
&dev_attr_phys_port_id.attr,
+ &dev_attr_phys_switch_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(net_class);
--
1.9.3
^ permalink raw reply related
* [patch net-next v3 08/17] bridge: call netdev_sw_port_stp_update when bridge port STP status changes
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
From: Scott Feldman <sfeldma@gmail.com>
To notify switch driver of change in STP state of bridge port, add new
.ndo op and provide switchdev wrapper func to call ndo op. Use it in bridge
code then.
Signed-off-by: Scott Feldman <sfeldma@gmail.com>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
v2->v3:
-changed "sw" string to "switch" to avoid confusion
v1->v2:
-no change
---
include/linux/netdevice.h | 5 +++++
include/net/switchdev.h | 7 +++++++
net/bridge/br_stp.c | 2 ++
net/switchdev/switchdev.c | 19 +++++++++++++++++++
4 files changed, 33 insertions(+)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ce096dc..66cb64e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1024,6 +1024,9 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* Called to get an ID of the switch chip this port is part of.
* If driver implements this, it indicates that it represents a port
* of a switch chip.
+ * int (*ndo_switch_port_stp_update)(struct net_device *dev, u8 state);
+ * Called to notify switch device port of bridge port STP
+ * state change.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
@@ -1180,6 +1183,8 @@ struct net_device_ops {
#ifdef CONFIG_NET_SWITCHDEV
int (*ndo_switch_parent_id_get)(struct net_device *dev,
struct netdev_phys_item_id *psid);
+ int (*ndo_switch_port_stp_update)(struct net_device *dev,
+ u8 state);
#endif
};
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 7a52360..8a6d164 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -16,6 +16,7 @@
int netdev_switch_parent_id_get(struct net_device *dev,
struct netdev_phys_item_id *psid);
+int netdev_switch_port_stp_update(struct net_device *dev, u8 state);
#else
@@ -25,6 +26,12 @@ static inline int netdev_switch_parent_id_get(struct net_device *dev,
return -EOPNOTSUPP;
}
+static inline int netdev_switch_port_stp_update(struct net_device *dev,
+ u8 state)
+{
+ return -EOPNOTSUPP;
+}
+
#endif
#endif /* _LINUX_SWITCHDEV_H_ */
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 2b047bc..35e016c 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -12,6 +12,7 @@
*/
#include <linux/kernel.h>
#include <linux/rculist.h>
+#include <net/switchdev.h>
#include "br_private.h"
#include "br_private_stp.h"
@@ -39,6 +40,7 @@ void br_log_state(const struct net_bridge_port *p)
void br_set_state(struct net_bridge_port *p, unsigned int state)
{
p->state = state;
+ netdev_switch_port_stp_update(p->dev, state);
}
/* called under bridge lock */
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 66973de..d162b21 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -31,3 +31,22 @@ int netdev_switch_parent_id_get(struct net_device *dev,
return ops->ndo_switch_parent_id_get(dev, psid);
}
EXPORT_SYMBOL(netdev_switch_parent_id_get);
+
+/**
+ * netdev_switch_port_stp_update - Notify switch device port of STP
+ * state change
+ * @dev: port device
+ * @state: port STP state
+ *
+ * Notify switch device port of bridge port STP state change.
+ */
+int netdev_switch_port_stp_update(struct net_device *dev, u8 state)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if (!ops->ndo_switch_port_stp_update)
+ return -EOPNOTSUPP;
+ WARN_ON(!ops->ndo_switch_parent_id_get);
+ return ops->ndo_switch_port_stp_update(dev, state);
+}
+EXPORT_SYMBOL(netdev_switch_port_stp_update);
--
1.9.3
^ permalink raw reply related
* [patch net-next v3 07/17] rocker: introduce rocker switch driver
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
This patch introduces the first driver to benefit from the switchdev
infrastructure and to implement newly introduced switch ndos. This is a
driver for emulated switch chip implemented in qemu:
https://github.com/sfeldma/qemu-rocker/
This patch is a result of joint work with Scott Feldman.
Signed-off-by: Scott Feldman <sfeldma@gmail.com>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Reviewed-by: Thomas Graf <tgraf@suug.ch>
Reviewed-by: John Fastabend <john.r.fastabend@intel.com>
---
v2->v3:
-changed "sw" string to "switch" to avoid confusion
v1->v2:
-fixed "unsigned" -> "unsigned int" as suggested by DaveM
-removed "ifdef CONFIG_NET_SWITCHDEV" in ndos as suggested by DaveM
-removed unnecessary "__packed" annotations as suggested by DaveM
---
MAINTAINERS | 7 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/rocker/Kconfig | 27 +
drivers/net/ethernet/rocker/Makefile | 5 +
drivers/net/ethernet/rocker/rocker.c | 2060 ++++++++++++++++++++++++++++++++++
drivers/net/ethernet/rocker/rocker.h | 427 +++++++
7 files changed, 2528 insertions(+)
create mode 100644 drivers/net/ethernet/rocker/Kconfig
create mode 100644 drivers/net/ethernet/rocker/Makefile
create mode 100644 drivers/net/ethernet/rocker/rocker.c
create mode 100644 drivers/net/ethernet/rocker/rocker.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 05addb6..9b1bd7b7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7864,6 +7864,13 @@ F: drivers/hid/hid-roccat*
F: include/linux/hid-roccat*
F: Documentation/ABI/*/sysfs-driver-hid-roccat*
+ROCKER DRIVER
+M: Jiri Pirko <jiri@resnulli.us>
+M: Scott Feldman <sfeldma@gmail.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/rocker/
+
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 1ed1fbb..df76050 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -155,6 +155,7 @@ source "drivers/net/ethernet/qualcomm/Kconfig"
source "drivers/net/ethernet/realtek/Kconfig"
source "drivers/net/ethernet/renesas/Kconfig"
source "drivers/net/ethernet/rdc/Kconfig"
+source "drivers/net/ethernet/rocker/Kconfig"
config S6GMAC
tristate "S6105 GMAC ethernet support"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 6e0b629..bf56f8b 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/
obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_SH_ETH) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
+obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/
obj-$(CONFIG_S6GMAC) += s6gmac.o
obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
diff --git a/drivers/net/ethernet/rocker/Kconfig b/drivers/net/ethernet/rocker/Kconfig
new file mode 100644
index 0000000..11a850e
--- /dev/null
+++ b/drivers/net/ethernet/rocker/Kconfig
@@ -0,0 +1,27 @@
+#
+# Rocker device configuration
+#
+
+config NET_VENDOR_ROCKER
+ bool "Rocker devices"
+ default y
+ ---help---
+ If you have a network device belonging to this class, say Y.
+
+ 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 Rocker devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_ROCKER
+
+config ROCKER
+ tristate "Rocker switch driver (EXPERIMENTAL)"
+ depends on PCI && NET_SWITCHDEV
+ ---help---
+ This driver supports Rocker switch device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rocker.
+
+endif # NET_VENDOR_ROCKER
diff --git a/drivers/net/ethernet/rocker/Makefile b/drivers/net/ethernet/rocker/Makefile
new file mode 100644
index 0000000..f85fb12
--- /dev/null
+++ b/drivers/net/ethernet/rocker/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Rocker network device drivers.
+#
+
+obj-$(CONFIG_ROCKER) += rocker.o
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
new file mode 100644
index 0000000..a53011c
--- /dev/null
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -0,0 +1,2060 @@
+/*
+ * drivers/net/ethernet/rocker/rocker.c - Rocker switch device driver
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/sort.h>
+#include <linux/random.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <net/switchdev.h>
+#include <net/rtnetlink.h>
+#include <asm-generic/io-64-nonatomic-lo-hi.h>
+#include <generated/utsrelease.h>
+
+#include "rocker.h"
+
+static const char rocker_driver_name[] = "rocker";
+
+static const struct pci_device_id rocker_pci_id_table[] = {
+ {PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_ROCKER), 0},
+ {0, }
+};
+
+struct rocker_desc_info {
+ char *data; /* mapped */
+ size_t data_size;
+ size_t tlv_size;
+ struct rocker_desc *desc;
+ DEFINE_DMA_UNMAP_ADDR(mapaddr);
+};
+
+struct rocker_dma_ring_info {
+ size_t size;
+ u32 head;
+ u32 tail;
+ struct rocker_desc *desc; /* mapped */
+ dma_addr_t mapaddr;
+ struct rocker_desc_info *desc_info;
+ unsigned int type;
+};
+
+struct rocker;
+
+struct rocker_port {
+ struct net_device *dev;
+ struct rocker *rocker;
+ unsigned int port_number;
+ u32 lport;
+ struct napi_struct napi_tx;
+ struct napi_struct napi_rx;
+ struct rocker_dma_ring_info tx_ring;
+ struct rocker_dma_ring_info rx_ring;
+};
+
+struct rocker {
+ struct pci_dev *pdev;
+ u8 __iomem *hw_addr;
+ struct msix_entry *msix_entries;
+ unsigned int port_count;
+ struct rocker_port **ports;
+ struct {
+ u64 id;
+ } hw;
+ spinlock_t cmd_ring_lock;
+ struct rocker_dma_ring_info cmd_ring;
+ struct rocker_dma_ring_info event_ring;
+};
+
+struct rocker_wait {
+ wait_queue_head_t wait;
+ bool done;
+ bool nowait;
+};
+
+static void rocker_wait_reset(struct rocker_wait *wait)
+{
+ wait->done = false;
+ wait->nowait = false;
+}
+
+static void rocker_wait_init(struct rocker_wait *wait)
+{
+ init_waitqueue_head(&wait->wait);
+ rocker_wait_reset(wait);
+}
+
+static struct rocker_wait *rocker_wait_create(gfp_t gfp)
+{
+ struct rocker_wait *wait;
+
+ wait = kmalloc(sizeof(*wait), gfp);
+ if (!wait)
+ return NULL;
+ rocker_wait_init(wait);
+ return wait;
+}
+
+static void rocker_wait_destroy(struct rocker_wait *work)
+{
+ kfree(work);
+}
+
+static bool rocker_wait_event_timeout(struct rocker_wait *wait,
+ unsigned long timeout)
+{
+ wait_event_timeout(wait->wait, wait->done, HZ / 10);
+ if (!wait->done)
+ return false;
+ return true;
+}
+
+static void rocker_wait_wake_up(struct rocker_wait *wait)
+{
+ wait->done = true;
+ wake_up(&wait->wait);
+}
+
+static u32 rocker_msix_vector(struct rocker *rocker, unsigned int vector)
+{
+ return rocker->msix_entries[vector].vector;
+}
+
+static u32 rocker_msix_tx_vector(struct rocker_port *rocker_port)
+{
+ return rocker_msix_vector(rocker_port->rocker,
+ ROCKER_MSIX_VEC_TX(rocker_port->port_number));
+}
+
+static u32 rocker_msix_rx_vector(struct rocker_port *rocker_port)
+{
+ return rocker_msix_vector(rocker_port->rocker,
+ ROCKER_MSIX_VEC_RX(rocker_port->port_number));
+}
+
+#define rocker_write32(rocker, reg, val) \
+ writel((val), (rocker)->hw_addr + (ROCKER_ ## reg))
+#define rocker_read32(rocker, reg) \
+ readl((rocker)->hw_addr + (ROCKER_ ## reg))
+#define rocker_write64(rocker, reg, val) \
+ writeq((val), (rocker)->hw_addr + (ROCKER_ ## reg))
+#define rocker_read64(rocker, reg) \
+ readq((rocker)->hw_addr + (ROCKER_ ## reg))
+
+/*****************************
+ * HW basic testing functions
+ *****************************/
+
+static int rocker_reg_test(struct rocker *rocker)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ u64 test_reg;
+ u64 rnd;
+
+ rnd = prandom_u32();
+ rnd >>= 1;
+ rocker_write32(rocker, TEST_REG, rnd);
+ test_reg = rocker_read32(rocker, TEST_REG);
+ if (test_reg != rnd * 2) {
+ dev_err(&pdev->dev, "unexpected 32bit register value %08llx, expected %08llx\n",
+ test_reg, rnd * 2);
+ return -EIO;
+ }
+
+ rnd = prandom_u32();
+ rnd <<= 31;
+ rnd |= prandom_u32();
+ rocker_write64(rocker, TEST_REG64, rnd);
+ test_reg = rocker_read64(rocker, TEST_REG64);
+ if (test_reg != rnd * 2) {
+ dev_err(&pdev->dev, "unexpected 64bit register value %16llx, expected %16llx\n",
+ test_reg, rnd * 2);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int rocker_dma_test_one(struct rocker *rocker, struct rocker_wait *wait,
+ u32 test_type, dma_addr_t dma_handle,
+ unsigned char *buf, unsigned char *expect,
+ size_t size)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ int i;
+
+ rocker_wait_reset(wait);
+ rocker_write32(rocker, TEST_DMA_CTRL, test_type);
+
+ if (!rocker_wait_event_timeout(wait, HZ / 10)) {
+ dev_err(&pdev->dev, "no interrupt received within a timeout\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (buf[i] != expect[i]) {
+ dev_err(&pdev->dev, "unexpected memory content %02x at byte %x\n, %02x expected",
+ buf[i], i, expect[i]);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+#define ROCKER_TEST_DMA_BUF_SIZE (PAGE_SIZE * 4)
+#define ROCKER_TEST_DMA_FILL_PATTERN 0x96
+
+static int rocker_dma_test_offset(struct rocker *rocker,
+ struct rocker_wait *wait, int offset)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ unsigned char *alloc;
+ unsigned char *buf;
+ unsigned char *expect;
+ dma_addr_t dma_handle;
+ int i;
+ int err;
+
+ alloc = kzalloc(ROCKER_TEST_DMA_BUF_SIZE * 2 + offset,
+ GFP_KERNEL | GFP_DMA);
+ if (!alloc)
+ return -ENOMEM;
+ buf = alloc + offset;
+ expect = buf + ROCKER_TEST_DMA_BUF_SIZE;
+
+ dma_handle = pci_map_single(pdev, buf, ROCKER_TEST_DMA_BUF_SIZE,
+ PCI_DMA_BIDIRECTIONAL);
+ if (pci_dma_mapping_error(pdev, dma_handle)) {
+ err = -EIO;
+ goto free_alloc;
+ }
+
+ rocker_write64(rocker, TEST_DMA_ADDR, dma_handle);
+ rocker_write32(rocker, TEST_DMA_SIZE, ROCKER_TEST_DMA_BUF_SIZE);
+
+ memset(expect, ROCKER_TEST_DMA_FILL_PATTERN, ROCKER_TEST_DMA_BUF_SIZE);
+ err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_FILL,
+ dma_handle, buf, expect,
+ ROCKER_TEST_DMA_BUF_SIZE);
+ if (err)
+ goto unmap;
+
+ memset(expect, 0, ROCKER_TEST_DMA_BUF_SIZE);
+ err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_CLEAR,
+ dma_handle, buf, expect,
+ ROCKER_TEST_DMA_BUF_SIZE);
+ if (err)
+ goto unmap;
+
+ prandom_bytes(buf, ROCKER_TEST_DMA_BUF_SIZE);
+ for (i = 0; i < ROCKER_TEST_DMA_BUF_SIZE; i++)
+ expect[i] = ~buf[i];
+ err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_INVERT,
+ dma_handle, buf, expect,
+ ROCKER_TEST_DMA_BUF_SIZE);
+ if (err)
+ goto unmap;
+
+unmap:
+ pci_unmap_single(pdev, dma_handle, ROCKER_TEST_DMA_BUF_SIZE,
+ PCI_DMA_BIDIRECTIONAL);
+free_alloc:
+ kfree(alloc);
+
+ return err;
+}
+
+static int rocker_dma_test(struct rocker *rocker, struct rocker_wait *wait)
+{
+ int i;
+ int err;
+
+ for (i = 0; i < 8; i++) {
+ err = rocker_dma_test_offset(rocker, wait, i);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static irqreturn_t rocker_test_irq_handler(int irq, void *dev_id)
+{
+ struct rocker_wait *wait = dev_id;
+
+ rocker_wait_wake_up(wait);
+
+ return IRQ_HANDLED;
+}
+
+static int rocker_basic_hw_test(struct rocker *rocker)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ struct rocker_wait wait;
+ int err;
+
+ err = rocker_reg_test(rocker);
+ if (err) {
+ dev_err(&pdev->dev, "reg test failed\n");
+ return err;
+ }
+
+ err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST),
+ rocker_test_irq_handler, 0,
+ rocker_driver_name, &wait);
+ if (err) {
+ dev_err(&pdev->dev, "cannot assign test irq\n");
+ return err;
+ }
+
+ rocker_wait_init(&wait);
+ rocker_write32(rocker, TEST_IRQ, ROCKER_MSIX_VEC_TEST);
+
+ if (!rocker_wait_event_timeout(&wait, HZ / 10)) {
+ dev_err(&pdev->dev, "no interrupt received within a timeout\n");
+ err = -EIO;
+ goto free_irq;
+ }
+
+ err = rocker_dma_test(rocker, &wait);
+ if (err)
+ dev_err(&pdev->dev, "dma test failed\n");
+
+free_irq:
+ free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST), &wait);
+ return err;
+}
+
+/******
+ * TLV
+ ******/
+
+#define ROCKER_TLV_ALIGNTO 8U
+#define ROCKER_TLV_ALIGN(len) \
+ (((len) + ROCKER_TLV_ALIGNTO - 1) & ~(ROCKER_TLV_ALIGNTO - 1))
+#define ROCKER_TLV_HDRLEN ROCKER_TLV_ALIGN(sizeof(struct rocker_tlv))
+
+/* <------- ROCKER_TLV_HDRLEN -------> <--- ROCKER_TLV_ALIGN(payload) --->
+ * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+
+ * | Header | Pad | Payload | Pad |
+ * | (struct rocker_tlv) | ing | | ing |
+ * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+
+ * <--------------------------- tlv->len -------------------------->
+ */
+
+static struct rocker_tlv *rocker_tlv_next(const struct rocker_tlv *tlv,
+ int *remaining)
+{
+ int totlen = ROCKER_TLV_ALIGN(tlv->len);
+
+ *remaining -= totlen;
+ return (struct rocker_tlv *) ((char *) tlv + totlen);
+}
+
+static int rocker_tlv_ok(const struct rocker_tlv *tlv, int remaining)
+{
+ return remaining >= (int) ROCKER_TLV_HDRLEN &&
+ tlv->len >= ROCKER_TLV_HDRLEN &&
+ tlv->len <= remaining;
+}
+
+#define rocker_tlv_for_each(pos, head, len, rem) \
+ for (pos = head, rem = len; \
+ rocker_tlv_ok(pos, rem); \
+ pos = rocker_tlv_next(pos, &(rem)))
+
+#define rocker_tlv_for_each_nested(pos, tlv, rem) \
+ rocker_tlv_for_each(pos, rocker_tlv_data(tlv), \
+ rocker_tlv_len(tlv), rem)
+
+static int rocker_tlv_attr_size(int payload)
+{
+ return ROCKER_TLV_HDRLEN + payload;
+}
+
+static int rocker_tlv_total_size(int payload)
+{
+ return ROCKER_TLV_ALIGN(rocker_tlv_attr_size(payload));
+}
+
+static int rocker_tlv_padlen(int payload)
+{
+ return rocker_tlv_total_size(payload) - rocker_tlv_attr_size(payload);
+}
+
+static int rocker_tlv_type(const struct rocker_tlv *tlv)
+{
+ return tlv->type;
+}
+
+static void *rocker_tlv_data(const struct rocker_tlv *tlv)
+{
+ return (char *) tlv + ROCKER_TLV_HDRLEN;
+}
+
+static int rocker_tlv_len(const struct rocker_tlv *tlv)
+{
+ return tlv->len - ROCKER_TLV_HDRLEN;
+}
+
+static u8 rocker_tlv_get_u8(const struct rocker_tlv *tlv)
+{
+ return *(u8 *) rocker_tlv_data(tlv);
+}
+
+static u16 rocker_tlv_get_u16(const struct rocker_tlv *tlv)
+{
+ return *(u16 *) rocker_tlv_data(tlv);
+}
+
+static u32 rocker_tlv_get_u32(const struct rocker_tlv *tlv)
+{
+ return *(u32 *) rocker_tlv_data(tlv);
+}
+
+static u64 rocker_tlv_get_u64(const struct rocker_tlv *tlv)
+{
+ return *(u64 *) rocker_tlv_data(tlv);
+}
+
+static void rocker_tlv_parse(struct rocker_tlv **tb, int maxtype,
+ const char *buf, int buf_len)
+{
+ const struct rocker_tlv *tlv;
+ const struct rocker_tlv *head = (const struct rocker_tlv *) buf;
+ int rem;
+
+ memset(tb, 0, sizeof(struct rocker_tlv *) * (maxtype + 1));
+
+ rocker_tlv_for_each(tlv, head, buf_len, rem) {
+ u32 type = rocker_tlv_type(tlv);
+
+ if (type > 0 && type <= maxtype)
+ tb[type] = (struct rocker_tlv *) tlv;
+ }
+}
+
+static void rocker_tlv_parse_nested(struct rocker_tlv **tb, int maxtype,
+ const struct rocker_tlv *tlv)
+{
+ rocker_tlv_parse(tb, maxtype, rocker_tlv_data(tlv),
+ rocker_tlv_len(tlv));
+}
+
+static void rocker_tlv_parse_desc(struct rocker_tlv **tb, int maxtype,
+ struct rocker_desc_info *desc_info)
+{
+ rocker_tlv_parse(tb, maxtype, desc_info->data,
+ desc_info->desc->tlv_size);
+}
+
+static struct rocker_tlv *rocker_tlv_start(struct rocker_desc_info *desc_info)
+{
+ return (struct rocker_tlv *) ((char *) desc_info->data +
+ desc_info->tlv_size);
+}
+
+static int rocker_tlv_put(struct rocker_desc_info *desc_info,
+ int attrtype, int attrlen, const void *data)
+{
+ int tail_room = desc_info->data_size - desc_info->tlv_size;
+ int total_size = rocker_tlv_total_size(attrlen);
+ struct rocker_tlv *tlv;
+
+ if (unlikely(tail_room < total_size))
+ return -EMSGSIZE;
+
+ tlv = rocker_tlv_start(desc_info);
+ desc_info->tlv_size += total_size;
+ tlv->type = attrtype;
+ tlv->len = rocker_tlv_attr_size(attrlen);
+ memcpy(rocker_tlv_data(tlv), data, attrlen);
+ memset((char *) tlv + tlv->len, 0, rocker_tlv_padlen(attrlen));
+ return 0;
+}
+
+static int rocker_tlv_put_u8(struct rocker_desc_info *desc_info,
+ int attrtype, u8 value)
+{
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u8), &value);
+}
+
+static int rocker_tlv_put_u16(struct rocker_desc_info *desc_info,
+ int attrtype, u16 value)
+{
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u16), &value);
+}
+
+static int rocker_tlv_put_u32(struct rocker_desc_info *desc_info,
+ int attrtype, u32 value)
+{
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u32), &value);
+}
+
+static int rocker_tlv_put_u64(struct rocker_desc_info *desc_info,
+ int attrtype, u64 value)
+{
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u64), &value);
+}
+
+static struct rocker_tlv *
+rocker_tlv_nest_start(struct rocker_desc_info *desc_info, int attrtype)
+{
+ struct rocker_tlv *start = rocker_tlv_start(desc_info);
+
+ if (rocker_tlv_put(desc_info, attrtype, 0, NULL) < 0)
+ return NULL;
+
+ return start;
+}
+
+static void rocker_tlv_nest_end(struct rocker_desc_info *desc_info,
+ struct rocker_tlv *start)
+{
+ start->len = (char *) rocker_tlv_start(desc_info) - (char *) start;
+}
+
+static void rocker_tlv_nest_cancel(struct rocker_desc_info *desc_info,
+ struct rocker_tlv *start)
+{
+ desc_info->tlv_size = (char *) start - desc_info->data;
+}
+
+/******************************************
+ * DMA rings and descriptors manipulations
+ ******************************************/
+
+static u32 __pos_inc(u32 pos, size_t limit)
+{
+ return ++pos == limit ? 0 : pos;
+}
+
+static int rocker_desc_err(struct rocker_desc_info *desc_info)
+{
+ return -(desc_info->desc->comp_err & ~ROCKER_DMA_DESC_COMP_ERR_GEN);
+}
+
+static void rocker_desc_gen_clear(struct rocker_desc_info *desc_info)
+{
+ desc_info->desc->comp_err &= ~ROCKER_DMA_DESC_COMP_ERR_GEN;
+}
+
+static bool rocker_desc_gen(struct rocker_desc_info *desc_info)
+{
+ u32 comp_err = desc_info->desc->comp_err;
+
+ return comp_err & ROCKER_DMA_DESC_COMP_ERR_GEN ? true : false;
+}
+
+static void *rocker_desc_cookie_ptr_get(struct rocker_desc_info *desc_info)
+{
+ return (void *) desc_info->desc->cookie;
+}
+
+static void rocker_desc_cookie_ptr_set(struct rocker_desc_info *desc_info,
+ void *ptr)
+{
+ desc_info->desc->cookie = (long) ptr;
+}
+
+static struct rocker_desc_info *
+rocker_desc_head_get(struct rocker_dma_ring_info *info)
+{
+ static struct rocker_desc_info *desc_info;
+ u32 head = __pos_inc(info->head, info->size);
+
+ desc_info = &info->desc_info[info->head];
+ if (head == info->tail)
+ return NULL; /* ring full */
+ desc_info->tlv_size = 0;
+ return desc_info;
+}
+
+static void rocker_desc_commit(struct rocker_desc_info *desc_info)
+{
+ desc_info->desc->buf_size = desc_info->data_size;
+ desc_info->desc->tlv_size = desc_info->tlv_size;
+}
+
+static void rocker_desc_head_set(struct rocker *rocker,
+ struct rocker_dma_ring_info *info,
+ struct rocker_desc_info *desc_info)
+{
+ u32 head = __pos_inc(info->head, info->size);
+
+ BUG_ON(head == info->tail);
+ rocker_desc_commit(desc_info);
+ info->head = head;
+ rocker_write32(rocker, DMA_DESC_HEAD(info->type), head);
+}
+
+static struct rocker_desc_info *
+rocker_desc_tail_get(struct rocker_dma_ring_info *info)
+{
+ static struct rocker_desc_info *desc_info;
+
+ if (info->tail == info->head)
+ return NULL; /* nothing to be done between head and tail */
+ desc_info = &info->desc_info[info->tail];
+ if (!rocker_desc_gen(desc_info))
+ return NULL; /* gen bit not set, desc is not ready yet */
+ info->tail = __pos_inc(info->tail, info->size);
+ desc_info->tlv_size = desc_info->desc->tlv_size;
+ return desc_info;
+}
+
+static void rocker_dma_ring_credits_set(struct rocker *rocker,
+ struct rocker_dma_ring_info *info,
+ u32 credits)
+{
+ if (credits)
+ rocker_write32(rocker, DMA_DESC_CREDITS(info->type), credits);
+}
+
+static unsigned long rocker_dma_ring_size_fix(size_t size)
+{
+ return max(ROCKER_DMA_SIZE_MIN,
+ min(roundup_pow_of_two(size), ROCKER_DMA_SIZE_MAX));
+}
+
+static int rocker_dma_ring_create(struct rocker *rocker,
+ unsigned int type,
+ size_t size,
+ struct rocker_dma_ring_info *info)
+{
+ int i;
+
+ BUG_ON(size != rocker_dma_ring_size_fix(size));
+ info->size = size;
+ info->type = type;
+ info->head = 0;
+ info->tail = 0;
+ info->desc_info = kcalloc(info->size, sizeof(*info->desc_info),
+ GFP_KERNEL);
+ if (!info->desc_info)
+ return -ENOMEM;
+
+ info->desc = pci_alloc_consistent(rocker->pdev,
+ info->size * sizeof(*info->desc),
+ &info->mapaddr);
+ if (!info->desc) {
+ kfree(info->desc_info);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < info->size; i++)
+ info->desc_info[i].desc = &info->desc[i];
+
+ rocker_write32(rocker, DMA_DESC_CTRL(info->type),
+ ROCKER_DMA_DESC_CTRL_RESET);
+ rocker_write64(rocker, DMA_DESC_ADDR(info->type), info->mapaddr);
+ rocker_write32(rocker, DMA_DESC_SIZE(info->type), info->size);
+
+ return 0;
+}
+
+static void rocker_dma_ring_destroy(struct rocker *rocker,
+ struct rocker_dma_ring_info *info)
+{
+ rocker_write64(rocker, DMA_DESC_ADDR(info->type), 0);
+
+ pci_free_consistent(rocker->pdev,
+ info->size * sizeof(struct rocker_desc),
+ info->desc, info->mapaddr);
+ kfree(info->desc_info);
+}
+
+static void rocker_dma_ring_pass_to_producer(struct rocker *rocker,
+ struct rocker_dma_ring_info *info)
+{
+ int i;
+
+ BUG_ON(info->head || info->tail);
+
+ /* When ring is consumer, we need to advance head for each desc.
+ * That tells hw that the desc is ready to be used by it.
+ */
+ for (i = 0; i < info->size - 1; i++)
+ rocker_desc_head_set(rocker, info, &info->desc_info[i]);
+ rocker_desc_commit(&info->desc_info[i]);
+}
+
+static int rocker_dma_ring_bufs_alloc(struct rocker *rocker,
+ struct rocker_dma_ring_info *info,
+ int direction, size_t buf_size)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ int i;
+ int err;
+
+ for (i = 0; i < info->size; i++) {
+ struct rocker_desc_info *desc_info = &info->desc_info[i];
+ struct rocker_desc *desc = &info->desc[i];
+ dma_addr_t dma_handle;
+ char *buf;
+
+ buf = kzalloc(buf_size, GFP_KERNEL | GFP_DMA);
+ if (!buf) {
+ err = -ENOMEM;
+ goto rollback;
+ }
+
+ dma_handle = pci_map_single(pdev, buf, buf_size, direction);
+ if (pci_dma_mapping_error(pdev, dma_handle)) {
+ kfree(buf);
+ err = -EIO;
+ goto rollback;
+ }
+
+ desc_info->data = buf;
+ desc_info->data_size = buf_size;
+ dma_unmap_addr_set(desc_info, mapaddr, dma_handle);
+
+ desc->buf_addr = dma_handle;
+ desc->buf_size = buf_size;
+ }
+ return 0;
+
+rollback:
+ for (i--; i >= 0; i--) {
+ struct rocker_desc_info *desc_info = &info->desc_info[i];
+
+ pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr),
+ desc_info->data_size, direction);
+ kfree(desc_info->data);
+ }
+ return err;
+}
+
+static void rocker_dma_ring_bufs_free(struct rocker *rocker,
+ struct rocker_dma_ring_info *info,
+ int direction)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ int i;
+
+ for (i = 0; i < info->size; i++) {
+ struct rocker_desc_info *desc_info = &info->desc_info[i];
+ struct rocker_desc *desc = &info->desc[i];
+
+ desc->buf_addr = 0;
+ desc->buf_size = 0;
+ pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr),
+ desc_info->data_size, direction);
+ kfree(desc_info->data);
+ }
+}
+
+static int rocker_dma_rings_init(struct rocker *rocker)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ int err;
+
+ err = rocker_dma_ring_create(rocker, ROCKER_DMA_CMD,
+ ROCKER_DMA_CMD_DEFAULT_SIZE,
+ &rocker->cmd_ring);
+ if (err) {
+ dev_err(&pdev->dev, "failed to create command dma ring\n");
+ return err;
+ }
+
+ spin_lock_init(&rocker->cmd_ring_lock);
+
+ err = rocker_dma_ring_bufs_alloc(rocker, &rocker->cmd_ring,
+ PCI_DMA_BIDIRECTIONAL, PAGE_SIZE);
+ if (err) {
+ dev_err(&pdev->dev, "failed to alloc command dma ring buffers\n");
+ goto err_dma_cmd_ring_bufs_alloc;
+ }
+
+ err = rocker_dma_ring_create(rocker, ROCKER_DMA_EVENT,
+ ROCKER_DMA_EVENT_DEFAULT_SIZE,
+ &rocker->event_ring);
+ if (err) {
+ dev_err(&pdev->dev, "failed to create event dma ring\n");
+ goto err_dma_event_ring_create;
+ }
+
+ err = rocker_dma_ring_bufs_alloc(rocker, &rocker->event_ring,
+ PCI_DMA_FROMDEVICE, PAGE_SIZE);
+ if (err) {
+ dev_err(&pdev->dev, "failed to alloc event dma ring buffers\n");
+ goto err_dma_event_ring_bufs_alloc;
+ }
+ rocker_dma_ring_pass_to_producer(rocker, &rocker->event_ring);
+ return 0;
+
+err_dma_event_ring_bufs_alloc:
+ rocker_dma_ring_destroy(rocker, &rocker->event_ring);
+err_dma_event_ring_create:
+ rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring,
+ PCI_DMA_BIDIRECTIONAL);
+err_dma_cmd_ring_bufs_alloc:
+ rocker_dma_ring_destroy(rocker, &rocker->cmd_ring);
+ return err;
+}
+
+static void rocker_dma_rings_fini(struct rocker *rocker)
+{
+ rocker_dma_ring_bufs_free(rocker, &rocker->event_ring,
+ PCI_DMA_BIDIRECTIONAL);
+ rocker_dma_ring_destroy(rocker, &rocker->event_ring);
+ rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring,
+ PCI_DMA_BIDIRECTIONAL);
+ rocker_dma_ring_destroy(rocker, &rocker->cmd_ring);
+}
+
+static int rocker_dma_rx_ring_skb_map(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ struct sk_buff *skb, size_t buf_len)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ dma_addr_t dma_handle;
+
+ dma_handle = pci_map_single(pdev, skb->data, buf_len,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(pdev, dma_handle))
+ return -EIO;
+ if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_RX_FRAG_ADDR, dma_handle))
+ goto tlv_put_failure;
+ if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_RX_FRAG_MAX_LEN, buf_len))
+ goto tlv_put_failure;
+ return 0;
+
+tlv_put_failure:
+ pci_unmap_single(pdev, dma_handle, buf_len, PCI_DMA_FROMDEVICE);
+ desc_info->tlv_size = 0;
+ return -EMSGSIZE;
+}
+
+static size_t rocker_port_rx_buf_len(struct rocker_port *rocker_port)
+{
+ return rocker_port->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+}
+
+static int rocker_dma_rx_ring_skb_alloc(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info)
+{
+ struct net_device *dev = rocker_port->dev;
+ struct sk_buff *skb;
+ size_t buf_len = rocker_port_rx_buf_len(rocker_port);
+ int err;
+
+ /* Ensure that hw will see tlv_size zero in case of an error.
+ * That tells hw to use another descriptor.
+ */
+ rocker_desc_cookie_ptr_set(desc_info, NULL);
+ desc_info->tlv_size = 0;
+
+ skb = netdev_alloc_skb_ip_align(dev, buf_len);
+ if (!skb)
+ return -ENOMEM;
+ err = rocker_dma_rx_ring_skb_map(rocker, rocker_port, desc_info,
+ skb, buf_len);
+ if (err) {
+ dev_kfree_skb_any(skb);
+ return err;
+ }
+ rocker_desc_cookie_ptr_set(desc_info, skb);
+ return 0;
+}
+
+static void rocker_dma_rx_ring_skb_unmap(struct rocker *rocker,
+ struct rocker_tlv **attrs)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ dma_addr_t dma_handle;
+ size_t len;
+
+ if (!attrs[ROCKER_TLV_RX_FRAG_ADDR] ||
+ !attrs[ROCKER_TLV_RX_FRAG_MAX_LEN])
+ return;
+ dma_handle = rocker_tlv_get_u64(attrs[ROCKER_TLV_RX_FRAG_ADDR]);
+ len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_MAX_LEN]);
+ pci_unmap_single(pdev, dma_handle, len, PCI_DMA_FROMDEVICE);
+}
+
+static void rocker_dma_rx_ring_skb_free(struct rocker *rocker,
+ struct rocker_desc_info *desc_info)
+{
+ struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1];
+ struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info);
+
+ if (!skb)
+ return;
+ rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info);
+ rocker_dma_rx_ring_skb_unmap(rocker, attrs);
+ dev_kfree_skb_any(skb);
+}
+
+static int rocker_dma_rx_ring_skbs_alloc(struct rocker *rocker,
+ struct rocker_port *rocker_port)
+{
+ struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring;
+ int i;
+ int err;
+
+ for (i = 0; i < rx_ring->size; i++) {
+ err = rocker_dma_rx_ring_skb_alloc(rocker, rocker_port,
+ &rx_ring->desc_info[i]);
+ if (err)
+ goto rollback;
+ }
+ return 0;
+
+rollback:
+ for (i--; i >= 0; i--)
+ rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]);
+ return err;
+}
+
+static void rocker_dma_rx_ring_skbs_free(struct rocker *rocker,
+ struct rocker_port *rocker_port)
+{
+ struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring;
+ int i;
+
+ for (i = 0; i < rx_ring->size; i++)
+ rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]);
+}
+
+static int rocker_port_dma_rings_init(struct rocker_port *rocker_port)
+{
+ struct rocker *rocker = rocker_port->rocker;
+ int err;
+
+ err = rocker_dma_ring_create(rocker,
+ ROCKER_DMA_TX(rocker_port->port_number),
+ ROCKER_DMA_TX_DEFAULT_SIZE,
+ &rocker_port->tx_ring);
+ if (err) {
+ netdev_err(rocker_port->dev, "failed to create tx dma ring\n");
+ return err;
+ }
+
+ err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->tx_ring,
+ PCI_DMA_TODEVICE,
+ ROCKER_DMA_TX_DESC_SIZE);
+ if (err) {
+ netdev_err(rocker_port->dev, "failed to alloc tx dma ring buffers\n");
+ goto err_dma_tx_ring_bufs_alloc;
+ }
+
+ err = rocker_dma_ring_create(rocker,
+ ROCKER_DMA_RX(rocker_port->port_number),
+ ROCKER_DMA_RX_DEFAULT_SIZE,
+ &rocker_port->rx_ring);
+ if (err) {
+ netdev_err(rocker_port->dev, "failed to create rx dma ring\n");
+ goto err_dma_rx_ring_create;
+ }
+
+ err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->rx_ring,
+ PCI_DMA_BIDIRECTIONAL,
+ ROCKER_DMA_RX_DESC_SIZE);
+ if (err) {
+ netdev_err(rocker_port->dev, "failed to alloc rx dma ring buffers\n");
+ goto err_dma_rx_ring_bufs_alloc;
+ }
+
+ err = rocker_dma_rx_ring_skbs_alloc(rocker, rocker_port);
+ if (err) {
+ netdev_err(rocker_port->dev, "failed to alloc rx dma ring skbs\n");
+ goto err_dma_rx_ring_skbs_alloc;
+ }
+ rocker_dma_ring_pass_to_producer(rocker, &rocker_port->rx_ring);
+
+ return 0;
+
+err_dma_rx_ring_skbs_alloc:
+ rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring,
+ PCI_DMA_BIDIRECTIONAL);
+err_dma_rx_ring_bufs_alloc:
+ rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring);
+err_dma_rx_ring_create:
+ rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring,
+ PCI_DMA_TODEVICE);
+err_dma_tx_ring_bufs_alloc:
+ rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring);
+ return err;
+}
+
+static void rocker_port_dma_rings_fini(struct rocker_port *rocker_port)
+{
+ struct rocker *rocker = rocker_port->rocker;
+
+ rocker_dma_rx_ring_skbs_free(rocker, rocker_port);
+ rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring,
+ PCI_DMA_BIDIRECTIONAL);
+ rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring);
+ rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring,
+ PCI_DMA_TODEVICE);
+ rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring);
+}
+
+static void rocker_port_set_enable(struct rocker_port *rocker_port, bool enable)
+{
+ u64 val = rocker_read64(rocker_port->rocker, PORT_PHYS_ENABLE);
+
+ if (enable)
+ val |= 1 << rocker_port->lport;
+ else
+ val &= ~(1 << rocker_port->lport);
+ rocker_write64(rocker_port->rocker, PORT_PHYS_ENABLE, val);
+}
+
+/********************************
+ * Interrupt handler and helpers
+ ********************************/
+
+static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id)
+{
+ struct rocker *rocker = dev_id;
+ struct rocker_desc_info *desc_info;
+ struct rocker_wait *wait;
+ u32 credits = 0;
+
+ spin_lock(&rocker->cmd_ring_lock);
+ while ((desc_info = rocker_desc_tail_get(&rocker->cmd_ring))) {
+ wait = rocker_desc_cookie_ptr_get(desc_info);
+ if (wait->nowait) {
+ rocker_desc_gen_clear(desc_info);
+ rocker_wait_destroy(wait);
+ } else {
+ rocker_wait_wake_up(wait);
+ }
+ credits++;
+ }
+ spin_unlock(&rocker->cmd_ring_lock);
+ rocker_dma_ring_credits_set(rocker, &rocker->cmd_ring, credits);
+
+ return IRQ_HANDLED;
+}
+
+static void rocker_port_link_up(struct rocker_port *rocker_port)
+{
+ netif_carrier_on(rocker_port->dev);
+ netdev_info(rocker_port->dev, "Link is up\n");
+}
+
+static void rocker_port_link_down(struct rocker_port *rocker_port)
+{
+ netif_carrier_off(rocker_port->dev);
+ netdev_info(rocker_port->dev, "Link is down\n");
+}
+
+static int rocker_event_link_change(struct rocker *rocker,
+ const struct rocker_tlv *info)
+{
+ struct rocker_tlv *attrs[ROCKER_TLV_EVENT_LINK_CHANGED_MAX + 1];
+ unsigned int port_number;
+ bool link_up;
+ struct rocker_port *rocker_port;
+
+ rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_LINK_CHANGED_MAX, info);
+ if (!attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LPORT] ||
+ !attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP])
+ return -EIO;
+ port_number =
+ rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LPORT]) - 1;
+ link_up = rocker_tlv_get_u8(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP]);
+
+ if (port_number >= rocker->port_count)
+ return -EINVAL;
+
+ rocker_port = rocker->ports[port_number];
+ if (netif_carrier_ok(rocker_port->dev) != link_up) {
+ if (link_up)
+ rocker_port_link_up(rocker_port);
+ else
+ rocker_port_link_down(rocker_port);
+ }
+
+ return 0;
+}
+
+static int rocker_event_process(struct rocker *rocker,
+ struct rocker_desc_info *desc_info)
+{
+ struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAX + 1];
+ struct rocker_tlv *info;
+ u16 type;
+
+ rocker_tlv_parse_desc(attrs, ROCKER_TLV_EVENT_MAX, desc_info);
+ if (!attrs[ROCKER_TLV_EVENT_TYPE] ||
+ !attrs[ROCKER_TLV_EVENT_INFO])
+ return -EIO;
+
+ type = rocker_tlv_get_u16(attrs[ROCKER_TLV_EVENT_TYPE]);
+ info = attrs[ROCKER_TLV_EVENT_INFO];
+
+ switch (type) {
+ case ROCKER_TLV_EVENT_TYPE_LINK_CHANGED:
+ return rocker_event_link_change(rocker, info);
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static irqreturn_t rocker_event_irq_handler(int irq, void *dev_id)
+{
+ struct rocker *rocker = dev_id;
+ struct pci_dev *pdev = rocker->pdev;
+ struct rocker_desc_info *desc_info;
+ u32 credits = 0;
+ int err;
+
+ while ((desc_info = rocker_desc_tail_get(&rocker->event_ring))) {
+ err = rocker_desc_err(desc_info);
+ if (err) {
+ dev_err(&pdev->dev, "event desc received with err %d\n",
+ err);
+ } else {
+ err = rocker_event_process(rocker, desc_info);
+ if (err)
+ dev_err(&pdev->dev, "event processing failed with err %d\n",
+ err);
+ }
+ rocker_desc_gen_clear(desc_info);
+ rocker_desc_head_set(rocker, &rocker->event_ring, desc_info);
+ credits++;
+ }
+ rocker_dma_ring_credits_set(rocker, &rocker->event_ring, credits);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rocker_tx_irq_handler(int irq, void *dev_id)
+{
+ struct rocker_port *rocker_port = dev_id;
+
+ napi_schedule(&rocker_port->napi_tx);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rocker_rx_irq_handler(int irq, void *dev_id)
+{
+ struct rocker_port *rocker_port = dev_id;
+
+ napi_schedule(&rocker_port->napi_rx);
+ return IRQ_HANDLED;
+}
+
+/********************
+ * Command interface
+ ********************/
+
+typedef int (*rocker_cmd_cb_t)(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv);
+
+static int rocker_cmd_exec(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ rocker_cmd_cb_t prepare, void *prepare_priv,
+ rocker_cmd_cb_t process, void *process_priv,
+ bool nowait)
+{
+ struct rocker_desc_info *desc_info;
+ struct rocker_wait *wait;
+ unsigned long flags;
+ int err;
+
+ wait = rocker_wait_create(nowait ? GFP_ATOMIC : GFP_KERNEL);
+ if (!wait)
+ return -ENOMEM;
+ wait->nowait = nowait;
+
+ spin_lock_irqsave(&rocker->cmd_ring_lock, flags);
+ desc_info = rocker_desc_head_get(&rocker->cmd_ring);
+ if (!desc_info) {
+ spin_unlock_irqrestore(&rocker->cmd_ring_lock, flags);
+ err = -EAGAIN;
+ goto out;
+ }
+ err = prepare(rocker, rocker_port, desc_info, prepare_priv);
+ if (err) {
+ spin_unlock_irqrestore(&rocker->cmd_ring_lock, flags);
+ goto out;
+ }
+ rocker_desc_cookie_ptr_set(desc_info, wait);
+ rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info);
+ spin_unlock_irqrestore(&rocker->cmd_ring_lock, flags);
+
+ if (nowait)
+ return 0;
+
+ if (!rocker_wait_event_timeout(wait, HZ / 10))
+ return -EIO;
+
+ err = rocker_desc_err(desc_info);
+ if (err)
+ return err;
+
+ if (process)
+ err = process(rocker, rocker_port, desc_info, process_priv);
+
+ rocker_desc_gen_clear(desc_info);
+out:
+ rocker_wait_destroy(wait);
+ return err;
+}
+
+static int
+rocker_cmd_get_port_settings_prep(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv)
+{
+ struct rocker_tlv *cmd_info;
+
+ if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
+ ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS))
+ return -EMSGSIZE;
+ cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
+ if (!cmd_info)
+ return -EMSGSIZE;
+ if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LPORT,
+ rocker_port->lport))
+ return -EMSGSIZE;
+ rocker_tlv_nest_end(desc_info, cmd_info);
+ return 0;
+}
+
+static int
+rocker_cmd_get_port_settings_ethtool_proc(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv)
+{
+ struct ethtool_cmd *ecmd = priv;
+ struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
+ struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
+ u32 speed;
+ u8 duplex;
+ u8 autoneg;
+
+ rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
+ if (!attrs[ROCKER_TLV_CMD_INFO])
+ return -EIO;
+
+ rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
+ attrs[ROCKER_TLV_CMD_INFO]);
+ if (!info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] ||
+ !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] ||
+ !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG])
+ return -EIO;
+
+ speed = rocker_tlv_get_u32(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]);
+ duplex = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]);
+ autoneg = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]);
+
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->supported = SUPPORTED_TP;
+ ecmd->phy_address = 0xff;
+ ecmd->port = PORT_TP;
+ ethtool_cmd_speed_set(ecmd, speed);
+ ecmd->duplex = duplex ? DUPLEX_FULL : DUPLEX_HALF;
+ ecmd->autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+
+ return 0;
+}
+
+static int
+rocker_cmd_get_port_settings_macaddr_proc(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv)
+{
+ unsigned char *macaddr = priv;
+ struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
+ struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
+ struct rocker_tlv *attr;
+
+ rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
+ if (!attrs[ROCKER_TLV_CMD_INFO])
+ return -EIO;
+
+ rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
+ attrs[ROCKER_TLV_CMD_INFO]);
+ attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR];
+ if (!attr)
+ return -EIO;
+
+ if (rocker_tlv_len(attr) != ETH_ALEN)
+ return -EINVAL;
+
+ ether_addr_copy(macaddr, rocker_tlv_data(attr));
+ return 0;
+}
+
+static int
+rocker_cmd_set_port_settings_ethtool_prep(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv)
+{
+ struct ethtool_cmd *ecmd = priv;
+ struct rocker_tlv *cmd_info;
+
+ if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
+ ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
+ return -EMSGSIZE;
+ cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
+ if (!cmd_info)
+ return -EMSGSIZE;
+ if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LPORT,
+ rocker_port->lport))
+ return -EMSGSIZE;
+ if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED,
+ ethtool_cmd_speed(ecmd)))
+ return -EMSGSIZE;
+ if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX,
+ ecmd->duplex))
+ return -EMSGSIZE;
+ if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG,
+ ecmd->autoneg))
+ return -EMSGSIZE;
+ rocker_tlv_nest_end(desc_info, cmd_info);
+ return 0;
+}
+
+static int
+rocker_cmd_set_port_settings_macaddr_prep(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv)
+{
+ unsigned char *macaddr = priv;
+ struct rocker_tlv *cmd_info;
+
+ if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
+ ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
+ return -EMSGSIZE;
+ cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
+ if (!cmd_info)
+ return -EMSGSIZE;
+ if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LPORT,
+ rocker_port->lport))
+ return -EMSGSIZE;
+ if (rocker_tlv_put(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR,
+ ETH_ALEN, macaddr))
+ return -EMSGSIZE;
+ rocker_tlv_nest_end(desc_info, cmd_info);
+ return 0;
+}
+
+static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port,
+ struct ethtool_cmd *ecmd)
+{
+ return rocker_cmd_exec(rocker_port->rocker, rocker_port,
+ rocker_cmd_get_port_settings_prep, NULL,
+ rocker_cmd_get_port_settings_ethtool_proc,
+ ecmd, false);
+}
+
+static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port,
+ unsigned char *macaddr)
+{
+ return rocker_cmd_exec(rocker_port->rocker, rocker_port,
+ rocker_cmd_get_port_settings_prep, NULL,
+ rocker_cmd_get_port_settings_macaddr_proc,
+ macaddr, false);
+}
+
+static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port,
+ struct ethtool_cmd *ecmd)
+{
+ return rocker_cmd_exec(rocker_port->rocker, rocker_port,
+ rocker_cmd_set_port_settings_ethtool_prep,
+ ecmd, NULL, NULL, false);
+}
+
+static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port,
+ unsigned char *macaddr)
+{
+ return rocker_cmd_exec(rocker_port->rocker, rocker_port,
+ rocker_cmd_set_port_settings_macaddr_prep,
+ macaddr, NULL, NULL, false);
+}
+
+/*****************
+ * Net device ops
+ *****************/
+
+static int rocker_port_open(struct net_device *dev)
+{
+ struct rocker_port *rocker_port = netdev_priv(dev);
+ int err;
+
+ err = rocker_port_dma_rings_init(rocker_port);
+ if (err)
+ return err;
+
+ err = request_irq(rocker_msix_tx_vector(rocker_port),
+ rocker_tx_irq_handler, 0,
+ rocker_driver_name, rocker_port);
+ if (err) {
+ netdev_err(rocker_port->dev, "cannot assign tx irq\n");
+ goto err_request_tx_irq;
+ }
+
+ err = request_irq(rocker_msix_rx_vector(rocker_port),
+ rocker_rx_irq_handler, 0,
+ rocker_driver_name, rocker_port);
+ if (err) {
+ netdev_err(rocker_port->dev, "cannot assign rx irq\n");
+ goto err_request_rx_irq;
+ }
+
+ napi_enable(&rocker_port->napi_tx);
+ napi_enable(&rocker_port->napi_rx);
+ rocker_port_set_enable(rocker_port, true);
+ netif_start_queue(dev);
+ return 0;
+
+err_request_rx_irq:
+ free_irq(rocker_msix_tx_vector(rocker_port), rocker_port);
+err_request_tx_irq:
+ rocker_port_dma_rings_fini(rocker_port);
+ return err;
+}
+
+static int rocker_port_stop(struct net_device *dev)
+{
+ struct rocker_port *rocker_port = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ rocker_port_set_enable(rocker_port, false);
+ napi_disable(&rocker_port->napi_rx);
+ napi_disable(&rocker_port->napi_tx);
+ free_irq(rocker_msix_rx_vector(rocker_port), rocker_port);
+ free_irq(rocker_msix_tx_vector(rocker_port), rocker_port);
+ rocker_port_dma_rings_fini(rocker_port);
+
+ return 0;
+}
+
+static void rocker_tx_desc_frags_unmap(struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info)
+{
+ struct rocker *rocker = rocker_port->rocker;
+ struct pci_dev *pdev = rocker->pdev;
+ struct rocker_tlv *attrs[ROCKER_TLV_TX_MAX + 1];
+ struct rocker_tlv *attr;
+ int rem;
+
+ rocker_tlv_parse_desc(attrs, ROCKER_TLV_TX_MAX, desc_info);
+ if (!attrs[ROCKER_TLV_TX_FRAGS])
+ return;
+ rocker_tlv_for_each_nested(attr, attrs[ROCKER_TLV_TX_FRAGS], rem) {
+ struct rocker_tlv *frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_MAX + 1];
+ dma_addr_t dma_handle;
+ size_t len;
+
+ if (rocker_tlv_type(attr) != ROCKER_TLV_TX_FRAG)
+ continue;
+ rocker_tlv_parse_nested(frag_attrs, ROCKER_TLV_TX_FRAG_ATTR_MAX,
+ attr);
+ if (!frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] ||
+ !frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN])
+ continue;
+ dma_handle = rocker_tlv_get_u64(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]);
+ len = rocker_tlv_get_u16(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN]);
+ pci_unmap_single(pdev, dma_handle, len, DMA_TO_DEVICE);
+ }
+}
+
+static int rocker_tx_desc_frag_map_put(struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ char *buf, size_t buf_len)
+{
+ struct rocker *rocker = rocker_port->rocker;
+ struct pci_dev *pdev = rocker->pdev;
+ dma_addr_t dma_handle;
+ struct rocker_tlv *frag;
+
+ dma_handle = pci_map_single(pdev, buf, buf_len, DMA_TO_DEVICE);
+ if (unlikely(pci_dma_mapping_error(pdev, dma_handle))) {
+ if (net_ratelimit())
+ netdev_err(rocker_port->dev, "failed to dma map tx frag\n");
+ return -EIO;
+ }
+ frag = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAG);
+ if (!frag)
+ goto unmap_frag;
+ if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_TX_FRAG_ATTR_ADDR,
+ dma_handle))
+ goto nest_cancel;
+ if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_TX_FRAG_ATTR_LEN,
+ buf_len))
+ goto nest_cancel;
+ rocker_tlv_nest_end(desc_info, frag);
+ return 0;
+
+nest_cancel:
+ rocker_tlv_nest_cancel(desc_info, frag);
+unmap_frag:
+ pci_unmap_single(pdev, dma_handle, buf_len, DMA_TO_DEVICE);
+ return -EMSGSIZE;
+}
+
+static netdev_tx_t rocker_port_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct rocker_port *rocker_port = netdev_priv(dev);
+ struct rocker *rocker = rocker_port->rocker;
+ struct rocker_desc_info *desc_info;
+ struct rocker_tlv *frags;
+ int i;
+ int err;
+
+ desc_info = rocker_desc_head_get(&rocker_port->tx_ring);
+ if (unlikely(!desc_info)) {
+ if (net_ratelimit())
+ netdev_err(dev, "tx ring full when queue awake\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ rocker_desc_cookie_ptr_set(desc_info, skb);
+
+ frags = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAGS);
+ if (!frags)
+ goto out;
+ err = rocker_tx_desc_frag_map_put(rocker_port, desc_info,
+ skb->data, skb_headlen(skb));
+ if (err)
+ goto nest_cancel;
+ if (skb_shinfo(skb)->nr_frags > ROCKER_TX_FRAGS_MAX)
+ goto nest_cancel;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ err = rocker_tx_desc_frag_map_put(rocker_port, desc_info,
+ skb_frag_address(frag),
+ skb_frag_size(frag));
+ if (err)
+ goto unmap_frags;
+ }
+ rocker_tlv_nest_end(desc_info, frags);
+
+ rocker_desc_gen_clear(desc_info);
+ rocker_desc_head_set(rocker, &rocker_port->tx_ring, desc_info);
+
+ desc_info = rocker_desc_head_get(&rocker_port->tx_ring);
+ if (!desc_info)
+ netif_stop_queue(dev);
+
+ return NETDEV_TX_OK;
+
+unmap_frags:
+ rocker_tx_desc_frags_unmap(rocker_port, desc_info);
+nest_cancel:
+ rocker_tlv_nest_cancel(desc_info, frags);
+out:
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static int rocker_port_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ struct rocker_port *rocker_port = netdev_priv(dev);
+ int err;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ err = rocker_cmd_set_port_settings_macaddr(rocker_port, addr->sa_data);
+ if (err)
+ return err;
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ return 0;
+}
+
+static int rocker_port_switch_parent_id_get(struct net_device *dev,
+ struct netdev_phys_item_id *psid)
+{
+ struct rocker_port *rocker_port = netdev_priv(dev);
+ struct rocker *rocker = rocker_port->rocker;
+
+ psid->id_len = sizeof(rocker->hw.id);
+ memcpy(&psid->id, &rocker->hw.id, psid->id_len);
+ return 0;
+}
+
+static const struct net_device_ops rocker_port_netdev_ops = {
+ .ndo_open = rocker_port_open,
+ .ndo_stop = rocker_port_stop,
+ .ndo_start_xmit = rocker_port_xmit,
+ .ndo_set_mac_address = rocker_port_set_mac_address,
+ .ndo_switch_parent_id_get = rocker_port_switch_parent_id_get,
+};
+
+/********************
+ * ethtool interface
+ ********************/
+
+static int rocker_port_get_settings(struct net_device *dev,
+ struct ethtool_cmd *ecmd)
+{
+ struct rocker_port *rocker_port = netdev_priv(dev);
+
+ return rocker_cmd_get_port_settings_ethtool(rocker_port, ecmd);
+}
+
+static int rocker_port_set_settings(struct net_device *dev,
+ struct ethtool_cmd *ecmd)
+{
+ struct rocker_port *rocker_port = netdev_priv(dev);
+
+ return rocker_cmd_set_port_settings_ethtool(rocker_port, ecmd);
+}
+
+static void rocker_port_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ strlcpy(drvinfo->driver, rocker_driver_name, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
+}
+
+static const struct ethtool_ops rocker_port_ethtool_ops = {
+ .get_settings = rocker_port_get_settings,
+ .set_settings = rocker_port_set_settings,
+ .get_drvinfo = rocker_port_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+/*****************
+ * NAPI interface
+ *****************/
+
+static struct rocker_port *rocker_port_napi_tx_get(struct napi_struct *napi)
+{
+ return container_of(napi, struct rocker_port, napi_tx);
+}
+
+static int rocker_port_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct rocker_port *rocker_port = rocker_port_napi_tx_get(napi);
+ struct rocker *rocker = rocker_port->rocker;
+ struct rocker_desc_info *desc_info;
+ u32 credits = 0;
+ int err;
+
+ /* Cleanup tx descriptors */
+ while ((desc_info = rocker_desc_tail_get(&rocker_port->tx_ring))) {
+ err = rocker_desc_err(desc_info);
+ if (err && net_ratelimit())
+ netdev_err(rocker_port->dev, "tx desc received with err %d\n",
+ err);
+ rocker_tx_desc_frags_unmap(rocker_port, desc_info);
+ dev_kfree_skb_any(rocker_desc_cookie_ptr_get(desc_info));
+ credits++;
+ }
+
+ if (credits && netif_queue_stopped(rocker_port->dev))
+ netif_wake_queue(rocker_port->dev);
+
+ napi_complete(napi);
+ rocker_dma_ring_credits_set(rocker, &rocker_port->tx_ring, credits);
+
+ return 0;
+}
+
+static int rocker_port_rx_proc(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info)
+{
+ struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1];
+ struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info);
+ size_t rx_len;
+
+ if (!skb)
+ return -ENOENT;
+
+ rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info);
+ if (!attrs[ROCKER_TLV_RX_FRAG_LEN])
+ return -EINVAL;
+
+ rocker_dma_rx_ring_skb_unmap(rocker, attrs);
+
+ rx_len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_LEN]);
+ skb_put(skb, rx_len);
+ skb->protocol = eth_type_trans(skb, rocker_port->dev);
+ netif_receive_skb(skb);
+
+ return rocker_dma_rx_ring_skb_alloc(rocker, rocker_port, desc_info);
+}
+
+static struct rocker_port *rocker_port_napi_rx_get(struct napi_struct *napi)
+{
+ return container_of(napi, struct rocker_port, napi_rx);
+}
+
+static int rocker_port_poll_rx(struct napi_struct *napi, int budget)
+{
+ struct rocker_port *rocker_port = rocker_port_napi_rx_get(napi);
+ struct rocker *rocker = rocker_port->rocker;
+ struct rocker_desc_info *desc_info;
+ u32 credits = 0;
+ int err;
+
+ /* Process rx descriptors */
+ while (credits < budget &&
+ (desc_info = rocker_desc_tail_get(&rocker_port->rx_ring))) {
+ err = rocker_desc_err(desc_info);
+ if (err) {
+ if (net_ratelimit())
+ netdev_err(rocker_port->dev, "rx desc received with err %d\n",
+ err);
+ } else {
+ err = rocker_port_rx_proc(rocker, rocker_port,
+ desc_info);
+ if (err && net_ratelimit())
+ netdev_err(rocker_port->dev, "rx processing failed with err %d\n",
+ err);
+ }
+ rocker_desc_gen_clear(desc_info);
+ rocker_desc_head_set(rocker, &rocker_port->rx_ring, desc_info);
+ credits++;
+ }
+
+ if (credits < budget)
+ napi_complete(napi);
+
+ rocker_dma_ring_credits_set(rocker, &rocker_port->rx_ring, credits);
+
+ return credits;
+}
+
+/*****************
+ * PCI driver ops
+ *****************/
+
+static void rocker_carrier_init(struct rocker_port *rocker_port)
+{
+ struct rocker *rocker = rocker_port->rocker;
+ u64 link_status = rocker_read64(rocker, PORT_PHYS_LINK_STATUS);
+ bool link_up;
+
+ link_up = link_status & (1 << rocker_port->lport);
+ if (link_up)
+ netif_carrier_on(rocker_port->dev);
+ else
+ netif_carrier_off(rocker_port->dev);
+}
+
+static void rocker_remove_ports(struct rocker *rocker)
+{
+ int i;
+
+ for (i = 0; i < rocker->port_count; i++)
+ unregister_netdev(rocker->ports[i]->dev);
+ kfree(rocker->ports);
+}
+
+static void rocker_port_dev_addr_init(struct rocker *rocker,
+ struct rocker_port *rocker_port)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ int err;
+
+ err = rocker_cmd_get_port_settings_macaddr(rocker_port,
+ rocker_port->dev->dev_addr);
+ if (err) {
+ dev_warn(&pdev->dev, "failed to get mac address, using random\n");
+ eth_hw_addr_random(rocker_port->dev);
+ }
+}
+
+static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ struct rocker_port *rocker_port;
+ struct net_device *dev;
+ int err;
+
+ dev = alloc_etherdev(sizeof(struct rocker_port));
+ if (!dev)
+ return -ENOMEM;
+ rocker_port = netdev_priv(dev);
+ rocker_port->dev = dev;
+ rocker_port->rocker = rocker;
+ rocker_port->port_number = port_number;
+ rocker_port->lport = port_number + 1;
+
+ rocker_port_dev_addr_init(rocker, rocker_port);
+ dev->netdev_ops = &rocker_port_netdev_ops;
+ dev->ethtool_ops = &rocker_port_ethtool_ops;
+ netif_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx,
+ NAPI_POLL_WEIGHT);
+ netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx,
+ NAPI_POLL_WEIGHT);
+ rocker_carrier_init(rocker_port);
+
+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ err = register_netdev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "register_netdev failed\n");
+ goto err_register_netdev;
+ }
+ rocker->ports[port_number] = rocker_port;
+
+ return 0;
+
+err_register_netdev:
+ free_netdev(dev);
+ return err;
+}
+
+static int rocker_probe_ports(struct rocker *rocker)
+{
+ int i;
+ size_t alloc_size;
+ int err;
+
+ alloc_size = sizeof(struct rocker_port *) * rocker->port_count;
+ rocker->ports = kmalloc(alloc_size, GFP_KERNEL);
+ for (i = 0; i < rocker->port_count; i++) {
+ err = rocker_probe_port(rocker, i);
+ if (err)
+ goto remove_ports;
+ }
+ return 0;
+
+remove_ports:
+ rocker_remove_ports(rocker);
+ return err;
+}
+
+static int rocker_msix_init(struct rocker *rocker)
+{
+ struct pci_dev *pdev = rocker->pdev;
+ int msix_entries;
+ int i;
+ int err;
+
+ msix_entries = pci_msix_vec_count(pdev);
+ if (msix_entries < 0)
+ return msix_entries;
+
+ if (msix_entries != ROCKER_MSIX_VEC_COUNT(rocker->port_count))
+ return -EINVAL;
+
+ rocker->msix_entries = kmalloc_array(msix_entries,
+ sizeof(struct msix_entry),
+ GFP_KERNEL);
+ if (!rocker->msix_entries)
+ return -ENOMEM;
+
+ for (i = 0; i < msix_entries; i++)
+ rocker->msix_entries[i].entry = i;
+
+ err = pci_enable_msix_exact(pdev, rocker->msix_entries, msix_entries);
+ if (err < 0)
+ goto err_enable_msix;
+
+ return 0;
+
+err_enable_msix:
+ kfree(rocker->msix_entries);
+ return err;
+}
+
+static void rocker_msix_fini(struct rocker *rocker)
+{
+ pci_disable_msix(rocker->pdev);
+ kfree(rocker->msix_entries);
+}
+
+static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct rocker *rocker;
+ int err;
+
+ rocker = kzalloc(sizeof(*rocker), GFP_KERNEL);
+ if (!rocker)
+ return -ENOMEM;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_device failed\n");
+ goto err_pci_enable_device;
+ }
+
+ err = pci_request_regions(pdev, rocker_driver_name);
+ if (err) {
+ dev_err(&pdev->dev, "pci_request_regions failed\n");
+ goto err_pci_request_regions;
+ }
+
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (!err) {
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (err) {
+ dev_err(&pdev->dev, "pci_set_consistent_dma_mask failed\n");
+ goto err_pci_set_dma_mask;
+ }
+ } else {
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev, "pci_set_dma_mask failed\n");
+ goto err_pci_set_dma_mask;
+ }
+ }
+
+ if (pci_resource_len(pdev, 0) < ROCKER_PCI_BAR0_SIZE) {
+ dev_err(&pdev->dev, "invalid PCI region size\n");
+ goto err_pci_resource_len_check;
+ }
+
+ rocker->hw_addr = ioremap(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (!rocker->hw_addr) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ err = -EIO;
+ goto err_ioremap;
+ }
+ pci_set_master(pdev);
+
+ rocker->pdev = pdev;
+ pci_set_drvdata(pdev, rocker);
+
+ rocker->port_count = rocker_read32(rocker, PORT_PHYS_COUNT);
+
+ err = rocker_msix_init(rocker);
+ if (err) {
+ dev_err(&pdev->dev, "MSI-X init failed\n");
+ goto err_msix_init;
+ }
+
+ err = rocker_basic_hw_test(rocker);
+ if (err) {
+ dev_err(&pdev->dev, "basic hw test failed\n");
+ goto err_basic_hw_test;
+ }
+
+ rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
+
+ err = rocker_dma_rings_init(rocker);
+ if (err)
+ goto err_dma_rings_init;
+
+ err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD),
+ rocker_cmd_irq_handler, 0,
+ rocker_driver_name, rocker);
+ if (err) {
+ dev_err(&pdev->dev, "cannot assign cmd irq\n");
+ goto err_request_cmd_irq;
+ }
+
+ err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT),
+ rocker_event_irq_handler, 0,
+ rocker_driver_name, rocker);
+ if (err) {
+ dev_err(&pdev->dev, "cannot assign event irq\n");
+ goto err_request_event_irq;
+ }
+
+ rocker->hw.id = rocker_read64(rocker, SWITCH_ID);
+
+ err = rocker_probe_ports(rocker);
+ if (err) {
+ dev_err(&pdev->dev, "failed to probe ports\n");
+ goto err_probe_ports;
+ }
+
+ dev_info(&pdev->dev, "Rocker switch with id %016llx\n", rocker->hw.id);
+
+ return 0;
+
+err_probe_ports:
+ free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker);
+err_request_event_irq:
+ free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker);
+err_request_cmd_irq:
+ rocker_dma_rings_fini(rocker);
+err_dma_rings_init:
+err_basic_hw_test:
+ rocker_msix_fini(rocker);
+err_msix_init:
+ iounmap(rocker->hw_addr);
+err_ioremap:
+err_pci_resource_len_check:
+err_pci_set_dma_mask:
+ pci_release_regions(pdev);
+err_pci_request_regions:
+ pci_disable_device(pdev);
+err_pci_enable_device:
+ kfree(rocker);
+ return err;
+}
+
+static void rocker_remove(struct pci_dev *pdev)
+{
+ struct rocker *rocker = pci_get_drvdata(pdev);
+
+ rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
+ rocker_remove_ports(rocker);
+ free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker);
+ free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker);
+ rocker_dma_rings_fini(rocker);
+ rocker_msix_fini(rocker);
+ iounmap(rocker->hw_addr);
+ pci_release_regions(rocker->pdev);
+ pci_disable_device(rocker->pdev);
+ kfree(rocker);
+}
+
+static struct pci_driver rocker_pci_driver = {
+ .name = rocker_driver_name,
+ .id_table = rocker_pci_id_table,
+ .probe = rocker_probe,
+ .remove = rocker_remove,
+};
+
+/***********************
+ * Module init and exit
+ ***********************/
+
+static int __init rocker_module_init(void)
+{
+ return pci_register_driver(&rocker_pci_driver);
+}
+
+static void __exit rocker_module_exit(void)
+{
+ pci_unregister_driver(&rocker_pci_driver);
+}
+
+module_init(rocker_module_init);
+module_exit(rocker_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
+MODULE_AUTHOR("Scott Feldman <sfeldma@gmail.com>");
+MODULE_DESCRIPTION("Rocker switch device driver");
+MODULE_DEVICE_TABLE(pci, rocker_pci_id_table);
diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h
new file mode 100644
index 0000000..5251cf8
--- /dev/null
+++ b/drivers/net/ethernet/rocker/rocker.h
@@ -0,0 +1,427 @@
+/*
+ * drivers/net/ethernet/rocker/rocker.h - Rocker switch device driver
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef _ROCKER_H
+#define _ROCKER_H
+
+#include <linux/types.h>
+
+#define PCI_VENDOR_ID_REDHAT 0x1b36
+#define PCI_DEVICE_ID_REDHAT_ROCKER 0x0006
+
+#define ROCKER_PCI_BAR0_SIZE 0x2000
+
+/* MSI-X vectors */
+enum {
+ ROCKER_MSIX_VEC_CMD,
+ ROCKER_MSIX_VEC_EVENT,
+ ROCKER_MSIX_VEC_TEST,
+ ROCKER_MSIX_VEC_RESERVED0,
+ __ROCKER_MSIX_VEC_TX,
+ __ROCKER_MSIX_VEC_RX,
+#define ROCKER_MSIX_VEC_TX(port) \
+ (__ROCKER_MSIX_VEC_TX + ((port) * 2))
+#define ROCKER_MSIX_VEC_RX(port) \
+ (__ROCKER_MSIX_VEC_RX + ((port) * 2))
+#define ROCKER_MSIX_VEC_COUNT(portcnt) \
+ (ROCKER_MSIX_VEC_RX((portcnt - 1)) + 1)
+};
+
+/* Rocker bogus registers */
+#define ROCKER_BOGUS_REG0 0x0000
+#define ROCKER_BOGUS_REG1 0x0004
+#define ROCKER_BOGUS_REG2 0x0008
+#define ROCKER_BOGUS_REG3 0x000c
+
+/* Rocker test registers */
+#define ROCKER_TEST_REG 0x0010
+#define ROCKER_TEST_REG64 0x0018 /* 8-byte */
+#define ROCKER_TEST_IRQ 0x0020
+#define ROCKER_TEST_DMA_ADDR 0x0028 /* 8-byte */
+#define ROCKER_TEST_DMA_SIZE 0x0030
+#define ROCKER_TEST_DMA_CTRL 0x0034
+
+/* Rocker test register ctrl */
+#define ROCKER_TEST_DMA_CTRL_CLEAR (1 << 0)
+#define ROCKER_TEST_DMA_CTRL_FILL (1 << 1)
+#define ROCKER_TEST_DMA_CTRL_INVERT (1 << 2)
+
+/* Rocker DMA ring register offsets */
+#define ROCKER_DMA_DESC_ADDR(x) (0x1000 + (x) * 32) /* 8-byte */
+#define ROCKER_DMA_DESC_SIZE(x) (0x1008 + (x) * 32)
+#define ROCKER_DMA_DESC_HEAD(x) (0x100c + (x) * 32)
+#define ROCKER_DMA_DESC_TAIL(x) (0x1010 + (x) * 32)
+#define ROCKER_DMA_DESC_CTRL(x) (0x1014 + (x) * 32)
+#define ROCKER_DMA_DESC_CREDITS(x) (0x1018 + (x) * 32)
+#define ROCKER_DMA_DESC_RES1(x) (0x101c + (x) * 32)
+
+/* Rocker dma ctrl register bits */
+#define ROCKER_DMA_DESC_CTRL_RESET (1 << 0)
+
+/* Rocker DMA ring types */
+enum rocker_dma_type {
+ ROCKER_DMA_CMD,
+ ROCKER_DMA_EVENT,
+ __ROCKER_DMA_TX,
+ __ROCKER_DMA_RX,
+#define ROCKER_DMA_TX(port) (__ROCKER_DMA_TX + (port) * 2)
+#define ROCKER_DMA_RX(port) (__ROCKER_DMA_RX + (port) * 2)
+};
+
+/* Rocker DMA ring size limits and default sizes */
+#define ROCKER_DMA_SIZE_MIN 2ul
+#define ROCKER_DMA_SIZE_MAX 65536ul
+#define ROCKER_DMA_CMD_DEFAULT_SIZE 32ul
+#define ROCKER_DMA_EVENT_DEFAULT_SIZE 32ul
+#define ROCKER_DMA_TX_DEFAULT_SIZE 64ul
+#define ROCKER_DMA_TX_DESC_SIZE 256
+#define ROCKER_DMA_RX_DEFAULT_SIZE 64ul
+#define ROCKER_DMA_RX_DESC_SIZE 256
+
+/* Rocker DMA descriptor struct */
+struct rocker_desc {
+ u64 buf_addr;
+ u64 cookie;
+ u16 buf_size;
+ u16 tlv_size;
+ u16 resv[5];
+ u16 comp_err;
+};
+
+#define ROCKER_DMA_DESC_COMP_ERR_GEN (1 << 15)
+
+/* Rocker DMA TLV struct */
+struct rocker_tlv {
+ u32 type;
+ u16 len;
+};
+
+/* TLVs */
+enum {
+ ROCKER_TLV_CMD_UNSPEC,
+ ROCKER_TLV_CMD_TYPE, /* u16 */
+ ROCKER_TLV_CMD_INFO, /* nest */
+
+ __ROCKER_TLV_CMD_MAX,
+ ROCKER_TLV_CMD_MAX = __ROCKER_TLV_CMD_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_CMD_TYPE_UNSPEC,
+ ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS,
+ ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS,
+
+ __ROCKER_TLV_CMD_TYPE_MAX,
+ ROCKER_TLV_CMD_TYPE_MAX = __ROCKER_TLV_CMD_TYPE_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_CMD_PORT_SETTINGS_UNSPEC,
+ ROCKER_TLV_CMD_PORT_SETTINGS_LPORT, /* u32 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, /* u32 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, /* u8 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, /* u8 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, /* binary */
+ ROCKER_TLV_CMD_PORT_SETTINGS_MODE, /* u8 */
+
+ __ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
+ ROCKER_TLV_CMD_PORT_SETTINGS_MAX =
+ __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1,
+};
+
+enum rocker_port_mode {
+ ROCKER_PORT_MODE_OF_DPA,
+};
+
+enum {
+ ROCKER_TLV_EVENT_UNSPEC,
+ ROCKER_TLV_EVENT_TYPE, /* u16 */
+ ROCKER_TLV_EVENT_INFO, /* nest */
+
+ __ROCKER_TLV_EVENT_MAX,
+ ROCKER_TLV_EVENT_MAX = __ROCKER_TLV_EVENT_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_EVENT_TYPE_UNSPEC,
+ ROCKER_TLV_EVENT_TYPE_LINK_CHANGED,
+ ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN,
+
+ __ROCKER_TLV_EVENT_TYPE_MAX,
+ ROCKER_TLV_EVENT_TYPE_MAX = __ROCKER_TLV_EVENT_TYPE_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_EVENT_LINK_CHANGED_UNSPEC,
+ ROCKER_TLV_EVENT_LINK_CHANGED_LPORT, /* u32 */
+ ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP, /* u8 */
+
+ __ROCKER_TLV_EVENT_LINK_CHANGED_MAX,
+ ROCKER_TLV_EVENT_LINK_CHANGED_MAX =
+ __ROCKER_TLV_EVENT_LINK_CHANGED_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_EVENT_MAC_VLAN_UNSPEC,
+ ROCKER_TLV_EVENT_MAC_VLAN_LPORT, /* u32 */
+ ROCKER_TLV_EVENT_MAC_VLAN_MAC, /* binary */
+ ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID, /* __be16 */
+
+ __ROCKER_TLV_EVENT_MAC_VLAN_MAX,
+ ROCKER_TLV_EVENT_MAC_VLAN_MAX = __ROCKER_TLV_EVENT_MAC_VLAN_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_RX_UNSPEC,
+ ROCKER_TLV_RX_FLAGS, /* u16, see ROCKER_RX_FLAGS_ */
+ ROCKER_TLV_RX_CSUM, /* u16 */
+ ROCKER_TLV_RX_FRAG_ADDR, /* u64 */
+ ROCKER_TLV_RX_FRAG_MAX_LEN, /* u16 */
+ ROCKER_TLV_RX_FRAG_LEN, /* u16 */
+
+ __ROCKER_TLV_RX_MAX,
+ ROCKER_TLV_RX_MAX = __ROCKER_TLV_RX_MAX - 1,
+};
+
+#define ROCKER_RX_FLAGS_IPV4 (1 << 0)
+#define ROCKER_RX_FLAGS_IPV6 (1 << 1)
+#define ROCKER_RX_FLAGS_CSUM_CALC (1 << 2)
+#define ROCKER_RX_FLAGS_IPV4_CSUM_GOOD (1 << 3)
+#define ROCKER_RX_FLAGS_IP_FRAG (1 << 4)
+#define ROCKER_RX_FLAGS_TCP (1 << 5)
+#define ROCKER_RX_FLAGS_UDP (1 << 6)
+#define ROCKER_RX_FLAGS_TCP_UDP_CSUM_GOOD (1 << 7)
+
+enum {
+ ROCKER_TLV_TX_UNSPEC,
+ ROCKER_TLV_TX_OFFLOAD, /* u8, see ROCKER_TX_OFFLOAD_ */
+ ROCKER_TLV_TX_L3_CSUM_OFF, /* u16 */
+ ROCKER_TLV_TX_TSO_MSS, /* u16 */
+ ROCKER_TLV_TX_TSO_HDR_LEN, /* u16 */
+ ROCKER_TLV_TX_FRAGS, /* array */
+
+ __ROCKER_TLV_TX_MAX,
+ ROCKER_TLV_TX_MAX = __ROCKER_TLV_TX_MAX - 1,
+};
+
+#define ROCKER_TX_OFFLOAD_NONE 0
+#define ROCKER_TX_OFFLOAD_IP_CSUM 1
+#define ROCKER_TX_OFFLOAD_TCP_UDP_CSUM 2
+#define ROCKER_TX_OFFLOAD_L3_CSUM 3
+#define ROCKER_TX_OFFLOAD_TSO 4
+
+#define ROCKER_TX_FRAGS_MAX 16
+
+enum {
+ ROCKER_TLV_TX_FRAG_UNSPEC,
+ ROCKER_TLV_TX_FRAG, /* nest */
+
+ __ROCKER_TLV_TX_FRAG_MAX,
+ ROCKER_TLV_TX_FRAG_MAX = __ROCKER_TLV_TX_FRAG_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_TX_FRAG_ATTR_UNSPEC,
+ ROCKER_TLV_TX_FRAG_ATTR_ADDR, /* u64 */
+ ROCKER_TLV_TX_FRAG_ATTR_LEN, /* u16 */
+
+ __ROCKER_TLV_TX_FRAG_ATTR_MAX,
+ ROCKER_TLV_TX_FRAG_ATTR_MAX = __ROCKER_TLV_TX_FRAG_ATTR_MAX - 1,
+};
+
+/* cmd info nested for OF-DPA msgs */
+enum {
+ ROCKER_TLV_OF_DPA_UNSPEC,
+ ROCKER_TLV_OF_DPA_TABLE_ID, /* u16 */
+ ROCKER_TLV_OF_DPA_PRIORITY, /* u32 */
+ ROCKER_TLV_OF_DPA_HARDTIME, /* u32 */
+ ROCKER_TLV_OF_DPA_IDLETIME, /* u32 */
+ ROCKER_TLV_OF_DPA_COOKIE, /* u64 */
+ ROCKER_TLV_OF_DPA_IN_LPORT, /* u32 */
+ ROCKER_TLV_OF_DPA_IN_LPORT_MASK, /* u32 */
+ ROCKER_TLV_OF_DPA_OUT_LPORT, /* u32 */
+ ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, /* u16 */
+ ROCKER_TLV_OF_DPA_GROUP_ID, /* u32 */
+ ROCKER_TLV_OF_DPA_GROUP_ID_LOWER, /* u32 */
+ ROCKER_TLV_OF_DPA_GROUP_COUNT, /* u16 */
+ ROCKER_TLV_OF_DPA_GROUP_IDS, /* u32 array */
+ ROCKER_TLV_OF_DPA_VLAN_ID, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_ID_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_PCP, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_PCP_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_PCP_ACTION, /* u8 */
+ ROCKER_TLV_OF_DPA_NEW_VLAN_ID, /* __be16 */
+ ROCKER_TLV_OF_DPA_NEW_VLAN_PCP, /* u8 */
+ ROCKER_TLV_OF_DPA_TUNNEL_ID, /* u32 */
+ ROCKER_TLV_OF_DPA_TUN_LOG_LPORT, /* u32 */
+ ROCKER_TLV_OF_DPA_ETHERTYPE, /* __be16 */
+ ROCKER_TLV_OF_DPA_DST_MAC, /* binary */
+ ROCKER_TLV_OF_DPA_DST_MAC_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_MAC, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_MAC_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_IP_PROTO, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_PROTO_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_DSCP, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_DSCP_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_DSCP_ACTION, /* u8 */
+ ROCKER_TLV_OF_DPA_NEW_IP_DSCP, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_ECN, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_ECN_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_DST_IP, /* __be32 */
+ ROCKER_TLV_OF_DPA_DST_IP_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_SRC_IP, /* __be32 */
+ ROCKER_TLV_OF_DPA_SRC_IP_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_DST_IPV6, /* binary */
+ ROCKER_TLV_OF_DPA_DST_IPV6_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_IPV6, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_IPV6_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_ARP_IP, /* __be32 */
+ ROCKER_TLV_OF_DPA_SRC_ARP_IP_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_L4_DST_PORT, /* __be16 */
+ ROCKER_TLV_OF_DPA_L4_DST_PORT_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_L4_SRC_PORT, /* __be16 */
+ ROCKER_TLV_OF_DPA_L4_SRC_PORT_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_ICMP_TYPE, /* u8 */
+ ROCKER_TLV_OF_DPA_ICMP_TYPE_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_ICMP_CODE, /* u8 */
+ ROCKER_TLV_OF_DPA_ICMP_CODE_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_IPV6_LABEL, /* __be32 */
+ ROCKER_TLV_OF_DPA_IPV6_LABEL_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_QUEUE_ID_ACTION, /* u8 */
+ ROCKER_TLV_OF_DPA_NEW_QUEUE_ID, /* u8 */
+ ROCKER_TLV_OF_DPA_CLEAR_ACTIONS, /* u32 */
+ ROCKER_TLV_OF_DPA_POP_VLAN, /* u8 */
+ ROCKER_TLV_OF_DPA_TTL_CHECK, /* u8 */
+ ROCKER_TLV_OF_DPA_COPY_CPU_ACTION, /* u8 */
+
+ __ROCKER_TLV_OF_DPA_MAX,
+ ROCKER_TLV_OF_DPA_MAX = __ROCKER_TLV_OF_DPA_MAX - 1,
+};
+
+/* OF-DPA table IDs */
+
+enum rocker_of_dpa_table_id {
+ ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT = 0,
+ ROCKER_OF_DPA_TABLE_ID_VLAN = 10,
+ ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC = 20,
+ ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING = 30,
+ ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING = 40,
+ ROCKER_OF_DPA_TABLE_ID_BRIDGING = 50,
+ ROCKER_OF_DPA_TABLE_ID_ACL_POLICY = 60,
+};
+
+/* OF-DPA flow stats */
+enum {
+ ROCKER_TLV_OF_DPA_FLOW_STAT_UNSPEC,
+ ROCKER_TLV_OF_DPA_FLOW_STAT_DURATION, /* u32 */
+ ROCKER_TLV_OF_DPA_FLOW_STAT_RX_PKTS, /* u64 */
+ ROCKER_TLV_OF_DPA_FLOW_STAT_TX_PKTS, /* u64 */
+
+ __ROCKER_TLV_OF_DPA_FLOW_STAT_MAX,
+ ROCKER_TLV_OF_DPA_FLOW_STAT_MAX = __ROCKER_TLV_OF_DPA_FLOW_STAT_MAX - 1,
+};
+
+/* OF-DPA group types */
+enum rocker_of_dpa_group_type {
+ ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE = 0,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_INTERFACE,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_MCAST,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_ECMP,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY,
+};
+
+/* OF-DPA group L2 overlay types */
+enum rocker_of_dpa_overlay_type {
+ ROCKER_OF_DPA_OVERLAY_TYPE_FLOOD_UCAST = 0,
+ ROCKER_OF_DPA_OVERLAY_TYPE_FLOOD_MCAST,
+ ROCKER_OF_DPA_OVERLAY_TYPE_MCAST_UCAST,
+ ROCKER_OF_DPA_OVERLAY_TYPE_MCAST_MCAST,
+};
+
+/* OF-DPA group ID encoding */
+#define ROCKER_GROUP_TYPE_SHIFT 28
+#define ROCKER_GROUP_TYPE_MASK 0xf0000000
+#define ROCKER_GROUP_VLAN_SHIFT 16
+#define ROCKER_GROUP_VLAN_MASK 0x0fff0000
+#define ROCKER_GROUP_PORT_SHIFT 0
+#define ROCKER_GROUP_PORT_MASK 0x0000ffff
+#define ROCKER_GROUP_TUNNEL_ID_SHIFT 12
+#define ROCKER_GROUP_TUNNEL_ID_MASK 0x0ffff000
+#define ROCKER_GROUP_SUBTYPE_SHIFT 10
+#define ROCKER_GROUP_SUBTYPE_MASK 0x00000c00
+#define ROCKER_GROUP_INDEX_SHIFT 0
+#define ROCKER_GROUP_INDEX_MASK 0x0000ffff
+#define ROCKER_GROUP_INDEX_LONG_SHIFT 0
+#define ROCKER_GROUP_INDEX_LONG_MASK 0x0fffffff
+
+#define ROCKER_GROUP_TYPE_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_TYPE_MASK) >> ROCKER_GROUP_TYPE_SHIFT)
+#define ROCKER_GROUP_TYPE_SET(type) \
+ (((type) << ROCKER_GROUP_TYPE_SHIFT) & ROCKER_GROUP_TYPE_MASK)
+#define ROCKER_GROUP_VLAN_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_VLAN_ID_MASK) >> ROCKER_GROUP_VLAN_ID_SHIFT)
+#define ROCKER_GROUP_VLAN_SET(vlan_id) \
+ (((vlan_id) << ROCKER_GROUP_VLAN_SHIFT) & ROCKER_GROUP_VLAN_MASK)
+#define ROCKER_GROUP_PORT_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_PORT_MASK) >> ROCKER_GROUP_PORT_SHIFT)
+#define ROCKER_GROUP_PORT_SET(port) \
+ (((port) << ROCKER_GROUP_PORT_SHIFT) & ROCKER_GROUP_PORT_MASK)
+#define ROCKER_GROUP_INDEX_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_INDEX_MASK) >> ROCKER_GROUP_INDEX_SHIFT)
+#define ROCKER_GROUP_INDEX_SET(index) \
+ (((index) << ROCKER_GROUP_INDEX_SHIFT) & ROCKER_GROUP_INDEX_MASK)
+#define ROCKER_GROUP_INDEX_LONG_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_INDEX_LONG_MASK) >> \
+ ROCKER_GROUP_INDEX_LONG_SHIFT)
+#define ROCKER_GROUP_INDEX_LONG_SET(index) \
+ (((index) << ROCKER_GROUP_INDEX_LONG_SHIFT) & \
+ ROCKER_GROUP_INDEX_LONG_MASK)
+
+#define ROCKER_GROUP_NONE 0
+#define ROCKER_GROUP_L2_INTERFACE(vlan_id, port) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) |\
+ ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_PORT_SET(port))
+#define ROCKER_GROUP_L2_REWRITE(index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE) |\
+ ROCKER_GROUP_INDEX_LONG_SET(index))
+#define ROCKER_GROUP_L2_MCAST(vlan_id, index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST) |\
+ ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_INDEX_SET(index))
+#define ROCKER_GROUP_L2_FLOOD(vlan_id, index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD) |\
+ ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_INDEX_SET(index))
+#define ROCKER_GROUP_L3_UNICAST(index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST) |\
+ ROCKER_GROUP_INDEX_LONG_SET(index))
+
+/* Rocker general purpose registers */
+#define ROCKER_CONTROL 0x0300
+#define ROCKER_PORT_PHYS_COUNT 0x0304
+#define ROCKER_PORT_PHYS_LINK_STATUS 0x0310 /* 8-byte */
+#define ROCKER_PORT_PHYS_ENABLE 0x0318 /* 8-byte */
+#define ROCKER_SWITCH_ID 0x0320 /* 8-byte */
+
+/* Rocker control bits */
+#define ROCKER_CONTROL_RESET (1 << 0)
+
+#endif
--
1.9.3
^ permalink raw reply related
* [patch net-next v3 09/17] bridge: add API to notify bridge driver of learned FBD on offloaded device
From: Jiri Pirko @ 2014-11-25 10:28 UTC (permalink / raw)
To: netdev
Cc: davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse, pshelar,
azhou, ben, stephen, jeffrey.t.kirsher, vyasevic, xiyou.wangcong,
john.r.fastabend, edumazet, jhs, sfeldma, f.fainelli, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram, ronye,
simon.horman, alexander.h.duyck, john.ronciak, mleitner, shrijeet,
gospo, bcrl
In-Reply-To: <1416911328-10979-1-git-send-email-jiri@resnulli.us>
From: Scott Feldman <sfeldma@gmail.com>
When the swdev device learns a new mac/vlan on a port, it sends some async
notification to the driver and the driver installs an FDB in the device.
To give a holistic system view, the learned mac/vlan should be reflected
in the bridge's FBD table, so the user, using normal iproute2 cmds, can view
what is currently learned by the device. This API on the bridge driver gives
a way for the swdev driver to install an FBD entry in the bridge FBD table.
(And remove one).
This is equivalent to the device running these cmds:
bridge fdb [add|del] <mac> dev <dev> vid <vlan id> master
This patch needs some extra eyeballs for review, in paricular around the
locking and contexts.
Signed-off-by: Scott Feldman <sfeldma@gmail.com>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
v2->v3:
-added "external" word into function names to emphasize fdbs are learned
externally
-added "added_by_external_learn" to fbd entry struct indicate the entry
was learned externaly and build some logic around that
-expose the fact that fdb entry was learned externally to userspace
v1->v2:
-no change
---
include/linux/if_bridge.h | 18 +++++++++
include/uapi/linux/neighbour.h | 1 +
net/bridge/br_fdb.c | 91 +++++++++++++++++++++++++++++++++++++++++-
net/bridge/br_private.h | 1 +
4 files changed, 110 insertions(+), 1 deletion(-)
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 808dcb8..fa2eca6 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -37,6 +37,24 @@ extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __use
typedef int br_should_route_hook_t(struct sk_buff *skb);
extern br_should_route_hook_t __rcu *br_should_route_hook;
+#if IS_ENABLED(CONFIG_BRIDGE)
+int br_fdb_external_learn_add(struct net_device *dev,
+ const unsigned char *addr, u16 vid);
+int br_fdb_external_learn_del(struct net_device *dev,
+ const unsigned char *addr, u16 vid);
+#else
+static inline int br_fdb_external_learn_add(struct net_device *dev,
+ const unsigned char *addr, u16 vid)
+{
+ return 0;
+}
+static inline int br_fdb_external_learn_del(struct net_device *dev,
+ const unsigned char *addr, u16 vid)
+{
+ return 0;
+}
+#endif
+
#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
int br_multicast_list_adjacent(struct net_device *dev,
struct list_head *br_ip_list);
diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h
index 4a1d7e9..3a9b0df 100644
--- a/include/uapi/linux/neighbour.h
+++ b/include/uapi/linux/neighbour.h
@@ -40,6 +40,7 @@ enum {
#define NTF_SELF 0x02
#define NTF_MASTER 0x04
+#define NTF_EXT_LEARNED 0x10
/*
* Neighbor Cache Entry States.
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index b1be971..b42e71d 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -481,6 +481,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
fdb->is_local = 0;
fdb->is_static = 0;
fdb->added_by_user = 0;
+ fdb->added_by_external_learn = 0;
fdb->updated = fdb->used = jiffies;
hlist_add_head_rcu(&fdb->hlist, head);
}
@@ -613,7 +614,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
- ndm->ndm_flags = 0;
+ ndm->ndm_flags = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0;
ndm->ndm_type = 0;
ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
ndm->ndm_state = fdb_to_nud(fdb);
@@ -983,3 +984,91 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
}
}
}
+
+int br_fdb_external_learn_add(struct net_device *dev,
+ const unsigned char *addr, u16 vid)
+{
+ struct net_bridge_port *p;
+ struct net_bridge *br;
+ struct hlist_head *head;
+ struct net_bridge_fdb_entry *fdb;
+ int err = 0;
+
+ rtnl_lock();
+
+ p = br_port_get_rtnl(dev);
+ if (!p) {
+ pr_info("bridge: %s not a bridge port\n", dev->name);
+ err = -EINVAL;
+ goto err_rtnl_unlock;
+ }
+
+ br = p->br;
+
+ spin_lock(&br->hash_lock);
+
+ head = &br->hash[br_mac_hash(addr, vid)];
+ fdb = fdb_find(head, addr, vid);
+ if (!fdb) {
+ fdb = fdb_create(head, p, addr, vid);
+ if (!fdb) {
+ err = -ENOMEM;
+ goto err_unlock;
+ }
+ fdb->added_by_external_learn = 1;
+ fdb_notify(br, fdb, RTM_NEWNEIGH);
+ } else if (fdb->added_by_external_learn) {
+ /* Refresh entry */
+ fdb->updated = fdb->used = jiffies;
+ } else if (!fdb->added_by_user) {
+ /* Take over SW learned entry */
+ fdb->added_by_external_learn = 1;
+ fdb->updated = jiffies;
+ fdb_notify(br, fdb, RTM_NEWNEIGH);
+ }
+
+err_unlock:
+ spin_unlock(&br->hash_lock);
+err_rtnl_unlock:
+ rtnl_unlock();
+
+ return err;
+}
+EXPORT_SYMBOL(br_fdb_external_learn_add);
+
+int br_fdb_external_learn_del(struct net_device *dev,
+ const unsigned char *addr, u16 vid)
+{
+ struct net_bridge_port *p;
+ struct net_bridge *br;
+ struct hlist_head *head;
+ struct net_bridge_fdb_entry *fdb;
+ int err = 0;
+
+ rtnl_lock();
+
+ p = br_port_get_rtnl(dev);
+ if (!p) {
+ pr_info("bridge: %s not a bridge port\n", dev->name);
+ err = -EINVAL;
+ goto err_rtnl_unlock;
+ }
+
+ br = p->br;
+
+ spin_lock(&br->hash_lock);
+
+ head = &br->hash[br_mac_hash(addr, vid)];
+ fdb = fdb_find(head, addr, vid);
+ if (fdb && fdb->added_by_external_learn)
+ fdb_delete(br, fdb);
+ else
+ err = -ENOENT;
+
+ spin_unlock(&br->hash_lock);
+err_rtnl_unlock:
+ rtnl_unlock();
+
+ return err;
+}
+EXPORT_SYMBOL(br_fdb_external_learn_del);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4f577c4..02cd63b 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -101,6 +101,7 @@ struct net_bridge_fdb_entry
unsigned char is_local;
unsigned char is_static;
unsigned char added_by_user;
+ unsigned char added_by_external_learn;
__u16 vlan_id;
};
--
1.9.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox