ralink: add reworked ethernet driver

update to the version of the driver that is on its way upstream

Signed-off-by: John Crispin <blogic@openwrt.org>

SVN-Revision: 47898
This commit is contained in:
John Crispin 2015-12-17 09:25:24 +00:00
parent a99c78a09a
commit 31116661ff
17 changed files with 7877 additions and 24 deletions

View file

@ -24,30 +24,6 @@ index 51f33a5..d7c4ba4 100644
ralink_clk_add("10000b00.spi", sys_rate);
ralink_clk_add("10000b40.spi", sys_rate);
ralink_clk_add("10000100.timer", wdt_rate);
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 05aa759..5628f2d 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -154,6 +154,7 @@ source "drivers/net/ethernet/packetengines/Kconfig"
source "drivers/net/ethernet/pasemi/Kconfig"
source "drivers/net/ethernet/qlogic/Kconfig"
source "drivers/net/ethernet/qualcomm/Kconfig"
+source "drivers/net/ethernet/ralink/Kconfig"
source "drivers/net/ethernet/realtek/Kconfig"
source "drivers/net/ethernet/renesas/Kconfig"
source "drivers/net/ethernet/rdc/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index ddfc808..cbfe95e 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_NET_PACKET_ENGINE) += packetengines/
obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/
obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/
+obj-$(CONFIG_NET_RALINK) += ralink/
obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
--
1.7.10.4

View file

@ -0,0 +1,162 @@
From 9c2487f148ee38807d86beaf12dc2b818a764a99 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Tue, 17 Nov 2015 00:20:07 +0100
Subject: [PATCH 500/513] Documentation: DT: net: add docs for ralink/mediatek
SoC ethernet binding
Add three files. ralink,rt2880-net.txt descibes the actual frame engine
and the other two describe the switch forntend bindings.
Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Michael Lee <igvtee@gmail.com>
Cc: devicetree@vger.kernel.org
---
.../bindings/net/mediatek,mt7620-gsw.txt | 26 +++++++++
.../devicetree/bindings/net/ralink,rt2880-net.txt | 61 ++++++++++++++++++++
.../devicetree/bindings/net/ralink,rt3050-esw.txt | 32 ++++++++++
3 files changed, 119 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/mediatek,mt7620-gsw.txt
create mode 100644 Documentation/devicetree/bindings/net/ralink,rt2880-net.txt
create mode 100644 Documentation/devicetree/bindings/net/ralink,rt3050-esw.txt
diff --git a/Documentation/devicetree/bindings/net/mediatek,mt7620-gsw.txt b/Documentation/devicetree/bindings/net/mediatek,mt7620-gsw.txt
new file mode 100644
index 0000000..fb47d8e
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mediatek,mt7620-gsw.txt
@@ -0,0 +1,26 @@
+Mediatek Gigabit Switch
+=======================
+
+The mediatek gigabit switch can be found on Mediatek SoCs (mt7620, mt7621).
+
+Required properties:
+- compatible: Should be "mediatek,mt7620-gsw"
+- reg: Address and length of the register set for the device
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device
+- interrupts: Should contain the gigabit switches interrupt
+- resets: Should contain the gigabit switches resets
+- reset-names: Should contain the reset names "gsw"
+
+Example:
+
+gsw@10110000 {
+ compatible = "ralink,mt7620-gsw";
+ reg = <0x10110000 8000>;
+
+ resets = <&rstctrl 23>;
+ reset-names = "gsw";
+
+ interrupt-parent = <&intc>;
+ interrupts = <17>;
+};
diff --git a/Documentation/devicetree/bindings/net/ralink,rt2880-net.txt b/Documentation/devicetree/bindings/net/ralink,rt2880-net.txt
new file mode 100644
index 0000000..88b095d
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/ralink,rt2880-net.txt
@@ -0,0 +1,61 @@
+Ralink Frame Engine Ethernet controller
+=======================================
+
+The Ralink frame engine ethernet controller can be found on Ralink and
+Mediatek SoCs (RT288x, RT3x5x, RT366x, RT388x, rt5350, mt7620, mt7621, mt76x8).
+
+Depending on the SoC, there is a number of ports connected to the CPU port
+directly and/or via a (gigabit-)switch.
+
+* Ethernet controller node
+
+Required properties:
+- compatible: Should be one of "ralink,rt2880-eth", "ralink,rt3050-eth",
+ "ralink,rt3050-eth", "ralink,rt3883-eth", "ralink,rt5350-eth",
+ "mediatek,mt7620-eth", "mediatek,mt7621-eth"
+- reg: Address and length of the register set for the device
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device
+- interrupts: Should contain the frame engines interrupt
+- resets: Should contain the frame engines resets
+- reset-names: Should contain the reset names "fe". If a switch is present
+ "esw" is also required.
+
+
+* Ethernet port node
+
+Required properties:
+- compatible: Should be "ralink,eth-port"
+- reg: The number of the physical port
+- phy-handle: reference to the node describing the phy
+
+Example:
+
+mdio-bus {
+ ...
+ phy0: ethernet-phy@0 {
+ phy-mode = "mii";
+ reg = <0>;
+ };
+};
+
+ethernet@400000 {
+ compatible = "ralink,rt2880-eth";
+ reg = <0x00400000 10000>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ resets = <&rstctrl 18>;
+ reset-names = "fe";
+
+ interrupt-parent = <&cpuintc>;
+ interrupts = <5>;
+
+ port@0 {
+ compatible = "ralink,eth-port";
+ reg = <0>;
+ phy-handle = <&phy0>;
+ };
+
+};
diff --git a/Documentation/devicetree/bindings/net/ralink,rt3050-esw.txt b/Documentation/devicetree/bindings/net/ralink,rt3050-esw.txt
new file mode 100644
index 0000000..ed32e21
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/ralink,rt3050-esw.txt
@@ -0,0 +1,32 @@
+Ralink Fast Ethernet Embedded Switch
+====================================
+
+The ralink fast ethernet embedded switch can be found on Ralink and Mediatek
+SoCs (RT3x5x, rt5350, mt76x8).
+
+Required properties:
+- compatible: Should be "ralink,rt3050-esw"
+- reg: Address and length of the register set for the device
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device
+- interrupts: Should contain the embedded switches interrupt
+- resets: Should contain the embedded switches resets
+- reset-names: Should contain the reset names "esw"
+
+Optional properties:
+- ralink,portmap: can be used to choose if the default switch setup is
+ llllw or wllll
+- ralink,led_polarity: override the active high/low settings of the leds
+
+Example:
+
+esw@10110000 {
+ compatible = "ralink,rt3050-esw";
+ reg = <0x10110000 8000>;
+
+ resets = <&rstctrl 23>;
+ reset-names = "esw";
+
+ interrupt-parent = <&intc>;
+ interrupts = <17>;
+};
--
1.7.10.4

View file

@ -0,0 +1,700 @@
From 2c39ddc83452c34fedc86261ed1f96d7537adfd1 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 21:28:10 +0100
Subject: [PATCH 502/513] net-next: mediatek: add switch driver for rt3050
This driver is very basic and only provides basic init and irq support.
Switchdev support for this device will follow.
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/esw_rt3050.c | 640 ++++++++++++++++++++++++++++
drivers/net/ethernet/mediatek/esw_rt3050.h | 29 ++
2 files changed, 669 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/esw_rt3050.c
create mode 100644 drivers/net/ethernet/mediatek/esw_rt3050.h
diff --git a/drivers/net/ethernet/mediatek/esw_rt3050.c b/drivers/net/ethernet/mediatek/esw_rt3050.c
new file mode 100644
index 0000000..f07f4a5
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.c
@@ -0,0 +1,640 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+
+#include <linux/ioport.h>
+#include <linux/mii.h>
+
+#include <ralink_regs.h>
+
+/* HW limitations for this switch:
+ * - No large frame support (PKT_MAX_LEN at most 1536)
+ * - Can't have untagged vlan and tagged vlan on one port at the same time,
+ * though this might be possible using the undocumented PPE.
+ */
+
+#define RT305X_ESW_REG_ISR 0x00
+#define RT305X_ESW_REG_IMR 0x04
+#define RT305X_ESW_REG_FCT0 0x08
+#define RT305X_ESW_REG_PFC1 0x14
+#define RT305X_ESW_REG_ATS 0x24
+#define RT305X_ESW_REG_ATS0 0x28
+#define RT305X_ESW_REG_ATS1 0x2c
+#define RT305X_ESW_REG_ATS2 0x30
+#define RT305X_ESW_REG_PVIDC(_n) (0x40 + 4 * (_n))
+#define RT305X_ESW_REG_VLANI(_n) (0x50 + 4 * (_n))
+#define RT305X_ESW_REG_VMSC(_n) (0x70 + 4 * (_n))
+#define RT305X_ESW_REG_POA 0x80
+#define RT305X_ESW_REG_FPA 0x84
+#define RT305X_ESW_REG_SOCPC 0x8c
+#define RT305X_ESW_REG_POC0 0x90
+#define RT305X_ESW_REG_POC1 0x94
+#define RT305X_ESW_REG_POC2 0x98
+#define RT305X_ESW_REG_SGC 0x9c
+#define RT305X_ESW_REG_STRT 0xa0
+#define RT305X_ESW_REG_PCR0 0xc0
+#define RT305X_ESW_REG_PCR1 0xc4
+#define RT305X_ESW_REG_FPA2 0xc8
+#define RT305X_ESW_REG_FCT2 0xcc
+#define RT305X_ESW_REG_SGC2 0xe4
+#define RT305X_ESW_REG_P0LED 0xa4
+#define RT305X_ESW_REG_P1LED 0xa8
+#define RT305X_ESW_REG_P2LED 0xac
+#define RT305X_ESW_REG_P3LED 0xb0
+#define RT305X_ESW_REG_P4LED 0xb4
+#define RT305X_ESW_REG_PXPC(_x) (0xe8 + (4 * _x))
+#define RT305X_ESW_REG_P1PC 0xec
+#define RT305X_ESW_REG_P2PC 0xf0
+#define RT305X_ESW_REG_P3PC 0xf4
+#define RT305X_ESW_REG_P4PC 0xf8
+#define RT305X_ESW_REG_P5PC 0xfc
+
+#define RT305X_ESW_LED_LINK 0
+#define RT305X_ESW_LED_100M 1
+#define RT305X_ESW_LED_DUPLEX 2
+#define RT305X_ESW_LED_ACTIVITY 3
+#define RT305X_ESW_LED_COLLISION 4
+#define RT305X_ESW_LED_LINKACT 5
+#define RT305X_ESW_LED_DUPLCOLL 6
+#define RT305X_ESW_LED_10MACT 7
+#define RT305X_ESW_LED_100MACT 8
+/* Additional led states not in datasheet: */
+#define RT305X_ESW_LED_BLINK 10
+#define RT305X_ESW_LED_ON 12
+
+#define RT305X_ESW_LINK_S 25
+#define RT305X_ESW_DUPLEX_S 9
+#define RT305X_ESW_SPD_S 0
+
+#define RT305X_ESW_PCR0_WT_NWAY_DATA_S 16
+#define RT305X_ESW_PCR0_WT_PHY_CMD BIT(13)
+#define RT305X_ESW_PCR0_CPU_PHY_REG_S 8
+
+#define RT305X_ESW_PCR1_WT_DONE BIT(0)
+
+#define RT305X_ESW_ATS_TIMEOUT (5 * HZ)
+#define RT305X_ESW_PHY_TIMEOUT (5 * HZ)
+
+#define RT305X_ESW_PVIDC_PVID_M 0xfff
+#define RT305X_ESW_PVIDC_PVID_S 12
+
+#define RT305X_ESW_VLANI_VID_M 0xfff
+#define RT305X_ESW_VLANI_VID_S 12
+
+#define RT305X_ESW_VMSC_MSC_M 0xff
+#define RT305X_ESW_VMSC_MSC_S 8
+
+#define RT305X_ESW_SOCPC_DISUN2CPU_S 0
+#define RT305X_ESW_SOCPC_DISMC2CPU_S 8
+#define RT305X_ESW_SOCPC_DISBC2CPU_S 16
+#define RT305X_ESW_SOCPC_CRC_PADDING BIT(25)
+
+#define RT305X_ESW_POC0_EN_BP_S 0
+#define RT305X_ESW_POC0_EN_FC_S 8
+#define RT305X_ESW_POC0_DIS_RMC2CPU_S 16
+#define RT305X_ESW_POC0_DIS_PORT_M 0x7f
+#define RT305X_ESW_POC0_DIS_PORT_S 23
+
+#define RT305X_ESW_POC2_UNTAG_EN_M 0xff
+#define RT305X_ESW_POC2_UNTAG_EN_S 0
+#define RT305X_ESW_POC2_ENAGING_S 8
+#define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16
+
+#define RT305X_ESW_SGC2_DOUBLE_TAG_M 0x7f
+#define RT305X_ESW_SGC2_DOUBLE_TAG_S 0
+#define RT305X_ESW_SGC2_LAN_PMAP_M 0x3f
+#define RT305X_ESW_SGC2_LAN_PMAP_S 24
+
+#define RT305X_ESW_PFC1_EN_VLAN_M 0xff
+#define RT305X_ESW_PFC1_EN_VLAN_S 16
+#define RT305X_ESW_PFC1_EN_TOS_S 24
+
+#define RT305X_ESW_VLAN_NONE 0xfff
+
+#define RT305X_ESW_GSC_BC_STROM_MASK 0x3
+#define RT305X_ESW_GSC_BC_STROM_SHIFT 4
+
+#define RT305X_ESW_GSC_LED_FREQ_MASK 0x3
+#define RT305X_ESW_GSC_LED_FREQ_SHIFT 23
+
+#define RT305X_ESW_POA_LINK_MASK 0x1f
+#define RT305X_ESW_POA_LINK_SHIFT 25
+
+#define RT305X_ESW_PORT_ST_CHG BIT(26)
+#define RT305X_ESW_PORT0 0
+#define RT305X_ESW_PORT1 1
+#define RT305X_ESW_PORT2 2
+#define RT305X_ESW_PORT3 3
+#define RT305X_ESW_PORT4 4
+#define RT305X_ESW_PORT5 5
+#define RT305X_ESW_PORT6 6
+
+#define RT305X_ESW_PMAP_LLLLLL 0x3f
+#define RT305X_ESW_PMAP_LLLLWL 0x2f
+#define RT305X_ESW_PMAP_WLLLLL 0x3e
+
+#define RT305X_ESW_PORTS_INTERNAL \
+ (BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) | \
+ BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) | \
+ BIT(RT305X_ESW_PORT4))
+
+#define RT305X_ESW_PORTS_NOCPU \
+ (RT305X_ESW_PORTS_INTERNAL | BIT(RT305X_ESW_PORT5))
+
+#define RT305X_ESW_PORTS_CPU BIT(RT305X_ESW_PORT6)
+
+#define RT305X_ESW_PORTS_ALL \
+ (RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU)
+
+#define RT305X_ESW_NUM_PORTS 7
+#define RT305X_ESW_NUM_LEDS 5
+
+#define RT5350_EWS_REG_LED_POLARITY 0x168
+#define RT5350_RESET_EPHY BIT(24)
+
+struct esw_port {
+ bool disable;
+ u8 led;
+};
+
+struct rt305x_esw {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+
+ /* Protects against concurrent register r/w operations. */
+ spinlock_t reg_rw_lock;
+
+ unsigned char port_map;
+ unsigned int reg_led_polarity;
+
+ struct esw_port ports[RT305X_ESW_NUM_PORTS];
+
+};
+
+static inline void esw_w32(struct rt305x_esw *esw, u32 val, unsigned reg)
+{
+ __raw_writel(val, esw->base + reg);
+}
+
+static inline u32 esw_r32(struct rt305x_esw *esw, unsigned reg)
+{
+ return __raw_readl(esw->base + reg);
+}
+
+static inline void esw_rmw_raw(struct rt305x_esw *esw, unsigned reg,
+ unsigned long mask, unsigned long val)
+{
+ unsigned long t;
+
+ t = __raw_readl(esw->base + reg) & ~mask;
+ __raw_writel(t | val, esw->base + reg);
+}
+
+static void esw_rmw(struct rt305x_esw *esw, unsigned reg,
+ unsigned long mask, unsigned long val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&esw->reg_rw_lock, flags);
+ esw_rmw_raw(esw, reg, mask, val);
+ spin_unlock_irqrestore(&esw->reg_rw_lock, flags);
+}
+
+static u32 rt305x_mii_write(struct rt305x_esw *esw, u32 phy_addr,
+ u32 phy_register, u32 write_data)
+{
+ unsigned long t_start = jiffies;
+ int ret = 0;
+
+ while (1) {
+ if (!(esw_r32(esw, RT305X_ESW_REG_PCR1) &
+ RT305X_ESW_PCR1_WT_DONE))
+ break;
+ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) {
+ ret = 1;
+ goto out;
+ }
+ }
+
+ write_data &= 0xffff;
+ esw_w32(esw, (write_data << RT305X_ESW_PCR0_WT_NWAY_DATA_S) |
+ (phy_register << RT305X_ESW_PCR0_CPU_PHY_REG_S) |
+ (phy_addr) | RT305X_ESW_PCR0_WT_PHY_CMD,
+ RT305X_ESW_REG_PCR0);
+
+ t_start = jiffies;
+ while (1) {
+ if (esw_r32(esw, RT305X_ESW_REG_PCR1) &
+ RT305X_ESW_PCR1_WT_DONE)
+ break;
+
+ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) {
+ ret = 1;
+ break;
+ }
+ }
+out:
+ if (ret)
+ dev_err(esw->dev, "ramips_eth: MDIO timeout\n");
+ return ret;
+}
+
+static unsigned esw_get_port_disable(struct rt305x_esw *esw)
+{
+ unsigned reg;
+
+ reg = esw_r32(esw, RT305X_ESW_REG_POC0);
+ return (reg >> RT305X_ESW_POC0_DIS_PORT_S) &
+ RT305X_ESW_POC0_DIS_PORT_M;
+}
+
+static void esw_hw_init(struct rt305x_esw *esw)
+{
+ int i;
+ u8 port_disable = 0;
+ u8 port_map = RT305X_ESW_PMAP_LLLLLL;
+
+ /* vodoo from original driver */
+ esw_w32(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
+ esw_w32(esw, 0x00000000, RT305X_ESW_REG_SGC2);
+ /* Port priority 1 for all ports, vlan enabled. */
+ esw_w32(esw, 0x00005555 |
+ (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S),
+ RT305X_ESW_REG_PFC1);
+
+ /* Enable Back Pressure, and Flow Control */
+ esw_w32(esw, ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) |
+ (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)),
+ RT305X_ESW_REG_POC0);
+
+ /* Enable Aging, and VLAN TAG removal */
+ esw_w32(esw, ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) |
+ (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)),
+ RT305X_ESW_REG_POC2);
+
+ esw_w32(esw, 0x00d6500c, RT305X_ESW_REG_FCT2);
+
+ /* 300s aging timer, max packet len 1536, broadcast storm prevention
+ * disabled, disable collision abort, mac xor48 hash, 10 packet back
+ * pressure jam, GMII disable was_transmit, back pressure disabled,
+ * 30ms led flash, unmatched IGMP as broadcast, rmc tb fault to all
+ * ports.
+ */
+ esw_w32(esw, 0x0008a301, RT305X_ESW_REG_SGC);
+
+ /* Setup SoC Port control register */
+ esw_w32(esw,
+ (RT305X_ESW_SOCPC_CRC_PADDING |
+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISUN2CPU_S) |
+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISMC2CPU_S) |
+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISBC2CPU_S)),
+ RT305X_ESW_REG_SOCPC);
+
+ /* ext phy base addr 31, enable port 5 polling, rx/tx clock skew 1,
+ * turbo mii off, rgmi 3.3v off
+ * port5: disabled
+ * port6: enabled, gige, full-duplex, rx/tx-flow-control
+ */
+ esw_w32(esw, 0x3f502b28, RT305X_ESW_REG_FPA2);
+ esw_w32(esw, 0x00000000, RT305X_ESW_REG_FPA);
+
+ /* Force Link/Activity on ports */
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P0LED);
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P1LED);
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P2LED);
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P3LED);
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P4LED);
+
+ /* Copy disabled port configuration from bootloader setup */
+ port_disable = esw_get_port_disable(esw);
+ for (i = 0; i < 6; i++)
+ esw->ports[i].disable = (port_disable & (1 << i)) != 0;
+
+ if (ralink_soc == RT305X_SOC_RT3352) {
+ /* reset EPHY */
+ fe_reset(RT5350_RESET_EPHY);
+
+ rt305x_mii_write(esw, 0, 31, 0x8000);
+ for (i = 0; i < 5; i++) {
+ if (esw->ports[i].disable) {
+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
+ } else {
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_FULLDPLX |
+ BMCR_ANENABLE |
+ BMCR_SPEED100);
+ }
+ /* TX10 waveform coefficient LSB=0 disable PHY */
+ rt305x_mii_write(esw, i, 26, 0x1601);
+ /* TX100/TX10 AD/DA current bias */
+ rt305x_mii_write(esw, i, 29, 0x7016);
+ /* TX100 slew rate control */
+ rt305x_mii_write(esw, i, 30, 0x0038);
+ }
+
+ /* select global register */
+ rt305x_mii_write(esw, 0, 31, 0x0);
+ /* enlarge agcsel threshold 3 and threshold 2 */
+ rt305x_mii_write(esw, 0, 1, 0x4a40);
+ /* enlarge agcsel threshold 5 and threshold 4 */
+ rt305x_mii_write(esw, 0, 2, 0x6254);
+ /* enlarge agcsel threshold */
+ rt305x_mii_write(esw, 0, 3, 0xa17f);
+ rt305x_mii_write(esw, 0, 12, 0x7eaa);
+ /* longer TP_IDL tail length */
+ rt305x_mii_write(esw, 0, 14, 0x65);
+ /* increased squelch pulse count threshold. */
+ rt305x_mii_write(esw, 0, 16, 0x0684);
+ /* set TX10 signal amplitude threshold to minimum */
+ rt305x_mii_write(esw, 0, 17, 0x0fe0);
+ /* set squelch amplitude to higher threshold */
+ rt305x_mii_write(esw, 0, 18, 0x40ba);
+ /* tune TP_IDL tail and head waveform, enable power
+ * down slew rate control
+ */
+ rt305x_mii_write(esw, 0, 22, 0x253f);
+ /* set PLL/Receive bias current are calibrated */
+ rt305x_mii_write(esw, 0, 27, 0x2fda);
+ /* change PLL/Receive bias current to internal(RT3350) */
+ rt305x_mii_write(esw, 0, 28, 0xc410);
+ /* change PLL bias current to internal(RT3052_MP3) */
+ rt305x_mii_write(esw, 0, 29, 0x598b);
+ /* select local register */
+ rt305x_mii_write(esw, 0, 31, 0x8000);
+ } else if (ralink_soc == RT305X_SOC_RT5350) {
+ /* reset EPHY */
+ fe_reset(RT5350_RESET_EPHY);
+
+ /* set the led polarity */
+ esw_w32(esw, esw->reg_led_polarity & 0x1F,
+ RT5350_EWS_REG_LED_POLARITY);
+
+ /* local registers */
+ rt305x_mii_write(esw, 0, 31, 0x8000);
+ for (i = 0; i < 5; i++) {
+ if (esw->ports[i].disable) {
+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
+ } else {
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_FULLDPLX |
+ BMCR_ANENABLE |
+ BMCR_SPEED100);
+ }
+ /* TX10 waveform coefficient LSB=0 disable PHY */
+ rt305x_mii_write(esw, i, 26, 0x1601);
+ /* TX100/TX10 AD/DA current bias */
+ rt305x_mii_write(esw, i, 29, 0x7015);
+ /* TX100 slew rate control */
+ rt305x_mii_write(esw, i, 30, 0x0038);
+ }
+
+ /* global registers */
+ rt305x_mii_write(esw, 0, 31, 0x0);
+ /* enlarge agcsel threshold 3 and threshold 2 */
+ rt305x_mii_write(esw, 0, 1, 0x4a40);
+ /* enlarge agcsel threshold 5 and threshold 4 */
+ rt305x_mii_write(esw, 0, 2, 0x6254);
+ /* enlarge agcsel threshold 6 */
+ rt305x_mii_write(esw, 0, 3, 0xa17f);
+ rt305x_mii_write(esw, 0, 12, 0x7eaa);
+ /* longer TP_IDL tail length */
+ rt305x_mii_write(esw, 0, 14, 0x65);
+ /* increased squelch pulse count threshold. */
+ rt305x_mii_write(esw, 0, 16, 0x0684);
+ /* set TX10 signal amplitude threshold to minimum */
+ rt305x_mii_write(esw, 0, 17, 0x0fe0);
+ /* set squelch amplitude to higher threshold */
+ rt305x_mii_write(esw, 0, 18, 0x40ba);
+ /* tune TP_IDL tail and head waveform, enable power
+ * down slew rate control
+ */
+ rt305x_mii_write(esw, 0, 22, 0x253f);
+ /* set PLL/Receive bias current are calibrated */
+ rt305x_mii_write(esw, 0, 27, 0x2fda);
+ /* change PLL/Receive bias current to internal(RT3350) */
+ rt305x_mii_write(esw, 0, 28, 0xc410);
+ /* change PLL bias current to internal(RT3052_MP3) */
+ rt305x_mii_write(esw, 0, 29, 0x598b);
+ /* select local register */
+ rt305x_mii_write(esw, 0, 31, 0x8000);
+ } else if (ralink_soc == MT762X_SOC_MT7628AN || ralink_soc == MT762X_SOC_MT7688) {
+ int i;
+
+ /* reset EPHY */
+ fe_reset(RT5350_RESET_EPHY);
+
+ rt305x_mii_write(esw, 0, 31, 0x2000); /* change G2 page */
+ rt305x_mii_write(esw, 0, 26, 0x0020);
+
+ for (i = 0; i < 5; i++) {
+ rt305x_mii_write(esw, i, 31, 0x8000);
+ rt305x_mii_write(esw, i, 0, 0x3100);
+ rt305x_mii_write(esw, i, 30, 0xa000);
+ rt305x_mii_write(esw, i, 31, 0xa000);
+ rt305x_mii_write(esw, i, 16, 0x0606);
+ rt305x_mii_write(esw, i, 23, 0x0f0e);
+ rt305x_mii_write(esw, i, 24, 0x1610);
+ rt305x_mii_write(esw, i, 30, 0x1f15);
+ rt305x_mii_write(esw, i, 28, 0x6111);
+ rt305x_mii_write(esw, i, 31, 0x2000);
+ rt305x_mii_write(esw, i, 26, 0x0000);
+ }
+
+ /* 100Base AOI setting */
+ rt305x_mii_write(esw, 0, 31, 0x5000);
+ rt305x_mii_write(esw, 0, 19, 0x004a);
+ rt305x_mii_write(esw, 0, 20, 0x015a);
+ rt305x_mii_write(esw, 0, 21, 0x00ee);
+ rt305x_mii_write(esw, 0, 22, 0x0033);
+ rt305x_mii_write(esw, 0, 23, 0x020a);
+ rt305x_mii_write(esw, 0, 24, 0x0000);
+ rt305x_mii_write(esw, 0, 25, 0x024a);
+ rt305x_mii_write(esw, 0, 26, 0x035a);
+ rt305x_mii_write(esw, 0, 27, 0x02ee);
+ rt305x_mii_write(esw, 0, 28, 0x0233);
+ rt305x_mii_write(esw, 0, 29, 0x000a);
+ rt305x_mii_write(esw, 0, 30, 0x0000);
+ } else {
+ rt305x_mii_write(esw, 0, 31, 0x8000);
+ for (i = 0; i < 5; i++) {
+ if (esw->ports[i].disable) {
+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
+ } else {
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_FULLDPLX |
+ BMCR_ANENABLE |
+ BMCR_SPEED100);
+ }
+ /* TX10 waveform coefficient */
+ rt305x_mii_write(esw, i, 26, 0x1601);
+ /* TX100/TX10 AD/DA current bias */
+ rt305x_mii_write(esw, i, 29, 0x7058);
+ /* TX100 slew rate control */
+ rt305x_mii_write(esw, i, 30, 0x0018);
+ }
+
+ /* PHY IOT */
+ /* select global register */
+ rt305x_mii_write(esw, 0, 31, 0x0);
+ /* tune TP_IDL tail and head waveform */
+ rt305x_mii_write(esw, 0, 22, 0x052f);
+ /* set TX10 signal amplitude threshold to minimum */
+ rt305x_mii_write(esw, 0, 17, 0x0fe0);
+ /* set squelch amplitude to higher threshold */
+ rt305x_mii_write(esw, 0, 18, 0x40ba);
+ /* longer TP_IDL tail length */
+ rt305x_mii_write(esw, 0, 14, 0x65);
+ /* select local register */
+ rt305x_mii_write(esw, 0, 31, 0x8000);
+ }
+
+ if (esw->port_map)
+ port_map = esw->port_map;
+ else
+ port_map = RT305X_ESW_PMAP_LLLLLL;
+
+ /* Unused HW feature, but still nice to be consistent here...
+ * This is also exported to userspace ('lan' attribute) so it's
+ * conveniently usable to decide which ports go into the wan vlan by
+ * default.
+ */
+ esw_rmw(esw, RT305X_ESW_REG_SGC2,
+ RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S,
+ port_map << RT305X_ESW_SGC2_LAN_PMAP_S);
+
+ /* make the switch leds blink */
+ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++)
+ esw->ports[i].led = 0x05;
+
+ /* Only unmask the port change interrupt */
+ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR);
+}
+
+static irqreturn_t esw_interrupt(int irq, void *_esw)
+{
+ struct rt305x_esw *esw = (struct rt305x_esw *)_esw;
+ u32 status;
+
+ status = esw_r32(esw, RT305X_ESW_REG_ISR);
+ if (status & RT305X_ESW_PORT_ST_CHG) {
+ u32 link = esw_r32(esw, RT305X_ESW_REG_POA);
+
+ link >>= RT305X_ESW_POA_LINK_SHIFT;
+ link &= RT305X_ESW_POA_LINK_MASK;
+ dev_info(esw->dev, "link changed 0x%02X\n", link);
+ }
+ esw_w32(esw, status, RT305X_ESW_REG_ISR);
+
+ return IRQ_HANDLED;
+}
+
+static int esw_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(p, IORESOURCE_MEM, 0);
+ struct device_node *np = pdev->dev.of_node;
+ const __be32 *port_map, *reg_init;
+ struct rt305x_esw *esw;
+ struct resource *irq;
+ int ret;
+
+ esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
+ if (!esw)
+ return -ENOMEM;
+
+ esw->dev = &pdev->dev;
+ esw->irq = irq->start;
+ esw->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!esw->base)
+ return -EADDRNOTAVAIL;
+
+ port_map = of_get_property(np, "mediatek,portmap", NULL);
+ if (port_map)
+ esw->port_map = be32_to_cpu(*port_map);
+
+ reg_init = of_get_property(np, "mediatek,led_polarity", NULL);
+ if (reg_init)
+ esw->reg_led_polarity = be32_to_cpu(*reg_init);
+
+ platform_set_drvdata(pdev, esw);
+
+ spin_lock_init(&esw->reg_rw_lock);
+
+ esw_hw_init(esw);
+
+ ret = devm_request_irq(&pdev->dev, esw->irq, esw_interrupt, 0, "esw",
+ esw);
+
+ if (!ret) {
+ esw_w32(esw, RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_ISR);
+ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR);
+ }
+
+ return ret;
+}
+
+static int esw_remove(struct platform_device *pdev)
+{
+ struct rt305x_esw *esw = platform_get_drvdata(pdev);
+
+ if (esw) {
+ esw_w32(esw, ~0, RT305X_ESW_REG_IMR);
+ platform_set_drvdata(pdev, NULL);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ralink_esw_match[] = {
+ { .compatible = "ralink,rt3050-esw" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ralink_esw_match);
+
+static struct platform_driver esw_driver = {
+ .probe = esw_probe,
+ .remove = esw_remove,
+ .driver = {
+ .name = "rt3050-esw",
+ .owner = THIS_MODULE,
+ .of_match_table = ralink_esw_match,
+ },
+};
+
+int __init mtk_switch_init(void)
+{
+ return platform_driver_register(&esw_driver);
+}
+
+void mtk_switch_exit(void)
+{
+ platform_driver_unregister(&esw_driver);
+}
diff --git a/drivers/net/ethernet/mediatek/esw_rt3050.h b/drivers/net/ethernet/mediatek/esw_rt3050.h
new file mode 100644
index 0000000..455107a
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.h
@@ -0,0 +1,29 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef _RALINK_ESW_RT3052_H__
+#define _RALINK_ESW_RT3052_H__
+
+#ifdef CONFIG_NET_MEDIATEK_ESW_RT3052
+
+int __init mtk_switch_init(void);
+void mtk_switch_exit(void);
+
+#else
+
+static inline int __init mtk_switch_init(void) { return 0; }
+static inline void mtk_switch_exit(void) { }
+
+#endif
+#endif
--
1.7.10.4

View file

@ -0,0 +1,403 @@
From 322a9598692943961791ac6e5a3f385b379dcdc3 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 21:23:18 +0100
Subject: [PATCH 503/513] net-next: mediatek: add switch driver for mt7620
This driver is very basic and only provides basic init and irq support.
Switchdev support for this device will follow.
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/gsw_mt7620.c | 255 ++++++++++++++++++++++++++++
drivers/net/ethernet/mediatek/gsw_mt7620.h | 117 +++++++++++++
2 files changed, 372 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.c
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.c b/drivers/net/ethernet/mediatek/gsw_mt7620.c
new file mode 100644
index 0000000..a37ed1b
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.c
@@ -0,0 +1,255 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
+{
+ iowrite32(val, gsw->base + reg);
+}
+
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
+{
+ return ioread32(gsw->base + reg);
+}
+
+static irqreturn_t gsw_interrupt_mt7620(int irq, void *_priv)
+{
+ struct fe_priv *priv = (struct fe_priv *)_priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
+ u32 status;
+ int i, max = (gsw->port4 == PORT4_EPHY) ? (4) : (3);
+
+ status = mtk_switch_r32(gsw, GSW_REG_ISR);
+ if (status & PORT_IRQ_ST_CHG)
+ for (i = 0; i <= max; i++) {
+ u32 status = mtk_switch_r32(gsw, GSW_REG_PORT_STATUS(i));
+ int link = status & 0x1;
+
+ if (link != priv->link[i])
+ mt7620_print_link_state(priv, i, link,
+ (status >> 2) & 3,
+ (status & 0x2));
+
+ priv->link[i] = link;
+ }
+ mtk_switch_w32(gsw, status, GSW_REG_ISR);
+
+ return IRQ_HANDLED;
+}
+
+static void mt7620_hw_init(struct mt7620_gsw *gsw, struct device_node *np)
+{
+ u32 is_BGA = (rt_sysc_r32(0x0c) >> 16) & 1;
+
+ rt_sysc_w32(rt_sysc_r32(SYSC_REG_CFG1) | BIT(8), SYSC_REG_CFG1);
+ mtk_switch_w32(gsw, mtk_switch_r32(gsw, GSW_REG_CKGCR) & ~(0x3 << 4), GSW_REG_CKGCR);
+
+ if (of_property_read_bool(np, "mediatek,mt7530")) {
+ u32 val;
+
+ /* turn off ephy and set phy base addr to 12 */
+ mtk_switch_w32(gsw, mtk_switch_r32(gsw, GSW_REG_GPC1) |
+ (0x1f << 24) | (0xc << 16),
+ GSW_REG_GPC1);
+
+ /* set MT7530 central align */
+ val = mt7530_mdio_r32(gsw, 0x7830);
+ val &= ~BIT(0);
+ val |= BIT(1);
+ mt7530_mdio_w32(gsw, 0x7830, val);
+
+ val = mt7530_mdio_r32(gsw, 0x7a40);
+ val &= ~BIT(30);
+ mt7530_mdio_w32(gsw, 0x7a40, val);
+
+ mt7530_mdio_w32(gsw, 0x7a78, 0x855);
+ } else {
+ /* global page 4 */
+ _mt7620_mii_write(gsw, 1, 31, 0x4000);
+
+ _mt7620_mii_write(gsw, 1, 17, 0x7444);
+ if (is_BGA)
+ _mt7620_mii_write(gsw, 1, 19, 0x0114);
+ else
+ _mt7620_mii_write(gsw, 1, 19, 0x0117);
+
+ _mt7620_mii_write(gsw, 1, 22, 0x10cf);
+ _mt7620_mii_write(gsw, 1, 25, 0x6212);
+ _mt7620_mii_write(gsw, 1, 26, 0x0777);
+ _mt7620_mii_write(gsw, 1, 29, 0x4000);
+ _mt7620_mii_write(gsw, 1, 28, 0xc077);
+ _mt7620_mii_write(gsw, 1, 24, 0x0000);
+
+ /* global page 3 */
+ _mt7620_mii_write(gsw, 1, 31, 0x3000);
+ _mt7620_mii_write(gsw, 1, 17, 0x4838);
+
+ /* global page 2 */
+ _mt7620_mii_write(gsw, 1, 31, 0x2000);
+ if (is_BGA) {
+ _mt7620_mii_write(gsw, 1, 21, 0x0515);
+ _mt7620_mii_write(gsw, 1, 22, 0x0053);
+ _mt7620_mii_write(gsw, 1, 23, 0x00bf);
+ _mt7620_mii_write(gsw, 1, 24, 0x0aaf);
+ _mt7620_mii_write(gsw, 1, 25, 0x0fad);
+ _mt7620_mii_write(gsw, 1, 26, 0x0fc1);
+ } else {
+ _mt7620_mii_write(gsw, 1, 21, 0x0517);
+ _mt7620_mii_write(gsw, 1, 22, 0x0fd2);
+ _mt7620_mii_write(gsw, 1, 23, 0x00bf);
+ _mt7620_mii_write(gsw, 1, 24, 0x0aab);
+ _mt7620_mii_write(gsw, 1, 25, 0x00ae);
+ _mt7620_mii_write(gsw, 1, 26, 0x0fff);
+ }
+ /* global page 1 */
+ _mt7620_mii_write(gsw, 1, 31, 0x1000);
+ _mt7620_mii_write(gsw, 1, 17, 0xe7f8);
+ }
+
+ /* global page 0 */
+ _mt7620_mii_write(gsw, 1, 31, 0x8000);
+ _mt7620_mii_write(gsw, 0, 30, 0xa000);
+ _mt7620_mii_write(gsw, 1, 30, 0xa000);
+ _mt7620_mii_write(gsw, 2, 30, 0xa000);
+ _mt7620_mii_write(gsw, 3, 30, 0xa000);
+
+ _mt7620_mii_write(gsw, 0, 4, 0x05e1);
+ _mt7620_mii_write(gsw, 1, 4, 0x05e1);
+ _mt7620_mii_write(gsw, 2, 4, 0x05e1);
+ _mt7620_mii_write(gsw, 3, 4, 0x05e1);
+
+ /* global page 2 */
+ _mt7620_mii_write(gsw, 1, 31, 0xa000);
+ _mt7620_mii_write(gsw, 0, 16, 0x1111);
+ _mt7620_mii_write(gsw, 1, 16, 0x1010);
+ _mt7620_mii_write(gsw, 2, 16, 0x1515);
+ _mt7620_mii_write(gsw, 3, 16, 0x0f0f);
+
+ /* CPU Port6 Force Link 1G, FC ON */
+ mtk_switch_w32(gsw, 0x5e33b, GSW_REG_PORT_PMCR(6));
+
+ /* Set Port 6 as CPU Port */
+ mtk_switch_w32(gsw, 0x7f7f7fe0, 0x0010);
+
+ /* setup port 4 */
+ if (gsw->port4 == PORT4_EPHY) {
+ u32 val = rt_sysc_r32(SYSC_REG_CFG1);
+
+ val |= 3 << 14;
+ rt_sysc_w32(val, SYSC_REG_CFG1);
+ _mt7620_mii_write(gsw, 4, 30, 0xa000);
+ _mt7620_mii_write(gsw, 4, 4, 0x05e1);
+ _mt7620_mii_write(gsw, 4, 16, 0x1313);
+ pr_info("gsw: setting port4 to ephy mode\n");
+ }
+}
+
+static const struct of_device_id mediatek_gsw_match[] = {
+ { .compatible = "mediatek,mt7620-gsw" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
+
+int mtk_gsw_init(struct fe_priv *priv)
+{
+ struct device_node *np = priv->switch_np;
+ struct platform_device *pdev = of_find_device_by_node(np);
+ struct mt7620_gsw *gsw;
+
+ if (!pdev)
+ return -ENODEV;
+
+ if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
+ return -EINVAL;
+
+ gsw = platform_get_drvdata(pdev);
+ priv->soc->swpriv = gsw;
+
+ mt7620_hw_init(gsw, np);
+
+ if (gsw->irq) {
+ request_irq(gsw->irq, gsw_interrupt_mt7620, 0,
+ "gsw", priv);
+ mtk_switch_w32(gsw, ~PORT_IRQ_ST_CHG, GSW_REG_IMR);
+ }
+
+ return 0;
+}
+
+static int mt7620_gsw_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ const char *port4 = NULL;
+ struct mt7620_gsw *gsw;
+ struct device_node *np;
+
+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
+ if (!gsw)
+ return -ENOMEM;
+
+ gsw->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!gsw->base)
+ return -EADDRNOTAVAIL;
+
+ gsw->dev = &pdev->dev;
+
+ of_property_read_string(np, "mediatek,port4", &port4);
+ if (port4 && !strcmp(port4, "ephy"))
+ gsw->port4 = PORT4_EPHY;
+ else if (port4 && !strcmp(port4, "gmac"))
+ gsw->port4 = PORT4_EXT;
+ else
+ gsw->port4 = PORT4_EPHY;
+
+ gsw->irq = irq_of_parse_and_map(np, 0);
+
+ platform_set_drvdata(pdev, gsw);
+
+ return 0;
+}
+
+static int mt7620_gsw_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver gsw_driver = {
+ .probe = mt7620_gsw_probe,
+ .remove = mt7620_gsw_remove,
+ .driver = {
+ .name = "mt7620-gsw",
+ .owner = THIS_MODULE,
+ .of_match_table = mediatek_gsw_match,
+ },
+};
+
+module_platform_driver(gsw_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7620 SoC");
+MODULE_VERSION(MTK_FE_DRV_VERSION);
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.h b/drivers/net/ethernet/mediatek/gsw_mt7620.h
new file mode 100644
index 0000000..0d6ee84
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
@@ -0,0 +1,117 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef _RALINK_GSW_MT7620_H__
+#define _RALINK_GSW_MT7620_H__
+
+#define GSW_REG_PHY_TIMEOUT (5 * HZ)
+
+#ifdef CONFIG_SOC_MT7621
+#define MT7620A_GSW_REG_PIAC 0x0004
+#else
+#define MT7620A_GSW_REG_PIAC 0x7004
+#endif
+
+#define GSW_NUM_VLANS 16
+#define GSW_NUM_VIDS 4096
+#define GSW_NUM_PORTS 7
+#define GSW_PORT6 6
+
+#define GSW_MDIO_ACCESS BIT(31)
+#define GSW_MDIO_READ BIT(19)
+#define GSW_MDIO_WRITE BIT(18)
+#define GSW_MDIO_START BIT(16)
+#define GSW_MDIO_ADDR_SHIFT 20
+#define GSW_MDIO_REG_SHIFT 25
+
+#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100))
+#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100))
+#define GSW_REG_SMACCR0 0x3fE4
+#define GSW_REG_SMACCR1 0x3fE8
+#define GSW_REG_CKGCR 0x3ff0
+
+#define GSW_REG_IMR 0x7008
+#define GSW_REG_ISR 0x700c
+#define GSW_REG_GPC1 0x7014
+
+#define SYSC_REG_CHIP_REV_ID 0x0c
+#define SYSC_REG_CFG1 0x14
+#define RST_CTRL_MCM BIT(2)
+#define SYSC_PAD_RGMII2_MDIO 0x58
+#define SYSC_GPIO_MODE 0x60
+
+#define PORT_IRQ_ST_CHG 0x7f
+
+#ifdef CONFIG_SOC_MT7621
+#define ESW_PHY_POLLING 0x0000
+#else
+#define ESW_PHY_POLLING 0x7000
+#endif
+
+#define PMCR_IPG BIT(18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACKOFF BIT(9)
+#define PMCR_BACKPRES BIT(8)
+#define PMCR_RX_FC BIT(5)
+#define PMCR_TX_FC BIT(4)
+#define PMCR_SPEED(_x) (_x << 2)
+#define PMCR_DUPLEX BIT(1)
+#define PMCR_LINK BIT(0)
+
+#define PHY_AN_EN BIT(31)
+#define PHY_PRE_EN BIT(30)
+#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24)
+
+enum {
+ /* Global attributes. */
+ GSW_ATTR_ENABLE_VLAN,
+ /* Port attributes. */
+ GSW_ATTR_PORT_UNTAG,
+};
+
+enum {
+ PORT4_EPHY = 0,
+ PORT4_EXT,
+};
+
+struct mt7620_gsw {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ int port4;
+ unsigned long int autopoll;
+};
+
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
+int mtk_gsw_init(struct fe_priv *priv);
+
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
+void mt7620_mdio_link_adjust(struct fe_priv *priv, int port);
+int mt7620_has_carrier(struct fe_priv *priv);
+void mt7620_print_link_state(struct fe_priv *priv, int port, int link,
+ int speed, int duplex);
+
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
+
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
+ u32 phy_register, u32 write_data);
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg);
+
+#endif
--
1.7.10.4

View file

@ -0,0 +1,307 @@
From 9fc19d5f7354709298dcb15b3a4c7cd9a18acebf Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 21:24:46 +0100
Subject: [PATCH 504/513] net-next: mediatek: add switch driver for mt7621
This driver is very basic and only provides basic init and irq support.
Switchdev support for this device will follow.
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/gsw_mt7621.c | 284 ++++++++++++++++++++++++++++
1 file changed, 284 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7621.c
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7621.c b/drivers/net/ethernet/mediatek/gsw_mt7621.c
new file mode 100644
index 0000000..500841f
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/gsw_mt7621.c
@@ -0,0 +1,284 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
+{
+ iowrite32(val, gsw->base + reg);
+}
+
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
+{
+ return ioread32(gsw->base + reg);
+}
+
+static irqreturn_t gsw_interrupt_mt7621(int irq, void *_priv)
+{
+ struct fe_priv *priv = (struct fe_priv *)_priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
+ u32 reg, i;
+
+ reg = mt7530_mdio_r32(gsw, 0x700c);
+
+ for (i = 0; i < 5; i++)
+ if (reg & BIT(i)) {
+ unsigned int link;
+
+ link = mt7530_mdio_r32(gsw,
+ 0x3008 + (i * 0x100)) & 0x1;
+
+ if (link != priv->link[i]) {
+ priv->link[i] = link;
+ if (link)
+ netdev_info(priv->netdev,
+ "port %d link up\n", i);
+ else
+ netdev_info(priv->netdev,
+ "port %d link down\n", i);
+ }
+ }
+
+ mt7530_mdio_w32(gsw, 0x700c, 0x1f);
+
+ return IRQ_HANDLED;
+}
+
+static void mt7621_hw_init(struct mt7620_gsw *gsw, struct device_node *np)
+{
+ u32 i;
+ u32 val;
+
+ /* wardware reset the switch */
+ fe_reset(RST_CTRL_MCM);
+ mdelay(10);
+
+ /* reduce RGMII2 PAD driving strength */
+ rt_sysc_m32(3 << 4, 0, SYSC_PAD_RGMII2_MDIO);
+
+ /* gpio mux - RGMII1=Normal mode */
+ rt_sysc_m32(BIT(14), 0, SYSC_GPIO_MODE);
+
+ /* set GMAC1 RGMII mode */
+ rt_sysc_m32(3 << 12, 0, SYSC_REG_CFG1);
+
+ /* enable MDIO to control MT7530 */
+ rt_sysc_m32(3 << 12, 0, SYSC_GPIO_MODE);
+
+ /* turn off all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mt7620_mii_read(gsw, i, 0x0);
+ val |= BIT(11);
+ _mt7620_mii_write(gsw, i, 0x0, val);
+ }
+
+ /* reset the switch */
+ mt7530_mdio_w32(gsw, 0x7000, 0x3);
+ usleep_range(10, 20);
+
+ if ((rt_sysc_r32(SYSC_REG_CHIP_REV_ID) & 0xFFFF) == 0x0101) {
+ /* (GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536) */
+ mtk_switch_w32(gsw, 0x2105e30b, 0x100);
+ mt7530_mdio_w32(gsw, 0x3600, 0x5e30b);
+ } else {
+ /* (GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536) */
+ mtk_switch_w32(gsw, 0x2105e33b, 0x100);
+ mt7530_mdio_w32(gsw, 0x3600, 0x5e33b);
+ }
+
+ /* (GE2, Link down) */
+ mtk_switch_w32(gsw, 0x8000, 0x200);
+
+ /* Enable Port 6, P5 as GMAC5, P5 disable */
+ val = mt7530_mdio_r32(gsw, 0x7804);
+ val &= ~BIT(8);
+ val |= BIT(6) | BIT(13) | BIT(16);
+ mt7530_mdio_w32(gsw, 0x7804, val);
+
+ val = rt_sysc_r32(0x10);
+ val = (val >> 6) & 0x7;
+ if (val >= 6) {
+ /* 25Mhz Xtal - do nothing */
+ } else if (val >= 3) {
+ /* 40Mhz */
+
+ /* disable MT7530 core clock */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x0);
+
+ /* disable MT7530 PLL */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x2020);
+
+ /* for MT7530 core clock = 500Mhz */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40e);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x119);
+
+ /* enable MT7530 PLL */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x2820);
+
+ usleep_range(20, 40);
+
+ /* enable MT7530 core clock */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ } else {
+ /* 20Mhz Xtal - TODO */
+ }
+
+ /* RGMII */
+ _mt7620_mii_write(gsw, 0, 14, 0x1);
+
+ /* set MT7530 central align */
+ val = mt7530_mdio_r32(gsw, 0x7830);
+ val &= ~BIT(0);
+ val |= BIT(1);
+ mt7530_mdio_w32(gsw, 0x7830, val);
+ val = mt7530_mdio_r32(gsw, 0x7a40);
+ val &= ~BIT(30);
+ mt7530_mdio_w32(gsw, 0x7a40, val);
+ mt7530_mdio_w32(gsw, 0x7a78, 0x855);
+
+ /* delay setting for 10/1000M */
+ mt7530_mdio_w32(gsw, 0x7b00, 0x102);
+ mt7530_mdio_w32(gsw, 0x7b04, 0x14);
+
+ /* lower Tx Driving*/
+ mt7530_mdio_w32(gsw, 0x7a54, 0x44);
+ mt7530_mdio_w32(gsw, 0x7a5c, 0x44);
+ mt7530_mdio_w32(gsw, 0x7a64, 0x44);
+ mt7530_mdio_w32(gsw, 0x7a6c, 0x44);
+ mt7530_mdio_w32(gsw, 0x7a74, 0x44);
+ mt7530_mdio_w32(gsw, 0x7a7c, 0x44);
+
+ /* turn on all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mt7620_mii_read(gsw, i, 0);
+ val &= ~BIT(11);
+ _mt7620_mii_write(gsw, i, 0, val);
+ }
+
+ /* enable irq */
+ val = mt7530_mdio_r32(gsw, 0x7808);
+ val |= 3 << 16;
+ mt7530_mdio_w32(gsw, 0x7808, val);
+}
+
+static const struct of_device_id mediatek_gsw_match[] = {
+ { .compatible = "mediatek,mt7621-gsw" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
+
+int mtk_gsw_init(struct fe_priv *priv)
+{
+ struct device_node *np = priv->switch_np;
+ struct platform_device *pdev = of_find_device_by_node(np);
+ struct mt7620_gsw *gsw;
+
+ if (!pdev)
+ return -ENODEV;
+
+ if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
+ return -EINVAL;
+
+ gsw = platform_get_drvdata(pdev);
+ priv->soc->swpriv = gsw;
+
+ mt7621_hw_init(gsw, np);
+
+ if (gsw->irq) {
+ request_irq(gsw->irq, gsw_interrupt_mt7621, 0,
+ "gsw", priv);
+ mt7530_mdio_w32(gsw, 0x7008, 0x1f);
+ }
+
+ return 0;
+}
+
+static int mt7621_gsw_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ const char *port4 = NULL;
+ struct mt7620_gsw *gsw;
+ struct device_node *np;
+
+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
+ if (!gsw)
+ return -ENOMEM;
+
+ gsw->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!gsw->base)
+ return -EADDRNOTAVAIL;
+
+ gsw->dev = &pdev->dev;
+
+ of_property_read_string(np, "mediatek,port4", &port4);
+ if (port4 && !strcmp(port4, "ephy"))
+ gsw->port4 = PORT4_EPHY;
+ else if (port4 && !strcmp(port4, "gmac"))
+ gsw->port4 = PORT4_EXT;
+ else
+ gsw->port4 = PORT4_EPHY;
+
+ gsw->irq = irq_of_parse_and_map(np, 0);
+
+ platform_set_drvdata(pdev, gsw);
+
+ return 0;
+}
+
+static int mt7621_gsw_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver gsw_driver = {
+ .probe = mt7621_gsw_probe,
+ .remove = mt7621_gsw_remove,
+ .driver = {
+ .name = "mt7621-gsw",
+ .owner = THIS_MODULE,
+ .of_match_table = mediatek_gsw_match,
+ },
+};
+
+module_platform_driver(gsw_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7621 SoC");
+MODULE_VERSION(MTK_FE_DRV_VERSION);
--
1.7.10.4

View file

@ -0,0 +1,363 @@
From f8c8f4bd2a13e0cc060c93812377373d436f7f02 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 18 Nov 2015 03:13:05 +0100
Subject: [PATCH 505/513] net-next: mediatek: add support for rt2880
rt2880 is the oldest SoC with this core. It has a single gBit port that will
normally be attached to an external phy of switch. The patch also adds the
code required to drive the mdio bus.
Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Michael Lee <igvtee@gmail.com>
---
drivers/net/ethernet/mediatek/mdio_rt2880.c | 222 +++++++++++++++++++++++++++
drivers/net/ethernet/mediatek/mdio_rt2880.h | 23 +++
drivers/net/ethernet/mediatek/soc_rt2880.c | 76 +++++++++
3 files changed, 321 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/mdio_rt2880.c
create mode 100644 drivers/net/ethernet/mediatek/mdio_rt2880.h
create mode 100644 drivers/net/ethernet/mediatek/soc_rt2880.c
diff --git a/drivers/net/ethernet/mediatek/mdio_rt2880.c b/drivers/net/ethernet/mediatek/mdio_rt2880.c
new file mode 100644
index 0000000..eb9df6e
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mdio_rt2880.c
@@ -0,0 +1,222 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio_rt2880.h"
+#include "mdio.h"
+
+#define FE_MDIO_RETRY 1000
+
+static unsigned char *rt2880_speed_str(struct fe_priv *priv)
+{
+ switch (priv->phy->speed[0]) {
+ case SPEED_1000:
+ return "1000";
+ case SPEED_100:
+ return "100";
+ case SPEED_10:
+ return "10";
+ }
+
+ return "?";
+}
+
+void rt2880_mdio_link_adjust(struct fe_priv *priv, int port)
+{
+ u32 mdio_cfg;
+
+ if (!priv->link[0]) {
+ netif_carrier_off(priv->netdev);
+ netdev_info(priv->netdev, "link down\n");
+ return;
+ }
+
+ mdio_cfg = FE_MDIO_CFG_TX_CLK_SKEW_200 |
+ FE_MDIO_CFG_RX_CLK_SKEW_200 |
+ FE_MDIO_CFG_GP1_FRC_EN;
+
+ if (priv->phy->duplex[0] == DUPLEX_FULL)
+ mdio_cfg |= FE_MDIO_CFG_GP1_DUPLEX;
+
+ if (priv->phy->tx_fc[0])
+ mdio_cfg |= FE_MDIO_CFG_GP1_FC_TX;
+
+ if (priv->phy->rx_fc[0])
+ mdio_cfg |= FE_MDIO_CFG_GP1_FC_RX;
+
+ switch (priv->phy->speed[0]) {
+ case SPEED_10:
+ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_10;
+ break;
+ case SPEED_100:
+ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_100;
+ break;
+ case SPEED_1000:
+ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_1000;
+ break;
+ default:
+ BUG();
+ }
+
+ fe_w32(mdio_cfg, FE_MDIO_CFG);
+
+ netif_carrier_on(priv->netdev);
+ netdev_info(priv->netdev, "link up (%sMbps/%s duplex)\n",
+ rt2880_speed_str(priv),
+ (priv->phy->duplex[0] == DUPLEX_FULL) ? "Full" : "Half");
+}
+
+static int rt2880_mdio_wait_ready(struct fe_priv *priv)
+{
+ int retries;
+
+ retries = FE_MDIO_RETRY;
+ while (1) {
+ u32 t;
+
+ t = fe_r32(FE_MDIO_ACCESS);
+ if ((t & BIT(31)) == 0)
+ return 0;
+
+ if (retries-- == 0)
+ break;
+
+ udelay(1);
+ }
+
+ dev_err(priv->device, "MDIO operation timed out\n");
+ return -ETIMEDOUT;
+}
+
+int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+{
+ struct fe_priv *priv = bus->priv;
+ int err;
+ u32 t;
+
+ err = rt2880_mdio_wait_ready(priv);
+ if (err)
+ return 0xffff;
+
+ t = (phy_addr << 24) | (phy_reg << 16);
+ fe_w32(t, FE_MDIO_ACCESS);
+ t |= BIT(31);
+ fe_w32(t, FE_MDIO_ACCESS);
+
+ err = rt2880_mdio_wait_ready(priv);
+ if (err)
+ return 0xffff;
+
+ pr_debug("%s: addr=%04x, reg=%04x, value=%04x\n", __func__,
+ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff);
+
+ return fe_r32(FE_MDIO_ACCESS) & 0xffff;
+}
+
+int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
+{
+ struct fe_priv *priv = bus->priv;
+ int err;
+ u32 t;
+
+ pr_debug("%s: addr=%04x, reg=%04x, value=%04x\n", __func__,
+ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff);
+
+ err = rt2880_mdio_wait_ready(priv);
+ if (err)
+ return err;
+
+ t = (1 << 30) | (phy_addr << 24) | (phy_reg << 16) | val;
+ fe_w32(t, FE_MDIO_ACCESS);
+ t |= BIT(31);
+ fe_w32(t, FE_MDIO_ACCESS);
+
+ return rt2880_mdio_wait_ready(priv);
+}
+
+void rt2880_port_init(struct fe_priv *priv, struct device_node *np)
+{
+ const __be32 *id = of_get_property(np, "reg", NULL);
+ const __be32 *link;
+ int size;
+ int phy_mode;
+
+ if (!id || (be32_to_cpu(*id) != 0)) {
+ pr_err("%s: invalid port id\n", np->name);
+ return;
+ }
+
+ priv->phy->phy_fixed[0] = of_get_property(np,
+ "mediatek,fixed-link", &size);
+ if (priv->phy->phy_fixed[0] &&
+ (size != (4 * sizeof(*priv->phy->phy_fixed[0])))) {
+ pr_err("%s: invalid fixed link property\n", np->name);
+ priv->phy->phy_fixed[0] = NULL;
+ return;
+ }
+
+ phy_mode = of_get_phy_mode(np);
+ switch (phy_mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ break;
+ default:
+ if (!priv->phy->phy_fixed[0])
+ dev_err(priv->device, "port %d - invalid phy mode\n",
+ priv->phy->speed[0]);
+ break;
+ }
+
+ priv->phy->phy_node[0] = of_parse_phandle(np, "phy-handle", 0);
+ if (!priv->phy->phy_node[0] && !priv->phy->phy_fixed[0])
+ return;
+
+ if (priv->phy->phy_fixed[0]) {
+ link = priv->phy->phy_fixed[0];
+ priv->phy->speed[0] = be32_to_cpup(link++);
+ priv->phy->duplex[0] = be32_to_cpup(link++);
+ priv->phy->tx_fc[0] = be32_to_cpup(link++);
+ priv->phy->rx_fc[0] = be32_to_cpup(link++);
+
+ priv->link[0] = 1;
+ switch (priv->phy->speed[0]) {
+ case SPEED_10:
+ break;
+ case SPEED_100:
+ break;
+ case SPEED_1000:
+ break;
+ default:
+ dev_err(priv->device, "invalid link speed: %d\n",
+ priv->phy->speed[0]);
+ priv->phy->phy_fixed[0] = 0;
+ return;
+ }
+ dev_info(priv->device, "using fixed link parameters\n");
+ rt2880_mdio_link_adjust(priv, 0);
+ return;
+ }
+
+ if (priv->phy->phy_node[0] && priv->mii_bus->phy_map[0])
+ fe_connect_phy_node(priv, priv->phy->phy_node[0]);
+}
diff --git a/drivers/net/ethernet/mediatek/mdio_rt2880.h b/drivers/net/ethernet/mediatek/mdio_rt2880.h
new file mode 100644
index 0000000..6884894
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mdio_rt2880.h
@@ -0,0 +1,23 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef _RALINK_MDIO_RT2880_H__
+#define _RALINK_MDIO_RT2880_H__
+
+void rt2880_mdio_link_adjust(struct fe_priv *priv, int port);
+int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
+int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
+void rt2880_port_init(struct fe_priv *priv, struct device_node *np);
+
+#endif
diff --git a/drivers/net/ethernet/mediatek/soc_rt2880.c b/drivers/net/ethernet/mediatek/soc_rt2880.c
new file mode 100644
index 0000000..0792dab
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/soc_rt2880.c
@@ -0,0 +1,76 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio_rt2880.h"
+
+#define RT2880_RESET_FE BIT(18)
+
+static void rt2880_init_data(struct fe_soc_data *data,
+ struct net_device *netdev)
+{
+ struct fe_priv *priv = netdev_priv(netdev);
+
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_PADDING_BUG |
+ FE_FLAG_JUMBO_FRAME | FE_FLAG_CALIBRATE_CLK;
+ netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_TX;
+ /* this should work according to the datasheet but actually does not*/
+ /* netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; */
+}
+
+void rt2880_fe_reset(void)
+{
+ fe_reset(RT2880_RESET_FE);
+}
+
+static int rt2880_fwd_config(struct fe_priv *priv)
+{
+ int ret;
+
+ ret = fe_set_clock_cycle(priv);
+ if (ret)
+ return ret;
+
+ fe_fwd_config(priv);
+ fe_w32(FE_PSE_FQFC_CFG_INIT, FE_PSE_FQ_CFG);
+ fe_csum_config(priv);
+
+ return ret;
+}
+
+struct fe_soc_data rt2880_data = {
+ .init_data = rt2880_init_data,
+ .reset_fe = rt2880_fe_reset,
+ .fwd_config = rt2880_fwd_config,
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
+ .checksum_bit = RX_DMA_L4VALID,
+ .rx_int = FE_RX_DONE_INT,
+ .tx_int = FE_TX_DONE_INT,
+ .status_int = FE_CNT_GDM_AF,
+ .mdio_read = rt2880_mdio_read,
+ .mdio_write = rt2880_mdio_write,
+ .mdio_adjust_link = rt2880_mdio_link_adjust,
+ .port_init = rt2880_port_init,
+};
+
+const struct of_device_id of_fe_match[] = {
+ { .compatible = "ralink,rt2880-eth", .data = &rt2880_data },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_fe_match);
--
1.7.10.4

View file

@ -0,0 +1,229 @@
From a3555658ce5dd97df3dc225289b92800da9d38ba Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 21:28:51 +0100
Subject: [PATCH 506/513] net-next: mediatek: add support for rt3050
Add support for SoCs from the rt3050 family. This include rt3050, rt3052,
rt3352 and rt5350. These all have a builtin 5 port 100mbit switch. This patch
includes rudimentary code to power up the switch. There are a lot of magic
values that get written to the switch and the internal phys. These values
come straight from the SDK driver and we do not know the meaning of most of
them.
Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Michael Lee <igvtee@gmail.com>
---
drivers/net/ethernet/mediatek/esw_rt3050.c | 18 +---
drivers/net/ethernet/mediatek/soc_rt3050.c | 158 ++++++++++++++++++++++++++++
2 files changed, 159 insertions(+), 17 deletions(-)
create mode 100644 drivers/net/ethernet/mediatek/soc_rt3050.c
diff --git a/drivers/net/ethernet/mediatek/esw_rt3050.c b/drivers/net/ethernet/mediatek/esw_rt3050.c
index f07f4a5..670ae16 100644
--- a/drivers/net/ethernet/mediatek/esw_rt3050.c
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.c
@@ -14,27 +14,11 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/clk.h>
-#include <linux/of_net.h>
-#include <linux/of_mdio.h>
-
#include <asm/mach-ralink/ralink_regs.h>
#include "mtk_eth_soc.h"
-#include <linux/ioport.h>
-#include <linux/mii.h>
-
-#include <ralink_regs.h>
-
/* HW limitations for this switch:
* - No large frame support (PKT_MAX_LEN at most 1536)
* - Can't have untagged vlan and tagged vlan on one port at the same time,
@@ -559,7 +543,7 @@ static irqreturn_t esw_interrupt(int irq, void *_esw)
static int esw_probe(struct platform_device *pdev)
{
- struct resource *res = platform_get_resource(p, IORESOURCE_MEM, 0);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct device_node *np = pdev->dev.of_node;
const __be32 *port_map, *reg_init;
struct rt305x_esw *esw;
diff --git a/drivers/net/ethernet/mediatek/soc_rt3050.c b/drivers/net/ethernet/mediatek/soc_rt3050.c
new file mode 100644
index 0000000..228c94f
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/soc_rt3050.c
@@ -0,0 +1,158 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio_rt2880.h"
+
+#define RT305X_RESET_FE BIT(21)
+#define RT305X_RESET_ESW BIT(23)
+
+static const u16 rt5350_reg_table[FE_REG_COUNT] = {
+ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
+ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
+ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
+ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
+ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
+ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+ [FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
+ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
+ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
+ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+ [FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
+ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
+ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
+ [FE_REG_FE_RST_GL] = 0,
+ [FE_REG_FE_DMA_VID_BASE] = 0,
+};
+
+static void rt305x_init_data(struct fe_soc_data *data,
+ struct net_device *netdev)
+{
+ struct fe_priv *priv = netdev_priv(netdev);
+
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_PADDING_BUG |
+ FE_FLAG_CALIBRATE_CLK | FE_FLAG_HAS_SWITCH;
+ netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX;
+}
+
+static int rt3050_fwd_config(struct fe_priv *priv)
+{
+ int ret;
+
+ if (ralink_soc != RT305X_SOC_RT3052) {
+ ret = fe_set_clock_cycle(priv);
+ if (ret)
+ return ret;
+ }
+
+ fe_fwd_config(priv);
+ if (ralink_soc != RT305X_SOC_RT3352)
+ fe_w32(FE_PSE_FQFC_CFG_INIT, FE_PSE_FQ_CFG);
+ fe_csum_config(priv);
+
+ return 0;
+}
+
+static void rt305x_fe_reset(void)
+{
+ fe_reset(RT305X_RESET_FE);
+}
+
+static void rt5350_init_data(struct fe_soc_data *data,
+ struct net_device *netdev)
+{
+ struct fe_priv *priv = netdev_priv(netdev);
+
+ priv->flags = FE_FLAG_HAS_SWITCH;
+ netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM;
+}
+
+static void rt5350_set_mac(struct fe_priv *priv, unsigned char *mac)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->page_lock, flags);
+ fe_w32((mac[0] << 8) | mac[1], RT5350_SDM_MAC_ADRH);
+ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5],
+ RT5350_SDM_MAC_ADRL);
+ spin_unlock_irqrestore(&priv->page_lock, flags);
+}
+
+static void rt5350_rxcsum_config(bool enable)
+{
+ if (enable)
+ fe_w32(fe_r32(RT5350_SDM_CFG) | (RT5350_SDM_ICS_EN |
+ RT5350_SDM_TCS_EN | RT5350_SDM_UCS_EN),
+ RT5350_SDM_CFG);
+ else
+ fe_w32(fe_r32(RT5350_SDM_CFG) & ~(RT5350_SDM_ICS_EN |
+ RT5350_SDM_TCS_EN | RT5350_SDM_UCS_EN),
+ RT5350_SDM_CFG);
+}
+
+static int rt5350_fwd_config(struct fe_priv *priv)
+{
+ struct net_device *dev = priv_netdev(priv);
+
+ rt5350_rxcsum_config((dev->features & NETIF_F_RXCSUM));
+
+ return 0;
+}
+
+static void rt5350_tx_dma(struct fe_tx_dma *txd)
+{
+ txd->txd4 = 0;
+}
+
+static void rt5350_fe_reset(void)
+{
+ fe_reset(RT305X_RESET_FE | RT305X_RESET_ESW);
+}
+
+static struct fe_soc_data rt3050_data = {
+ .init_data = rt305x_init_data,
+ .reset_fe = rt305x_fe_reset,
+ .fwd_config = rt3050_fwd_config,
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
+ .checksum_bit = RX_DMA_L4VALID,
+ .rx_int = FE_RX_DONE_INT,
+ .tx_int = FE_TX_DONE_INT,
+ .status_int = FE_CNT_GDM_AF,
+};
+
+static struct fe_soc_data rt5350_data = {
+ .init_data = rt5350_init_data,
+ .reg_table = rt5350_reg_table,
+ .reset_fe = rt5350_fe_reset,
+ .set_mac = rt5350_set_mac,
+ .fwd_config = rt5350_fwd_config,
+ .tx_dma = rt5350_tx_dma,
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
+ .checksum_bit = RX_DMA_L4VALID,
+ .rx_int = RT5350_RX_DONE_INT,
+ .tx_int = RT5350_TX_DONE_INT,
+};
+
+const struct of_device_id of_fe_match[] = {
+ { .compatible = "ralink,rt3050-eth", .data = &rt3050_data },
+ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_fe_match);
--
1.7.10.4

View file

@ -0,0 +1,100 @@
From 5ad283c69029a519681ed453e7f7ddf250c10559 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 18 Nov 2015 03:51:24 +0100
Subject: [PATCH 507/513] net-next: mediatek: add support for rt3883
Add support for rt3883 and its smaller version rt3662. They both have a single
gBit port that will normally be attached to an external phy of switch.
Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Michael Lee <igvtee@gmail.com>
---
drivers/net/ethernet/mediatek/soc_rt3883.c | 75 ++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/soc_rt3883.c
diff --git a/drivers/net/ethernet/mediatek/soc_rt3883.c b/drivers/net/ethernet/mediatek/soc_rt3883.c
new file mode 100644
index 0000000..7f34d4d
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/soc_rt3883.c
@@ -0,0 +1,75 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio_rt2880.h"
+
+#define RT3883_RSTCTRL_FE BIT(21)
+
+static void rt3883_fe_reset(void)
+{
+ fe_reset(RT3883_RSTCTRL_FE);
+}
+
+static int rt3883_fwd_config(struct fe_priv *priv)
+{
+ int ret;
+
+ ret = fe_set_clock_cycle(priv);
+ if (ret)
+ return ret;
+
+ fe_fwd_config(priv);
+ fe_w32(FE_PSE_FQFC_CFG_256Q, FE_PSE_FQ_CFG);
+ fe_csum_config(priv);
+
+ return ret;
+}
+
+static void rt3883_init_data(struct fe_soc_data *data,
+ struct net_device *netdev)
+{
+ struct fe_priv *priv = netdev_priv(netdev);
+
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_PADDING_BUG |
+ FE_FLAG_JUMBO_FRAME | FE_FLAG_CALIBRATE_CLK;
+ netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX;
+}
+
+static struct fe_soc_data rt3883_data = {
+ .init_data = rt3883_init_data,
+ .reset_fe = rt3883_fe_reset,
+ .fwd_config = rt3883_fwd_config,
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
+ .rx_int = FE_RX_DONE_INT,
+ .tx_int = FE_TX_DONE_INT,
+ .status_int = FE_CNT_GDM_AF,
+ .checksum_bit = RX_DMA_L4VALID,
+ .mdio_read = rt2880_mdio_read,
+ .mdio_write = rt2880_mdio_write,
+ .mdio_adjust_link = rt2880_mdio_link_adjust,
+ .port_init = rt2880_port_init,
+};
+
+const struct of_device_id of_fe_match[] = {
+ { .compatible = "ralink,rt3883-eth", .data = &rt3883_data },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_fe_match);
--
1.7.10.4

View file

@ -0,0 +1,528 @@
From 1efca7b539a91c49ab1d6484ec3a69c48fa6062b Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 21:25:35 +0100
Subject: [PATCH 508/513] net-next: mediatek: add support for mt7620
Add support for SoCs from the mt7620 family. This include mt7620 and mt7621.
These all have one dedicated external gbit port and a builtin 5 port 100mbit
switch. Additionally one of the 5 switch ports can be changed to become an
additional gbit port that we can attach a phy to. This patch includes
rudimentary code to power up the switch. There are a lot of magic values
that get written to the switch and the internal phys. These values come
straight from the SDK driver.
Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Michael Lee <igvtee@gmail.com>
---
drivers/net/ethernet/mediatek/mdio_mt7620.c | 156 +++++++++++++
drivers/net/ethernet/mediatek/soc_mt7620.c | 334 +++++++++++++++++++++++++++
2 files changed, 490 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/mdio_mt7620.c
create mode 100644 drivers/net/ethernet/mediatek/soc_mt7620.c
diff --git a/drivers/net/ethernet/mediatek/mdio_mt7620.c b/drivers/net/ethernet/mediatek/mdio_mt7620.c
new file mode 100644
index 0000000..89c6c30
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mdio_mt7620.c
@@ -0,0 +1,156 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mdio.h"
+
+static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw)
+{
+ unsigned long t_start = jiffies;
+
+ while (1) {
+ if (!(mtk_switch_r32(gsw, MT7620A_GSW_REG_PIAC) & GSW_MDIO_ACCESS))
+ return 0;
+ if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
+ break;
+ }
+
+ dev_err(gsw->dev, "mdio: MDIO timeout\n");
+ return -1;
+}
+
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
+ u32 phy_register, u32 write_data)
+{
+ if (mt7620_mii_busy_wait(gsw))
+ return -1;
+
+ write_data &= 0xffff;
+
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
+ (phy_register << GSW_MDIO_REG_SHIFT) |
+ (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
+ MT7620A_GSW_REG_PIAC);
+
+ if (mt7620_mii_busy_wait(gsw))
+ return -1;
+
+ return 0;
+}
+
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
+{
+ u32 d;
+
+ if (mt7620_mii_busy_wait(gsw))
+ return 0xffff;
+
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
+ (phy_reg << GSW_MDIO_REG_SHIFT) |
+ (phy_addr << GSW_MDIO_ADDR_SHIFT),
+ MT7620A_GSW_REG_PIAC);
+
+ if (mt7620_mii_busy_wait(gsw))
+ return 0xffff;
+
+ d = mtk_switch_r32(gsw, MT7620A_GSW_REG_PIAC) & 0xffff;
+
+ return d;
+}
+
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
+{
+ struct fe_priv *priv = bus->priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
+
+ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
+}
+
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+{
+ struct fe_priv *priv = bus->priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
+
+ return _mt7620_mii_read(gsw, phy_addr, phy_reg);
+}
+
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
+{
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ _mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
+ _mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
+}
+
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
+{
+ u16 high, low;
+
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
+ high = _mt7620_mii_read(gsw, 0x1f, 0x10);
+
+ return (high << 16) | (low & 0xffff);
+}
+
+static unsigned char *fe_speed_str(int speed)
+{
+ switch (speed) {
+ case 2:
+ case SPEED_1000:
+ return "1000";
+ case 1:
+ case SPEED_100:
+ return "100";
+ case 0:
+ case SPEED_10:
+ return "10";
+ }
+
+ return "? ";
+}
+
+int mt7620_has_carrier(struct fe_priv *priv)
+{
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
+ int i;
+
+ for (i = 0; i < GSW_PORT6; i++)
+ if (mtk_switch_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
+ return 1;
+ return 0;
+}
+
+
+void mt7620_print_link_state(struct fe_priv *priv, int port, int link,
+ int speed, int duplex)
+{
+ if (link)
+ netdev_info(priv->netdev, "port %d link up (%sMbps/%s duplex)\n",
+ port, fe_speed_str(speed),
+ (duplex) ? "Full" : "Half");
+ else
+ netdev_info(priv->netdev, "port %d link down\n", port);
+}
+
+void mt7620_mdio_link_adjust(struct fe_priv *priv, int port)
+{
+ mt7620_print_link_state(priv, port, priv->link[port],
+ priv->phy->speed[port],
+ (priv->phy->duplex[port] == DUPLEX_FULL));
+}
diff --git a/drivers/net/ethernet/mediatek/soc_mt7620.c b/drivers/net/ethernet/mediatek/soc_mt7620.c
new file mode 100644
index 0000000..9ad6bc9
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/soc_mt7620.c
@@ -0,0 +1,334 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/if_vlan.h>
+#include <linux/of_net.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include <mt7620.h>
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mt7530.h"
+#include "mdio.h"
+
+#define MT7620A_CDMA_CSG_CFG 0x400
+#define MT7620_DMA_VID (MT7620A_CDMA_CSG_CFG | 0x30)
+#define MT7621_CDMP_IG_CTRL (MT7620A_CDMA_CSG_CFG + 0x00)
+#define MT7621_CDMP_EG_CTRL (MT7620A_CDMA_CSG_CFG + 0x04)
+#define MT7620A_RESET_FE BIT(21)
+#define MT7621_RESET_FE BIT(6)
+#define MT7620A_RESET_ESW BIT(23)
+#define MT7620_L4_VALID BIT(23)
+#define MT7621_L4_VALID BIT(24)
+
+#define MT7620_TX_DMA_UDF BIT(15)
+#define MT7621_TX_DMA_UDF BIT(19)
+#define TX_DMA_FP_BMAP ((0xff) << 19)
+
+#define CDMA_ICS_EN BIT(2)
+#define CDMA_UCS_EN BIT(1)
+#define CDMA_TCS_EN BIT(0)
+
+#define GDMA_ICS_EN BIT(22)
+#define GDMA_TCS_EN BIT(21)
+#define GDMA_UCS_EN BIT(20)
+
+/* frame engine counters */
+#define MT7620_REG_MIB_OFFSET 0x1000
+#define MT7620_PPE_AC_BCNT0 (MT7620_REG_MIB_OFFSET + 0x00)
+#define MT7620_GDM1_TX_GBCNT (MT7620_REG_MIB_OFFSET + 0x300)
+#define MT7620_GDM2_TX_GBCNT (MT7620_GDM1_TX_GBCNT + 0x40)
+
+#define MT7621_REG_MIB_OFFSET 0x2000
+#define MT7621_PPE_AC_BCNT0 (MT7621_REG_MIB_OFFSET + 0x00)
+#define MT7621_GDM1_TX_GBCNT (MT7621_REG_MIB_OFFSET + 0x400)
+#define MT7621_GDM2_TX_GBCNT (MT7621_GDM1_TX_GBCNT + 0x40)
+
+#define GSW_REG_GDMA1_MAC_ADRL 0x508
+#define GSW_REG_GDMA1_MAC_ADRH 0x50C
+
+#define MT7621_FE_RST_GL (FE_FE_OFFSET + 0x04)
+#define MT7620_FE_INT_STATUS2 (FE_FE_OFFSET + 0x08)
+
+/* FE_INT_STATUS reg on mt7620 define CNT_GDM1_AF at BIT(29)
+ * but after test it should be BIT(13).
+ */
+#define MT7620_FE_GDM1_AF BIT(13)
+#define MT7621_FE_GDM1_AF BIT(28)
+#define MT7621_FE_GDM2_AF BIT(29)
+
+static const u16 mt7620_reg_table[FE_REG_COUNT] = {
+ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
+ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
+ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
+ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
+ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
+ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+ [FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
+ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
+ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
+ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+ [FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
+ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
+ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
+ [FE_REG_FE_DMA_VID_BASE] = MT7620_DMA_VID,
+ [FE_REG_FE_COUNTER_BASE] = MT7620_GDM1_TX_GBCNT,
+ [FE_REG_FE_RST_GL] = MT7621_FE_RST_GL,
+ [FE_REG_FE_INT_STATUS2] = MT7620_FE_INT_STATUS2,
+};
+
+static int mt7620_gsw_config(struct fe_priv *priv)
+{
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv;
+
+ /* is the mt7530 internal or external */
+ if (priv->mii_bus && priv->mii_bus->phy_map[0x1f]) {
+ mt7530_probe(priv->device, gsw->base, NULL, 0);
+ mt7530_probe(priv->device, NULL, priv->mii_bus, 1);
+ } else {
+ mt7530_probe(priv->device, gsw->base, NULL, 1);
+ }
+
+ return 0;
+}
+
+static void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac)
+{
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->page_lock, flags);
+ mtk_switch_w32(gsw, (mac[0] << 8) | mac[1], GSW_REG_SMACCR1);
+ mtk_switch_w32(gsw, (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5],
+ GSW_REG_SMACCR0);
+ spin_unlock_irqrestore(&priv->page_lock, flags);
+}
+
+static void mt7620_auto_poll(struct mt7620_gsw *gsw)
+{
+ int phy;
+ int lsb = -1, msb = 0;
+
+ for_each_set_bit(phy, &gsw->autopoll, 32) {
+ if (lsb < 0)
+ lsb = phy;
+ msb = phy;
+ }
+
+ if (lsb == msb)
+ lsb--;
+
+ mtk_switch_w32(gsw, PHY_AN_EN | PHY_PRE_EN | PMY_MDC_CONF(5) |
+ (msb << 8) | lsb, ESW_PHY_POLLING);
+}
+
+static void mt7620_port_init(struct fe_priv *priv, struct device_node *np)
+{
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
+ const __be32 *_id = of_get_property(np, "reg", NULL);
+ int phy_mode, size, id;
+ int shift = 12;
+ u32 val, mask = 0;
+ int min = (gsw->port4 == PORT4_EPHY) ? (5) : (4);
+
+ if (!_id || (be32_to_cpu(*_id) < min) || (be32_to_cpu(*_id) > 5)) {
+ if (_id)
+ pr_err("%s: invalid port id %d\n", np->name,
+ be32_to_cpu(*_id));
+ else
+ pr_err("%s: invalid port id\n", np->name);
+ return;
+ }
+
+ id = be32_to_cpu(*_id);
+
+ if (id == 4)
+ shift = 14;
+
+ priv->phy->phy_fixed[id] = of_get_property(np, "mediatek,fixed-link",
+ &size);
+ if (priv->phy->phy_fixed[id] &&
+ (size != (4 * sizeof(*priv->phy->phy_fixed[id])))) {
+ pr_err("%s: invalid fixed link property\n", np->name);
+ priv->phy->phy_fixed[id] = NULL;
+ return;
+ }
+
+ phy_mode = of_get_phy_mode(np);
+ switch (phy_mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ mask = 0;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ mask = 1;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ mask = 2;
+ break;
+ default:
+ dev_err(priv->device, "port %d - invalid phy mode\n", id);
+ return;
+ }
+
+ priv->phy->phy_node[id] = of_parse_phandle(np, "phy-handle", 0);
+ if (!priv->phy->phy_node[id] && !priv->phy->phy_fixed[id])
+ return;
+
+ val = rt_sysc_r32(SYSC_REG_CFG1);
+ val &= ~(3 << shift);
+ val |= mask << shift;
+ rt_sysc_w32(val, SYSC_REG_CFG1);
+
+ if (priv->phy->phy_fixed[id]) {
+ const __be32 *link = priv->phy->phy_fixed[id];
+ int tx_fc, rx_fc;
+ u32 val = 0;
+
+ priv->phy->speed[id] = be32_to_cpup(link++);
+ tx_fc = be32_to_cpup(link++);
+ rx_fc = be32_to_cpup(link++);
+ priv->phy->duplex[id] = be32_to_cpup(link++);
+ priv->link[id] = 1;
+
+ switch (priv->phy->speed[id]) {
+ case SPEED_10:
+ val = 0;
+ break;
+ case SPEED_100:
+ val = 1;
+ break;
+ case SPEED_1000:
+ val = 2;
+ break;
+ default:
+ dev_err(priv->device, "invalid link speed: %d\n",
+ priv->phy->speed[id]);
+ priv->phy->phy_fixed[id] = 0;
+ return;
+ }
+ val = PMCR_SPEED(val);
+ val |= PMCR_LINK | PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN |
+ PMCR_TX_EN | PMCR_FORCE | PMCR_MAC_MODE | PMCR_IPG;
+ if (tx_fc)
+ val |= PMCR_TX_FC;
+ if (rx_fc)
+ val |= PMCR_RX_FC;
+ if (priv->phy->duplex[id])
+ val |= PMCR_DUPLEX;
+ mtk_switch_w32(gsw, val, GSW_REG_PORT_PMCR(id));
+ dev_info(priv->device, "using fixed link parameters\n");
+ return;
+ }
+
+ if (priv->phy->phy_node[id] && priv->mii_bus->phy_map[id]) {
+ u32 val = PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN |
+ PMCR_TX_EN | PMCR_MAC_MODE | PMCR_IPG;
+
+ mtk_switch_w32(gsw, val, GSW_REG_PORT_PMCR(id));
+ fe_connect_phy_node(priv, priv->phy->phy_node[id]);
+ gsw->autopoll |= BIT(id);
+ mt7620_auto_poll(gsw);
+ return;
+ }
+}
+
+static void mt7620_fe_reset(void)
+{
+ fe_reset(MT7620A_RESET_FE | MT7620A_RESET_ESW);
+}
+
+static void mt7620_rxcsum_config(bool enable)
+{
+ if (enable)
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) | (GDMA_ICS_EN |
+ GDMA_TCS_EN | GDMA_UCS_EN),
+ MT7620A_GDMA1_FWD_CFG);
+ else
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~(GDMA_ICS_EN |
+ GDMA_TCS_EN | GDMA_UCS_EN),
+ MT7620A_GDMA1_FWD_CFG);
+}
+
+static void mt7620_txcsum_config(bool enable)
+{
+ if (enable)
+ fe_w32(fe_r32(MT7620A_CDMA_CSG_CFG) | (CDMA_ICS_EN |
+ CDMA_UCS_EN | CDMA_TCS_EN),
+ MT7620A_CDMA_CSG_CFG);
+ else
+ fe_w32(fe_r32(MT7620A_CDMA_CSG_CFG) & ~(CDMA_ICS_EN |
+ CDMA_UCS_EN | CDMA_TCS_EN),
+ MT7620A_CDMA_CSG_CFG);
+}
+
+static int mt7620_fwd_config(struct fe_priv *priv)
+{
+ struct net_device *dev = priv_netdev(priv);
+
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~7, MT7620A_GDMA1_FWD_CFG);
+
+ mt7620_txcsum_config((dev->features & NETIF_F_IP_CSUM));
+ mt7620_rxcsum_config((dev->features & NETIF_F_RXCSUM));
+
+ return 0;
+}
+
+static void mt7620_tx_dma(struct fe_tx_dma *txd)
+{
+}
+
+static void mt7620_init_data(struct fe_soc_data *data,
+ struct net_device *netdev)
+{
+ struct fe_priv *priv = netdev_priv(netdev);
+
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_RX_2B_OFFSET |
+ FE_FLAG_RX_SG_DMA | FE_FLAG_HAS_SWITCH;
+
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_HW_VLAN_CTAG_TX;
+ if (mt7620_get_eco() >= 5)
+ netdev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_IPV6_CSUM;
+}
+
+static struct fe_soc_data mt7620_data = {
+ .init_data = mt7620_init_data,
+ .reset_fe = mt7620_fe_reset,
+ .set_mac = mt7620_set_mac,
+ .fwd_config = mt7620_fwd_config,
+ .tx_dma = mt7620_tx_dma,
+ .switch_init = mtk_gsw_init,
+ .port_init = mt7620_port_init,
+ .reg_table = mt7620_reg_table,
+ .pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS,
+ .rx_int = RT5350_RX_DONE_INT,
+ .tx_int = RT5350_TX_DONE_INT,
+ .status_int = MT7620_FE_GDM1_AF,
+ .checksum_bit = MT7620_L4_VALID,
+ .has_carrier = mt7620_has_carrier,
+ .mdio_read = mt7620_mdio_read,
+ .mdio_write = mt7620_mdio_write,
+ .mdio_adjust_link = mt7620_mdio_link_adjust,
+};
+
+const struct of_device_id of_fe_match[] = {
+ { .compatible = "mediatek,mt7620-eth", .data = &mt7620_data },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_fe_match);
--
1.7.10.4

View file

@ -0,0 +1,214 @@
From 693f0ff3dbc53f910dff57ac15c324f7a94cc0ad Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 23:50:53 +0100
Subject: [PATCH 509/513] net-next: mediatek: add support for mt7621
Add support for SoCs from the mt7620 family. This include mt7620 and mt7621.
These all have one dedicated external gbit port and a builtin 5 port 100mbit
switch. Additionally one of the 5 switch ports can be changed to become an
additional gbit port that we can attach a phy to. This patch includes
rudimentary code to power up the switch. There are a lot of magic values
that get written to the switch and the internal phys. These values come
straight from the SDK driver.
Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Michael Lee <igvtee@gmail.com>
---
drivers/net/ethernet/mediatek/soc_mt7621.c | 184 ++++++++++++++++++++++++++++
1 file changed, 184 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/soc_mt7621.c
diff --git a/drivers/net/ethernet/mediatek/soc_mt7621.c b/drivers/net/ethernet/mediatek/soc_mt7621.c
new file mode 100644
index 0000000..d6f7f23
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/soc_mt7621.c
@@ -0,0 +1,184 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/if_vlan.h>
+#include <linux/of_net.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mt7530.h"
+#include "mdio.h"
+
+#define MT7620A_CDMA_CSG_CFG 0x400
+#define MT7620_DMA_VID (MT7620A_CDMA_CSG_CFG | 0x30)
+#define MT7621_CDMP_IG_CTRL (MT7620A_CDMA_CSG_CFG + 0x00)
+#define MT7621_CDMP_EG_CTRL (MT7620A_CDMA_CSG_CFG + 0x04)
+#define MT7620A_RESET_FE BIT(21)
+#define MT7621_RESET_FE BIT(6)
+#define MT7620A_RESET_ESW BIT(23)
+#define MT7620_L4_VALID BIT(23)
+#define MT7621_L4_VALID BIT(24)
+
+#define MT7620_TX_DMA_UDF BIT(15)
+#define MT7621_TX_DMA_UDF BIT(19)
+#define TX_DMA_FP_BMAP ((0xff) << 19)
+
+#define CDMA_ICS_EN BIT(2)
+#define CDMA_UCS_EN BIT(1)
+#define CDMA_TCS_EN BIT(0)
+
+#define GDMA_ICS_EN BIT(22)
+#define GDMA_TCS_EN BIT(21)
+#define GDMA_UCS_EN BIT(20)
+
+/* frame engine counters */
+#define MT7620_REG_MIB_OFFSET 0x1000
+#define MT7620_PPE_AC_BCNT0 (MT7620_REG_MIB_OFFSET + 0x00)
+#define MT7620_GDM1_TX_GBCNT (MT7620_REG_MIB_OFFSET + 0x300)
+#define MT7620_GDM2_TX_GBCNT (MT7620_GDM1_TX_GBCNT + 0x40)
+
+#define MT7621_REG_MIB_OFFSET 0x2000
+#define MT7621_PPE_AC_BCNT0 (MT7621_REG_MIB_OFFSET + 0x00)
+#define MT7621_GDM1_TX_GBCNT (MT7621_REG_MIB_OFFSET + 0x400)
+#define MT7621_GDM2_TX_GBCNT (MT7621_GDM1_TX_GBCNT + 0x40)
+
+#define GSW_REG_GDMA1_MAC_ADRL 0x508
+#define GSW_REG_GDMA1_MAC_ADRH 0x50C
+
+#define MT7621_FE_RST_GL (FE_FE_OFFSET + 0x04)
+#define MT7620_FE_INT_STATUS2 (FE_FE_OFFSET + 0x08)
+
+/* FE_INT_STATUS reg on mt7620 define CNT_GDM1_AF at BIT(29)
+ * but after test it should be BIT(13).
+ */
+#define MT7620_FE_GDM1_AF BIT(13)
+#define MT7621_FE_GDM1_AF BIT(28)
+#define MT7621_FE_GDM2_AF BIT(29)
+
+static const u16 mt7621_reg_table[FE_REG_COUNT] = {
+ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
+ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
+ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
+ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
+ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
+ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+ [FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
+ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
+ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
+ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+ [FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
+ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
+ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
+ [FE_REG_FE_DMA_VID_BASE] = 0,
+ [FE_REG_FE_COUNTER_BASE] = MT7621_GDM1_TX_GBCNT,
+ [FE_REG_FE_RST_GL] = MT7621_FE_RST_GL,
+ [FE_REG_FE_INT_STATUS2] = MT7620_FE_INT_STATUS2,
+};
+
+static int mt7621_gsw_config(struct fe_priv *priv)
+{
+ if (priv->mii_bus && priv->mii_bus->phy_map[0x1f])
+ mt7530_probe(priv->device, NULL, priv->mii_bus, 1);
+
+ return 0;
+}
+
+static void mt7621_fe_reset(void)
+{
+ fe_reset(MT7621_RESET_FE);
+}
+
+static void mt7621_rxvlan_config(bool enable)
+{
+ if (enable)
+ fe_w32(1, MT7621_CDMP_EG_CTRL);
+ else
+ fe_w32(0, MT7621_CDMP_EG_CTRL);
+}
+
+static int mt7621_fwd_config(struct fe_priv *priv)
+{
+ struct net_device *dev = priv_netdev(priv);
+
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~0xffff,
+ MT7620A_GDMA1_FWD_CFG);
+
+ /* mt7621 don't have txcsum config */
+ mt7620_rxcsum_config((dev->features & NETIF_F_RXCSUM));
+ mt7621_rxvlan_config((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ (priv->flags & FE_FLAG_RX_VLAN_CTAG));
+
+ return 0;
+}
+
+static void mt7621_tx_dma(struct fe_tx_dma *txd)
+{
+ txd->txd4 = BIT(25);
+}
+
+static void mt7621_init_data(struct fe_soc_data *data,
+ struct net_device *netdev)
+{
+ struct fe_priv *priv = netdev_priv(netdev);
+
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_RX_2B_OFFSET |
+ FE_FLAG_RX_SG_DMA | FE_FLAG_NAPI_WEIGHT |
+ FE_FLAG_HAS_SWITCH;
+
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_TSO |
+ NETIF_F_TSO6 | NETIF_F_IPV6_CSUM;
+}
+
+static void mt7621_set_mac(struct fe_priv *priv, unsigned char *mac)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->page_lock, flags);
+ fe_w32((mac[0] << 8) | mac[1], GSW_REG_GDMA1_MAC_ADRH);
+ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5],
+ GSW_REG_GDMA1_MAC_ADRL);
+ spin_unlock_irqrestore(&priv->page_lock, flags);
+}
+
+static struct fe_soc_data mt7621_data = {
+ .init_data = mt7621_init_data,
+ .reset_fe = mt7621_fe_reset,
+ .set_mac = mt7621_set_mac,
+ .fwd_config = mt7621_fwd_config,
+ .tx_dma = mt7621_tx_dma,
+ .switch_init = mtk_gsw_init,
+ .switch_config = mt7621_gsw_config,
+ .reg_table = mt7621_reg_table,
+ .pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS,
+ .rx_int = RT5350_RX_DONE_INT,
+ .tx_int = RT5350_TX_DONE_INT,
+ .status_int = (MT7621_FE_GDM1_AF | MT7621_FE_GDM2_AF),
+ .checksum_bit = MT7621_L4_VALID,
+ .has_carrier = mt7620_has_carrier,
+ .mdio_read = mt7620_mdio_read,
+ .mdio_write = mt7620_mdio_write,
+ .mdio_adjust_link = mt7620_mdio_link_adjust,
+};
+
+const struct of_device_id of_fe_match[] = {
+ { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_fe_match);
--
1.7.10.4

View file

@ -0,0 +1,140 @@
From b6f779ea9c329451b89404583b45b9eb00155b32 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 18 Nov 2015 03:58:26 +0100
Subject: [PATCH 510/513] net-next: mediatek: add Kconfig and Makefile
This patch adds the Makefile and Kconfig required to make the driver build.
Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Michael Lee <igvtee@gmail.com>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/mediatek/Kconfig | 62 ++++++++++++++++++++++++++++++++
drivers/net/ethernet/mediatek/Makefile | 20 +++++++++++
4 files changed, 84 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/Kconfig
create mode 100644 drivers/net/ethernet/mediatek/Makefile
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 955d06b..5302f81 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -105,6 +105,7 @@ config LANTIQ_ETOP
Support for the MII0 inside the Lantiq SoC
source "drivers/net/ethernet/marvell/Kconfig"
+source "drivers/net/ethernet/mediatek/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 4a2ee98..ecc16c2 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_JME) += jme.o
obj-$(CONFIG_KORINA) += korina.o
obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
+obj-$(CONFIG_NET_VENDOR_MEDIATEK) += mediatek/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig
new file mode 100644
index 0000000..cf883c4
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/Kconfig
@@ -0,0 +1,62 @@
+config NET_VENDOR_MEDIATEK
+ tristate "Mediatek/Ralink ethernet driver"
+ depends on RALINK
+ help
+ This driver supports the ethernet mac inside the Mediatek and Ralink WiSoCs
+
+config NET_MEDIATEK_SOC
+ def_tristate NET_VENDOR_MEDIATEK
+
+if NET_MEDIATEK_SOC
+choice
+ prompt "MAC type"
+
+config NET_MEDIATEK_RT2880
+ bool "RT2882"
+ depends on MIPS && SOC_RT288X
+
+config NET_MEDIATEK_RT3050
+ bool "RT3050/MT7628"
+ depends on MIPS && (SOC_RT305X || SOC_MT7620)
+
+config NET_MEDIATEK_RT3883
+ bool "RT3883"
+ depends on MIPS && SOC_RT3883
+
+config NET_MEDIATEK_MT7620
+ bool "MT7620"
+ depends on MIPS && SOC_MT7620
+
+config NET_MEDIATEK_MT7621
+ bool "MT7621"
+ depends on MIPS && SOC_MT7621
+
+endchoice
+
+config NET_MEDIATEK_MDIO
+ def_bool NET_MEDIATEK_SOC
+ depends on (NET_MEDIATEK_RT2880 || NET_MEDIATEK_RT3883 || NET_MEDIATEK_MT7620 || NET_MEDIATEK_MT7621)
+ select PHYLIB
+
+config NET_MEDIATEK_MDIO_RT2880
+ def_bool NET_MEDIATEK_SOC
+ depends on (NET_MEDIATEK_RT2880 || NET_MEDIATEK_RT3883)
+ select NET_MEDIATEK_MDIO
+
+config NET_MEDIATEK_MDIO_MT7620
+ def_bool NET_MEDIATEK_SOC
+ depends on (NET_MEDIATEK_MT7620 || NET_MEDIATEK_MT7621)
+ select NET_MEDIATEK_MDIO
+
+config NET_MEDIATEK_ESW_RT3050
+ def_tristate NET_MEDIATEK_SOC
+ depends on NET_MEDIATEK_RT3050
+
+config NET_MEDIATEK_GSW_MT7620
+ def_tristate NET_MEDIATEK_SOC
+ depends on NET_MEDIATEK_MT7620
+
+config NET_MEDIATEK_GSW_MT7621
+ def_tristate NET_MEDIATEK_SOC
+ depends on NET_MEDIATEK_MT7621
+endif
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
new file mode 100644
index 0000000..c4d2dfb
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the Ralink SoCs built-in ethernet macs
+#
+
+mtk-eth-soc-y += mtk_eth_soc.o ethtool.o
+
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO) += mdio.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO_RT2880) += mdio_rt2880.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO_MT7620) += mdio_mt7620.o
+
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_RT2880) += soc_rt2880.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_RT3050) += soc_rt3050.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_RT3883) += soc_rt3883.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7620) += soc_mt7620.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
+
+obj-$(CONFIG_NET_MEDIATEK_ESW_RT3050) += esw_rt3050.o
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
+obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk-eth-soc.o
--
1.7.10.4

View file

@ -0,0 +1,93 @@
From 6543b4cef96c12903f5ec5c015cd223a6b3c9a33 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 21:16:59 +0100
Subject: [PATCH 511/513] net: mediatek: add support for the multiphy carrier
patch
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/gsw_mt7620.c | 1 +
drivers/net/ethernet/mediatek/gsw_mt7620.h | 1 +
drivers/net/ethernet/mediatek/gsw_mt7621.c | 1 +
drivers/net/ethernet/mediatek/mdio.c | 1 +
drivers/net/ethernet/mediatek/mdio_mt7620.c | 12 ++++++++++++
5 files changed, 16 insertions(+)
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.c b/drivers/net/ethernet/mediatek/gsw_mt7620.c
index a37ed1b..28122ac 100644
--- a/drivers/net/ethernet/mediatek/gsw_mt7620.c
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.c
@@ -54,6 +54,7 @@ static irqreturn_t gsw_interrupt_mt7620(int irq, void *_priv)
priv->link[i] = link;
}
+ mt7620_handle_carrier(priv);
mtk_switch_w32(gsw, status, GSW_REG_ISR);
return IRQ_HANDLED;
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.h b/drivers/net/ethernet/mediatek/gsw_mt7620.h
index 0d6ee84..dcef9a8 100644
--- a/drivers/net/ethernet/mediatek/gsw_mt7620.h
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
@@ -113,5 +113,6 @@ u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
u32 phy_register, u32 write_data);
u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg);
+void mt7620_handle_carrier(struct fe_priv *priv);
#endif
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7621.c b/drivers/net/ethernet/mediatek/gsw_mt7621.c
index 500841f..dc349ef 100644
--- a/drivers/net/ethernet/mediatek/gsw_mt7621.c
+++ b/drivers/net/ethernet/mediatek/gsw_mt7621.c
@@ -60,6 +60,7 @@ static irqreturn_t gsw_interrupt_mt7621(int irq, void *_priv)
}
}
+ mt7620_handle_carrier(priv);
mt7530_mdio_w32(gsw, 0x700c, 0x1f);
return IRQ_HANDLED;
diff --git a/drivers/net/ethernet/mediatek/mdio.c b/drivers/net/ethernet/mediatek/mdio.c
index 169c937..79a946e 100644
--- a/drivers/net/ethernet/mediatek/mdio.c
+++ b/drivers/net/ethernet/mediatek/mdio.c
@@ -89,6 +89,7 @@ int fe_connect_phy_node(struct fe_priv *priv, struct device_node *phy_node)
phydev->supported &= PHY_GBIT_FEATURES;
phydev->advertising = phydev->supported;
+ phydev->no_auto_carrier_off = 1;
dev_info(priv->device,
"connected port %d to PHY at %s [uid=%08x, driver=%s]\n",
diff --git a/drivers/net/ethernet/mediatek/mdio_mt7620.c b/drivers/net/ethernet/mediatek/mdio_mt7620.c
index 89c6c30..db6db52 100644
--- a/drivers/net/ethernet/mediatek/mdio_mt7620.c
+++ b/drivers/net/ethernet/mediatek/mdio_mt7620.c
@@ -137,6 +137,17 @@ int mt7620_has_carrier(struct fe_priv *priv)
}
+void mt7620_handle_carrier(struct fe_priv *priv)
+{
+ if (!priv->phy)
+ return;
+
+ if (mt7620_has_carrier(priv))
+ netif_carrier_on(priv->netdev);
+ else
+ netif_carrier_off(priv->netdev);
+}
+
void mt7620_print_link_state(struct fe_priv *priv, int port, int link,
int speed, int duplex)
{
@@ -153,4 +164,5 @@ void mt7620_mdio_link_adjust(struct fe_priv *priv, int port)
mt7620_print_link_state(priv, port, priv->link[port],
priv->phy->speed[port],
(priv->phy->duplex[port] == DUPLEX_FULL));
+ mt7620_handle_carrier(priv);
}
--
1.7.10.4

View file

@ -0,0 +1,906 @@
From 4473f30809eed09037e1932a0c1805172cd997f7 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 22:07:31 +0100
Subject: [PATCH 512/513] net: mediatek: add swconfig driver for esw_rt3050
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/esw_rt3050.c | 805 ++++++++++++++++++++++++++++
1 file changed, 805 insertions(+)
diff --git a/drivers/net/ethernet/mediatek/esw_rt3050.c b/drivers/net/ethernet/mediatek/esw_rt3050.c
index 670ae16..352d50a 100644
--- a/drivers/net/ethernet/mediatek/esw_rt3050.c
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.c
@@ -17,6 +17,8 @@
#include <linux/platform_device.h>
#include <asm/mach-ralink/ralink_regs.h>
+#include <linux/switch.h>
+
#include "mtk_eth_soc.h"
/* HW limitations for this switch:
@@ -141,6 +143,8 @@
#define RT305X_ESW_PORT5 5
#define RT305X_ESW_PORT6 6
+#define RT305X_ESW_PORTS_NONE 0
+
#define RT305X_ESW_PMAP_LLLLLL 0x3f
#define RT305X_ESW_PMAP_LLLLWL 0x2f
#define RT305X_ESW_PMAP_WLLLLL 0x3e
@@ -158,15 +162,51 @@
#define RT305X_ESW_PORTS_ALL \
(RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU)
+#define RT305X_ESW_NUM_VLANS 16
+#define RT305X_ESW_NUM_VIDS 4096
#define RT305X_ESW_NUM_PORTS 7
+#define RT305X_ESW_NUM_LANWAN 6
#define RT305X_ESW_NUM_LEDS 5
+#define RT5350_ESW_REG_PXTPC(_x) (0x150 + (4 * _x))
#define RT5350_EWS_REG_LED_POLARITY 0x168
#define RT5350_RESET_EPHY BIT(24)
+enum {
+ /* Global attributes. */
+ RT305X_ESW_ATTR_ENABLE_VLAN,
+ RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
+ RT305X_ESW_ATTR_BC_STATUS,
+ RT305X_ESW_ATTR_LED_FREQ,
+ /* Port attributes. */
+ RT305X_ESW_ATTR_PORT_DISABLE,
+ RT305X_ESW_ATTR_PORT_DOUBLETAG,
+ RT305X_ESW_ATTR_PORT_UNTAG,
+ RT305X_ESW_ATTR_PORT_LED,
+ RT305X_ESW_ATTR_PORT_LAN,
+ RT305X_ESW_ATTR_PORT_RECV_BAD,
+ RT305X_ESW_ATTR_PORT_RECV_GOOD,
+ RT5350_ESW_ATTR_PORT_TR_BAD,
+ RT5350_ESW_ATTR_PORT_TR_GOOD,
+};
+
struct esw_port {
bool disable;
+ bool doubletag;
+ bool untag;
u8 led;
+ u16 pvid;
+};
+
+struct esw_vlan {
+ u8 ports;
+ u16 vid;
+};
+
+enum {
+ RT305X_ESW_VLAN_CONFIG_NONE = 0,
+ RT305X_ESW_VLAN_CONFIG_LLLLW,
+ RT305X_ESW_VLAN_CONFIG_WLLLL,
};
struct rt305x_esw {
@@ -180,6 +220,12 @@ struct rt305x_esw {
unsigned char port_map;
unsigned int reg_led_polarity;
+ struct switch_dev swdev;
+ bool global_vlan_enable;
+ bool alt_vlan_disable;
+ int bc_storm_protect;
+ int led_frequency;
+ struct esw_vlan vlans[RT305X_ESW_NUM_VLANS];
struct esw_port ports[RT305X_ESW_NUM_PORTS];
};
@@ -252,6 +298,71 @@ out:
return ret;
}
+static unsigned esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan)
+{
+ unsigned s;
+ unsigned val;
+
+ s = RT305X_ESW_VLANI_VID_S * (vlan % 2);
+ val = esw_r32(esw, RT305X_ESW_REG_VLANI(vlan / 2));
+ val = (val >> s) & RT305X_ESW_VLANI_VID_M;
+
+ return val;
+}
+
+static void esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid)
+{
+ unsigned s;
+
+ s = RT305X_ESW_VLANI_VID_S * (vlan % 2);
+ esw_rmw(esw,
+ RT305X_ESW_REG_VLANI(vlan / 2),
+ RT305X_ESW_VLANI_VID_M << s,
+ (vid & RT305X_ESW_VLANI_VID_M) << s);
+}
+
+static unsigned esw_get_pvid(struct rt305x_esw *esw, unsigned port)
+{
+ unsigned s, val;
+
+ s = RT305X_ESW_PVIDC_PVID_S * (port % 2);
+ val = esw_r32(esw, RT305X_ESW_REG_PVIDC(port / 2));
+ return (val >> s) & RT305X_ESW_PVIDC_PVID_M;
+}
+
+static void esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid)
+{
+ unsigned s;
+
+ s = RT305X_ESW_PVIDC_PVID_S * (port % 2);
+ esw_rmw(esw,
+ RT305X_ESW_REG_PVIDC(port / 2),
+ RT305X_ESW_PVIDC_PVID_M << s,
+ (pvid & RT305X_ESW_PVIDC_PVID_M) << s);
+}
+
+static unsigned esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan)
+{
+ unsigned s, val;
+
+ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4);
+ val = esw_r32(esw, RT305X_ESW_REG_VMSC(vlan / 4));
+ val = (val >> s) & RT305X_ESW_VMSC_MSC_M;
+
+ return val;
+}
+
+static void esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc)
+{
+ unsigned s;
+
+ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4);
+ esw_rmw(esw,
+ RT305X_ESW_REG_VMSC(vlan / 4),
+ RT305X_ESW_VMSC_MSC_M << s,
+ (msc & RT305X_ESW_VMSC_MSC_M) << s);
+}
+
static unsigned esw_get_port_disable(struct rt305x_esw *esw)
{
unsigned reg;
@@ -261,6 +372,59 @@ static unsigned esw_get_port_disable(struct rt305x_esw *esw)
RT305X_ESW_POC0_DIS_PORT_M;
}
+static void esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask)
+{
+ unsigned old_mask;
+ unsigned enable_mask;
+ unsigned changed;
+ int i;
+
+ old_mask = esw_get_port_disable(esw);
+ changed = old_mask ^ disable_mask;
+ enable_mask = old_mask & disable_mask;
+
+ /* enable before writing to MII */
+ esw_rmw(esw, RT305X_ESW_REG_POC0,
+ (RT305X_ESW_POC0_DIS_PORT_M <<
+ RT305X_ESW_POC0_DIS_PORT_S),
+ enable_mask << RT305X_ESW_POC0_DIS_PORT_S);
+
+ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) {
+ if (!(changed & (1 << i)))
+ continue;
+ if (disable_mask & (1 << i)) {
+ /* disable */
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_PDOWN);
+ } else {
+ /* enable */
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_FULLDPLX |
+ BMCR_ANENABLE |
+ BMCR_ANRESTART |
+ BMCR_SPEED100);
+ }
+ }
+
+ /* disable after writing to MII */
+ esw_rmw(esw, RT305X_ESW_REG_POC0,
+ (RT305X_ESW_POC0_DIS_PORT_M <<
+ RT305X_ESW_POC0_DIS_PORT_S),
+ disable_mask << RT305X_ESW_POC0_DIS_PORT_S);
+}
+
+static void esw_set_gsc(struct rt305x_esw *esw)
+{
+ esw_rmw(esw, RT305X_ESW_REG_SGC,
+ RT305X_ESW_GSC_BC_STROM_MASK << RT305X_ESW_GSC_BC_STROM_SHIFT,
+ esw->bc_storm_protect << RT305X_ESW_GSC_BC_STROM_SHIFT);
+ esw_rmw(esw, RT305X_ESW_REG_SGC,
+ RT305X_ESW_GSC_LED_FREQ_MASK << RT305X_ESW_GSC_LED_FREQ_SHIFT,
+ esw->led_frequency << RT305X_ESW_GSC_LED_FREQ_SHIFT);
+}
+
+static int esw_apply_config(struct switch_dev *dev);
+
static void esw_hw_init(struct rt305x_esw *esw)
{
int i;
@@ -519,6 +683,9 @@ static void esw_hw_init(struct rt305x_esw *esw)
for (i = 0; i < RT305X_ESW_NUM_LEDS; i++)
esw->ports[i].led = 0x05;
+ /* Apply the empty config. */
+ esw_apply_config(&esw->swdev);
+
/* Only unmask the port change interrupt */
esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR);
}
@@ -541,11 +708,629 @@ static irqreturn_t esw_interrupt(int irq, void *_esw)
return IRQ_HANDLED;
}
+static int esw_apply_config(struct switch_dev *dev)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ int i;
+ u8 disable = 0;
+ u8 doubletag = 0;
+ u8 en_vlan = 0;
+ u8 untag = 0;
+
+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+ u32 vid, vmsc;
+ if (esw->global_vlan_enable) {
+ vid = esw->vlans[i].vid;
+ vmsc = esw->vlans[i].ports;
+ } else {
+ vid = RT305X_ESW_VLAN_NONE;
+ vmsc = RT305X_ESW_PORTS_NONE;
+ }
+ esw_set_vlan_id(esw, i, vid);
+ esw_set_vmsc(esw, i, vmsc);
+ }
+
+ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+ u32 pvid;
+ disable |= esw->ports[i].disable << i;
+ if (esw->global_vlan_enable) {
+ doubletag |= esw->ports[i].doubletag << i;
+ en_vlan |= 1 << i;
+ untag |= esw->ports[i].untag << i;
+ pvid = esw->ports[i].pvid;
+ } else {
+ int x = esw->alt_vlan_disable ? 0 : 1;
+ doubletag |= x << i;
+ en_vlan |= x << i;
+ untag |= x << i;
+ pvid = 0;
+ }
+ esw_set_pvid(esw, i, pvid);
+ if (i < RT305X_ESW_NUM_LEDS)
+ esw_w32(esw, esw->ports[i].led,
+ RT305X_ESW_REG_P0LED + 4*i);
+ }
+
+ esw_set_gsc(esw);
+ esw_set_port_disable(esw, disable);
+ esw_rmw(esw, RT305X_ESW_REG_SGC2,
+ (RT305X_ESW_SGC2_DOUBLE_TAG_M <<
+ RT305X_ESW_SGC2_DOUBLE_TAG_S),
+ doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_S);
+ esw_rmw(esw, RT305X_ESW_REG_PFC1,
+ RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S,
+ en_vlan << RT305X_ESW_PFC1_EN_VLAN_S);
+ esw_rmw(esw, RT305X_ESW_REG_POC2,
+ RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S,
+ untag << RT305X_ESW_POC2_UNTAG_EN_S);
+
+ if (!esw->global_vlan_enable) {
+ /*
+ * Still need to put all ports into vlan 0 or they'll be
+ * isolated.
+ * NOTE: vlan 0 is special, no vlan tag is prepended
+ */
+ esw_set_vlan_id(esw, 0, 0);
+ esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL);
+ }
+
+ return 0;
+}
+
+static int esw_reset_switch(struct switch_dev *dev)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ esw->global_vlan_enable = 0;
+ memset(esw->ports, 0, sizeof(esw->ports));
+ memset(esw->vlans, 0, sizeof(esw->vlans));
+ esw_hw_init(esw);
+
+ return 0;
+}
+
+static int esw_get_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ val->value.i = esw->global_vlan_enable;
+
+ return 0;
+}
+
+static int esw_set_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ esw->global_vlan_enable = val->value.i != 0;
+
+ return 0;
+}
+
+static int esw_get_alt_vlan_disable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ val->value.i = esw->alt_vlan_disable;
+
+ return 0;
+}
+
+static int esw_set_alt_vlan_disable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ esw->alt_vlan_disable = val->value.i != 0;
+
+ return 0;
+}
+
+static int
+rt305x_esw_set_bc_status(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ esw->bc_storm_protect = val->value.i & RT305X_ESW_GSC_BC_STROM_MASK;
+
+ return 0;
+}
+
+static int
+rt305x_esw_get_bc_status(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ val->value.i = esw->bc_storm_protect;
+
+ return 0;
+}
+
+static int
+rt305x_esw_set_led_freq(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ esw->led_frequency = val->value.i & RT305X_ESW_GSC_LED_FREQ_MASK;
+
+ return 0;
+}
+
+static int
+rt305x_esw_get_led_freq(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ val->value.i = esw->led_frequency;
+
+ return 0;
+}
+
+static int esw_get_port_link(struct switch_dev *dev,
+ int port,
+ struct switch_port_link *link)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ u32 speed, poa;
+
+ if (port < 0 || port >= RT305X_ESW_NUM_PORTS)
+ return -EINVAL;
+
+ poa = esw_r32(esw, RT305X_ESW_REG_POA) >> port;
+
+ link->link = (poa >> RT305X_ESW_LINK_S) & 1;
+ link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1;
+ if (port < RT305X_ESW_NUM_LEDS) {
+ speed = (poa >> RT305X_ESW_SPD_S) & 1;
+ } else {
+ if (port == RT305X_ESW_NUM_PORTS - 1)
+ poa >>= 1;
+ speed = (poa >> RT305X_ESW_SPD_S) & 3;
+ }
+ switch (speed) {
+ case 0:
+ link->speed = SWITCH_PORT_SPEED_10;
+ break;
+ case 1:
+ link->speed = SWITCH_PORT_SPEED_100;
+ break;
+ case 2:
+ case 3: /* forced gige speed can be 2 or 3 */
+ link->speed = SWITCH_PORT_SPEED_1000;
+ break;
+ default:
+ link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int esw_get_port_bool(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ int idx = val->port_vlan;
+ u32 x, reg, shift;
+
+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS)
+ return -EINVAL;
+
+ switch (attr->id) {
+ case RT305X_ESW_ATTR_PORT_DISABLE:
+ reg = RT305X_ESW_REG_POC0;
+ shift = RT305X_ESW_POC0_DIS_PORT_S;
+ break;
+ case RT305X_ESW_ATTR_PORT_DOUBLETAG:
+ reg = RT305X_ESW_REG_SGC2;
+ shift = RT305X_ESW_SGC2_DOUBLE_TAG_S;
+ break;
+ case RT305X_ESW_ATTR_PORT_UNTAG:
+ reg = RT305X_ESW_REG_POC2;
+ shift = RT305X_ESW_POC2_UNTAG_EN_S;
+ break;
+ case RT305X_ESW_ATTR_PORT_LAN:
+ reg = RT305X_ESW_REG_SGC2;
+ shift = RT305X_ESW_SGC2_LAN_PMAP_S;
+ if (idx >= RT305X_ESW_NUM_LANWAN)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ x = esw_r32(esw, reg);
+ val->value.i = (x >> (idx + shift)) & 1;
+
+ return 0;
+}
+
+static int esw_set_port_bool(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ int idx = val->port_vlan;
+
+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS ||
+ val->value.i < 0 || val->value.i > 1)
+ return -EINVAL;
+
+ switch (attr->id) {
+ case RT305X_ESW_ATTR_PORT_DISABLE:
+ esw->ports[idx].disable = val->value.i;
+ break;
+ case RT305X_ESW_ATTR_PORT_DOUBLETAG:
+ esw->ports[idx].doubletag = val->value.i;
+ break;
+ case RT305X_ESW_ATTR_PORT_UNTAG:
+ esw->ports[idx].untag = val->value.i;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int esw_get_port_recv_badgood(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ int idx = val->port_vlan;
+ int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16;
+ u32 reg;
+
+ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
+ return -EINVAL;
+ reg = esw_r32(esw, RT305X_ESW_REG_PXPC(idx));
+ val->value.i = (reg >> shift) & 0xffff;
+
+ return 0;
+}
+
+static int
+esw_get_port_tr_badgood(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ int idx = val->port_vlan;
+ int shift = attr->id == RT5350_ESW_ATTR_PORT_TR_GOOD ? 0 : 16;
+ u32 reg;
+
+ if ((ralink_soc != RT305X_SOC_RT5350) && (ralink_soc != MT762X_SOC_MT7628AN) && (ralink_soc != MT762X_SOC_MT7688))
+ return -EINVAL;
+
+ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
+ return -EINVAL;
+
+ reg = esw_r32(esw, RT5350_ESW_REG_PXTPC(idx));
+ val->value.i = (reg >> shift) & 0xffff;
+
+ return 0;
+}
+
+static int esw_get_port_led(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ int idx = val->port_vlan;
+
+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS ||
+ idx >= RT305X_ESW_NUM_LEDS)
+ return -EINVAL;
+
+ val->value.i = esw_r32(esw, RT305X_ESW_REG_P0LED + 4*idx);
+
+ return 0;
+}
+
+static int esw_set_port_led(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ int idx = val->port_vlan;
+
+ if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS)
+ return -EINVAL;
+
+ esw->ports[idx].led = val->value.i;
+
+ return 0;
+}
+
+static int esw_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ if (port >= RT305X_ESW_NUM_PORTS)
+ return -EINVAL;
+
+ *val = esw_get_pvid(esw, port);
+
+ return 0;
+}
+
+static int esw_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+ if (port >= RT305X_ESW_NUM_PORTS)
+ return -EINVAL;
+
+ esw->ports[port].pvid = val;
+
+ return 0;
+}
+
+static int esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ u32 vmsc, poc2;
+ int vlan_idx = -1;
+ int i;
+
+ val->len = 0;
+
+ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS)
+ return -EINVAL;
+
+ /* valid vlan? */
+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+ if (esw_get_vlan_id(esw, i) == val->port_vlan &&
+ esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) {
+ vlan_idx = i;
+ break;
+ }
+ }
+
+ if (vlan_idx == -1)
+ return -EINVAL;
+
+ vmsc = esw_get_vmsc(esw, vlan_idx);
+ poc2 = esw_r32(esw, RT305X_ESW_REG_POC2);
+
+ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+ struct switch_port *p;
+ int port_mask = 1 << i;
+
+ if (!(vmsc & port_mask))
+ continue;
+
+ p = &val->value.ports[val->len++];
+ p->id = i;
+ if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S))
+ p->flags = 0;
+ else
+ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED;
+ }
+
+ return 0;
+}
+
+static int esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+ int ports;
+ int vlan_idx = -1;
+ int i;
+
+ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS ||
+ val->len > RT305X_ESW_NUM_PORTS)
+ return -EINVAL;
+
+ /* one of the already defined vlans? */
+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+ if (esw->vlans[i].vid == val->port_vlan &&
+ esw->vlans[i].ports != RT305X_ESW_PORTS_NONE) {
+ vlan_idx = i;
+ break;
+ }
+ }
+
+ /* select a free slot */
+ for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) {
+ if (esw->vlans[i].ports == RT305X_ESW_PORTS_NONE)
+ vlan_idx = i;
+ }
+
+ /* bail if all slots are in use */
+ if (vlan_idx == -1)
+ return -EINVAL;
+
+ ports = RT305X_ESW_PORTS_NONE;
+ for (i = 0; i < val->len; i++) {
+ struct switch_port *p = &val->value.ports[i];
+ int port_mask = 1 << p->id;
+ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED));
+
+ if (p->id >= RT305X_ESW_NUM_PORTS)
+ return -EINVAL;
+
+ ports |= port_mask;
+ esw->ports[p->id].untag = untagged;
+ }
+ esw->vlans[vlan_idx].ports = ports;
+ if (ports == RT305X_ESW_PORTS_NONE)
+ esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE;
+ else
+ esw->vlans[vlan_idx].vid = val->port_vlan;
+
+ return 0;
+}
+
+static const struct switch_attr esw_global[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "VLAN mode (1:enabled)",
+ .max = 1,
+ .id = RT305X_ESW_ATTR_ENABLE_VLAN,
+ .get = esw_get_vlan_enable,
+ .set = esw_set_vlan_enable,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "alternate_vlan_disable",
+ .description = "Use en_vlan instead of doubletag to disable"
+ " VLAN mode",
+ .max = 1,
+ .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
+ .get = esw_get_alt_vlan_disable,
+ .set = esw_set_alt_vlan_disable,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "bc_storm_protect",
+ .description = "Global broadcast storm protection (0:Disable, 1:64 blocks, 2:96 blocks, 3:128 blocks)",
+ .max = 3,
+ .id = RT305X_ESW_ATTR_BC_STATUS,
+ .get = rt305x_esw_get_bc_status,
+ .set = rt305x_esw_set_bc_status,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "led_frequency",
+ .description = "LED Flash frequency (0:30mS, 1:60mS, 2:240mS, 3:480mS)",
+ .max = 3,
+ .id = RT305X_ESW_ATTR_LED_FREQ,
+ .get = rt305x_esw_get_led_freq,
+ .set = rt305x_esw_set_led_freq,
+ }
+};
+
+static const struct switch_attr esw_port[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "disable",
+ .description = "Port state (1:disabled)",
+ .max = 1,
+ .id = RT305X_ESW_ATTR_PORT_DISABLE,
+ .get = esw_get_port_bool,
+ .set = esw_set_port_bool,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "doubletag",
+ .description = "Double tagging for incoming vlan packets "
+ "(1:enabled)",
+ .max = 1,
+ .id = RT305X_ESW_ATTR_PORT_DOUBLETAG,
+ .get = esw_get_port_bool,
+ .set = esw_set_port_bool,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "untag",
+ .description = "Untag (1:strip outgoing vlan tag)",
+ .max = 1,
+ .id = RT305X_ESW_ATTR_PORT_UNTAG,
+ .get = esw_get_port_bool,
+ .set = esw_set_port_bool,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "led",
+ .description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity,"
+ " 4:collision, 5:linkact, 6:duplcoll, 7:10mact,"
+ " 8:100mact, 10:blink, 11:off, 12:on)",
+ .max = 15,
+ .id = RT305X_ESW_ATTR_PORT_LED,
+ .get = esw_get_port_led,
+ .set = esw_set_port_led,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "lan",
+ .description = "HW port group (0:wan, 1:lan)",
+ .max = 1,
+ .id = RT305X_ESW_ATTR_PORT_LAN,
+ .get = esw_get_port_bool,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "recv_bad",
+ .description = "Receive bad packet counter",
+ .id = RT305X_ESW_ATTR_PORT_RECV_BAD,
+ .get = esw_get_port_recv_badgood,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "recv_good",
+ .description = "Receive good packet counter",
+ .id = RT305X_ESW_ATTR_PORT_RECV_GOOD,
+ .get = esw_get_port_recv_badgood,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "tr_bad",
+
+ .description = "Transmit bad packet counter. rt5350 only",
+ .id = RT5350_ESW_ATTR_PORT_TR_BAD,
+ .get = esw_get_port_tr_badgood,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "tr_good",
+
+ .description = "Transmit good packet counter. rt5350 only",
+ .id = RT5350_ESW_ATTR_PORT_TR_GOOD,
+ .get = esw_get_port_tr_badgood,
+ },
+};
+
+static const struct switch_attr esw_vlan[] = {
+};
+
+static const struct switch_dev_ops esw_ops = {
+ .attr_global = {
+ .attr = esw_global,
+ .n_attr = ARRAY_SIZE(esw_global),
+ },
+ .attr_port = {
+ .attr = esw_port,
+ .n_attr = ARRAY_SIZE(esw_port),
+ },
+ .attr_vlan = {
+ .attr = esw_vlan,
+ .n_attr = ARRAY_SIZE(esw_vlan),
+ },
+ .get_vlan_ports = esw_get_vlan_ports,
+ .set_vlan_ports = esw_set_vlan_ports,
+ .get_port_pvid = esw_get_port_pvid,
+ .set_port_pvid = esw_set_port_pvid,
+ .get_port_link = esw_get_port_link,
+ .apply_config = esw_apply_config,
+ .reset_switch = esw_reset_switch,
+};
+
static int esw_probe(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct device_node *np = pdev->dev.of_node;
const __be32 *port_map, *reg_init;
+ struct switch_dev *swdev;
struct rt305x_esw *esw;
struct resource *irq;
int ret;
@@ -568,6 +1353,21 @@ static int esw_probe(struct platform_device *pdev)
if (reg_init)
esw->reg_led_polarity = be32_to_cpu(*reg_init);
+ swdev = &esw->swdev;
+ swdev->of_node = pdev->dev.of_node;
+ swdev->name = "rt305x-esw";
+ swdev->alias = "rt305x";
+ swdev->cpu_port = RT305X_ESW_PORT6;
+ swdev->ports = RT305X_ESW_NUM_PORTS;
+ swdev->vlans = RT305X_ESW_NUM_VIDS;
+ swdev->ops = &esw_ops;
+
+ ret = register_switch(swdev, NULL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register_switch failed\n");
+ goto unmap_base;
+ }
+
platform_set_drvdata(pdev, esw);
spin_lock_init(&esw->reg_rw_lock);
@@ -583,6 +1383,11 @@ static int esw_probe(struct platform_device *pdev)
}
return ret;
+
+unmap_base:
+ iounmap(esw->base);
+ kfree(esw);
+ return ret;
}
static int esw_remove(struct platform_device *pdev)
--
1.7.10.4

View file

@ -0,0 +1,913 @@
From cf5a08f1f16913da8bb24a96afaa2969b29d0827 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 14 Dec 2015 22:25:57 +0100
Subject: [PATCH 513/513] net: mediatek: add swconfig driver for gsw_mt762x
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/Makefile | 4 +-
drivers/net/ethernet/mediatek/mt7530.c | 804 +++++++++++++++++++++++++++
drivers/net/ethernet/mediatek/mt7530.h | 20 +
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 9 +-
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
drivers/net/ethernet/mediatek/soc_mt7620.c | 1 +
6 files changed, 835 insertions(+), 4 deletions(-)
create mode 100644 drivers/net/ethernet/mediatek/mt7530.c
create mode 100644 drivers/net/ethernet/mediatek/mt7530.h
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
index c4d2dfb..07ba4c2 100644
--- a/drivers/net/ethernet/mediatek/Makefile
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -15,6 +15,6 @@ mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7620) += soc_mt7620.o
mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
obj-$(CONFIG_NET_MEDIATEK_ESW_RT3050) += esw_rt3050.o
-obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o
-obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o mt7530.o
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o mt7530.o
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk-eth-soc.o
diff --git a/drivers/net/ethernet/mediatek/mt7530.c b/drivers/net/ethernet/mediatek/mt7530.c
new file mode 100644
index 0000000..4d9980d
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mt7530.c
@@ -0,0 +1,804 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/bitops.h>
+#include <net/genetlink.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/lockdep.h>
+#include <linux/workqueue.h>
+#include <linux/of_device.h>
+
+#include "mt7530.h"
+
+#define MT7530_CPU_PORT 6
+#define MT7530_NUM_PORTS 8
+#define MT7530_NUM_VLANS 16
+#define MT7530_MAX_VID 4095
+#define MT7530_MIN_VID 0
+
+/* registers */
+#define REG_ESW_VLAN_VTCR 0x90
+#define REG_ESW_VLAN_VAWD1 0x94
+#define REG_ESW_VLAN_VAWD2 0x98
+#define REG_ESW_VLAN_VTIM(x) (0x100 + 4 * ((x) / 2))
+
+#define REG_ESW_VLAN_VAWD1_IVL_MAC BIT(30)
+#define REG_ESW_VLAN_VAWD1_VTAG_EN BIT(28)
+#define REG_ESW_VLAN_VAWD1_VALID BIT(0)
+
+/* vlan egress mode */
+enum {
+ ETAG_CTRL_UNTAG = 0,
+ ETAG_CTRL_TAG = 2,
+ ETAG_CTRL_SWAP = 1,
+ ETAG_CTRL_STACK = 3,
+};
+
+#define REG_ESW_PORT_PCR(x) (0x2004 | ((x) << 8))
+#define REG_ESW_PORT_PVC(x) (0x2010 | ((x) << 8))
+#define REG_ESW_PORT_PPBV1(x) (0x2014 | ((x) << 8))
+
+#define REG_HWTRAP 0x7804
+
+#define MIB_DESC(_s , _o, _n) \
+ { \
+ .size = (_s), \
+ .offset = (_o), \
+ .name = (_n), \
+ }
+
+struct mt7xxx_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+#define MT7621_MIB_COUNTER_BASE 0x4000
+#define MT7621_MIB_COUNTER_PORT_OFFSET 0x100
+#define MT7621_STATS_TDPC 0x00
+#define MT7621_STATS_TCRC 0x04
+#define MT7621_STATS_TUPC 0x08
+#define MT7621_STATS_TMPC 0x0C
+#define MT7621_STATS_TBPC 0x10
+#define MT7621_STATS_TCEC 0x14
+#define MT7621_STATS_TSCEC 0x18
+#define MT7621_STATS_TMCEC 0x1C
+#define MT7621_STATS_TDEC 0x20
+#define MT7621_STATS_TLCEC 0x24
+#define MT7621_STATS_TXCEC 0x28
+#define MT7621_STATS_TPPC 0x2C
+#define MT7621_STATS_TL64PC 0x30
+#define MT7621_STATS_TL65PC 0x34
+#define MT7621_STATS_TL128PC 0x38
+#define MT7621_STATS_TL256PC 0x3C
+#define MT7621_STATS_TL512PC 0x40
+#define MT7621_STATS_TL1024PC 0x44
+#define MT7621_STATS_TOC 0x48
+#define MT7621_STATS_RDPC 0x60
+#define MT7621_STATS_RFPC 0x64
+#define MT7621_STATS_RUPC 0x68
+#define MT7621_STATS_RMPC 0x6C
+#define MT7621_STATS_RBPC 0x70
+#define MT7621_STATS_RAEPC 0x74
+#define MT7621_STATS_RCEPC 0x78
+#define MT7621_STATS_RUSPC 0x7C
+#define MT7621_STATS_RFEPC 0x80
+#define MT7621_STATS_ROSPC 0x84
+#define MT7621_STATS_RJEPC 0x88
+#define MT7621_STATS_RPPC 0x8C
+#define MT7621_STATS_RL64PC 0x90
+#define MT7621_STATS_RL65PC 0x94
+#define MT7621_STATS_RL128PC 0x98
+#define MT7621_STATS_RL256PC 0x9C
+#define MT7621_STATS_RL512PC 0xA0
+#define MT7621_STATS_RL1024PC 0xA4
+#define MT7621_STATS_ROC 0xA8
+#define MT7621_STATS_RDPC_CTRL 0xB0
+#define MT7621_STATS_RDPC_ING 0xB4
+#define MT7621_STATS_RDPC_ARL 0xB8
+
+static const struct mt7xxx_mib_desc mt7621_mibs[] = {
+ MIB_DESC(1, MT7621_STATS_TDPC, "TxDrop"),
+ MIB_DESC(1, MT7621_STATS_TCRC, "TxCRC"),
+ MIB_DESC(1, MT7621_STATS_TUPC, "TxUni"),
+ MIB_DESC(1, MT7621_STATS_TMPC, "TxMulti"),
+ MIB_DESC(1, MT7621_STATS_TBPC, "TxBroad"),
+ MIB_DESC(1, MT7621_STATS_TCEC, "TxCollision"),
+ MIB_DESC(1, MT7621_STATS_TSCEC, "TxSingleCol"),
+ MIB_DESC(1, MT7621_STATS_TMCEC, "TxMultiCol"),
+ MIB_DESC(1, MT7621_STATS_TDEC, "TxDefer"),
+ MIB_DESC(1, MT7621_STATS_TLCEC, "TxLateCol"),
+ MIB_DESC(1, MT7621_STATS_TXCEC, "TxExcCol"),
+ MIB_DESC(1, MT7621_STATS_TPPC, "TxPause"),
+ MIB_DESC(1, MT7621_STATS_TL64PC, "Tx64Byte"),
+ MIB_DESC(1, MT7621_STATS_TL65PC, "Tx65Byte"),
+ MIB_DESC(1, MT7621_STATS_TL128PC, "Tx128Byte"),
+ MIB_DESC(1, MT7621_STATS_TL256PC, "Tx256Byte"),
+ MIB_DESC(1, MT7621_STATS_TL512PC, "Tx512Byte"),
+ MIB_DESC(1, MT7621_STATS_TL1024PC, "Tx1024Byte"),
+ MIB_DESC(2, MT7621_STATS_TOC, "TxByte"),
+ MIB_DESC(1, MT7621_STATS_RDPC, "RxDrop"),
+ MIB_DESC(1, MT7621_STATS_RFPC, "RxFiltered"),
+ MIB_DESC(1, MT7621_STATS_RUPC, "RxUni"),
+ MIB_DESC(1, MT7621_STATS_RMPC, "RxMulti"),
+ MIB_DESC(1, MT7621_STATS_RBPC, "RxBroad"),
+ MIB_DESC(1, MT7621_STATS_RAEPC, "RxAlignErr"),
+ MIB_DESC(1, MT7621_STATS_RCEPC, "RxCRC"),
+ MIB_DESC(1, MT7621_STATS_RUSPC, "RxUnderSize"),
+ MIB_DESC(1, MT7621_STATS_RFEPC, "RxFragment"),
+ MIB_DESC(1, MT7621_STATS_ROSPC, "RxOverSize"),
+ MIB_DESC(1, MT7621_STATS_RJEPC, "RxJabber"),
+ MIB_DESC(1, MT7621_STATS_RPPC, "RxPause"),
+ MIB_DESC(1, MT7621_STATS_RL64PC, "Rx64Byte"),
+ MIB_DESC(1, MT7621_STATS_RL65PC, "Rx65Byte"),
+ MIB_DESC(1, MT7621_STATS_RL128PC, "Rx128Byte"),
+ MIB_DESC(1, MT7621_STATS_RL256PC, "Rx256Byte"),
+ MIB_DESC(1, MT7621_STATS_RL512PC, "Rx512Byte"),
+ MIB_DESC(1, MT7621_STATS_RL1024PC, "Rx1024Byte"),
+ MIB_DESC(2, MT7621_STATS_ROC, "RxByte"),
+ MIB_DESC(1, MT7621_STATS_RDPC_CTRL, "RxCtrlDrop"),
+ MIB_DESC(1, MT7621_STATS_RDPC_ING, "RxIngDrop"),
+ MIB_DESC(1, MT7621_STATS_RDPC_ARL, "RxARLDrop")
+};
+
+enum {
+ /* Global attributes. */
+ MT7530_ATTR_ENABLE_VLAN,
+};
+
+struct mt7530_port_entry {
+ u16 pvid;
+};
+
+struct mt7530_vlan_entry {
+ u16 vid;
+ u8 member;
+ u8 etags;
+};
+
+struct mt7530_priv {
+ void __iomem *base;
+ struct mii_bus *bus;
+ struct switch_dev swdev;
+
+ bool global_vlan_enable;
+ struct mt7530_vlan_entry vlan_entries[MT7530_NUM_VLANS];
+ struct mt7530_port_entry port_entries[MT7530_NUM_PORTS];
+};
+
+struct mt7530_mapping {
+ char *name;
+ u16 pvids[MT7530_NUM_PORTS];
+ u8 members[MT7530_NUM_VLANS];
+ u8 etags[MT7530_NUM_VLANS];
+ u16 vids[MT7530_NUM_VLANS];
+} mt7530_defaults[] = {
+ {
+ .name = "llllw",
+ .pvids = { 1, 1, 1, 1, 2, 1, 1 },
+ .members = { 0, 0x6f, 0x50 },
+ .etags = { 0, 0x40, 0x40 },
+ .vids = { 0, 1, 2 },
+ }, {
+ .name = "wllll",
+ .pvids = { 2, 1, 1, 1, 1, 1, 1 },
+ .members = { 0, 0x7e, 0x41 },
+ .etags = { 0, 0x40, 0x40 },
+ .vids = { 0, 1, 2 },
+ },
+};
+
+struct mt7530_mapping*
+mt7530_find_mapping(struct device_node *np)
+{
+ const char *map;
+ int i;
+
+ if (of_property_read_string(np, "mediatek,portmap", &map))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(mt7530_defaults); i++)
+ if (!strcmp(map, mt7530_defaults[i].name))
+ return &mt7530_defaults[i];
+
+ return NULL;
+}
+
+static void
+mt7530_apply_mapping(struct mt7530_priv *mt7530, struct mt7530_mapping *map)
+{
+ int i = 0;
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530->port_entries[i].pvid = map->pvids[i];
+
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
+ mt7530->vlan_entries[i].member = map->members[i];
+ mt7530->vlan_entries[i].etags = map->etags[i];
+ mt7530->vlan_entries[i].vid = map->vids[i];
+ }
+}
+
+static int
+mt7530_reset_switch(struct switch_dev *dev)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ int i;
+
+ memset(priv->port_entries, 0, sizeof(priv->port_entries));
+ memset(priv->vlan_entries, 0, sizeof(priv->vlan_entries));
+
+ /* set default vid of each vlan to the same number of vlan, so the vid
+ * won't need be set explicitly.
+ */
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
+ priv->vlan_entries[i].vid = i;
+ }
+
+ return 0;
+}
+
+static int
+mt7530_get_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+
+ val->value.i = priv->global_vlan_enable;
+
+ return 0;
+}
+
+static int
+mt7530_set_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+
+ priv->global_vlan_enable = val->value.i != 0;
+
+ return 0;
+}
+
+static u32
+mt7530_r32(struct mt7530_priv *priv, u32 reg)
+{
+ u32 val;
+ if (priv->bus) {
+ u16 high, low;
+
+ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ low = mdiobus_read(priv->bus, 0x1f, (reg >> 2) & 0xf);
+ high = mdiobus_read(priv->bus, 0x1f, 0x10);
+
+ return (high << 16) | (low & 0xffff);
+ }
+
+ val = ioread32(priv->base + reg);
+ pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
+
+ return val;
+}
+
+static void
+mt7530_w32(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ if (priv->bus) {
+ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ mdiobus_write(priv->bus, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
+ mdiobus_write(priv->bus, 0x1f, 0x10, val >> 16);
+ return;
+ }
+
+ pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
+ iowrite32(val, priv->base + reg);
+}
+
+static void
+mt7530_vtcr(struct mt7530_priv *priv, u32 cmd, u32 val)
+{
+ int i;
+
+ mt7530_w32(priv, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
+
+ for (i = 0; i < 20; i++) {
+ u32 val = mt7530_r32(priv, REG_ESW_VLAN_VTCR);
+
+ if ((val & BIT(31)) == 0)
+ break;
+
+ udelay(1000);
+ }
+ if (i == 20)
+ printk("mt7530: vtcr timeout\n");
+}
+
+static int
+mt7530_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+
+ if (port >= MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ *val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(port));
+ *val &= 0xfff;
+
+ return 0;
+}
+
+static int
+mt7530_set_port_pvid(struct switch_dev *dev, int port, int pvid)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+
+ if (port >= MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ if (pvid < MT7530_MIN_VID || pvid > MT7530_MAX_VID)
+ return -EINVAL;
+
+ priv->port_entries[port].pvid = pvid;
+
+ return 0;
+}
+
+static int
+mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ u32 member;
+ u32 etags;
+ int i;
+
+ val->len = 0;
+
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS)
+ return -EINVAL;
+
+ mt7530_vtcr(priv, 0, val->port_vlan);
+
+ member = mt7530_r32(priv, REG_ESW_VLAN_VAWD1);
+ member >>= 16;
+ member &= 0xff;
+
+ etags = mt7530_r32(priv, REG_ESW_VLAN_VAWD2);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ struct switch_port *p;
+ int etag;
+
+ if (!(member & BIT(i)))
+ continue;
+
+ p = &val->value.ports[val->len++];
+ p->id = i;
+
+ etag = (etags >> (i * 2)) & 0x3;
+
+ if (etag == ETAG_CTRL_TAG)
+ p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED);
+ else if (etag != ETAG_CTRL_UNTAG)
+ printk("vlan egress tag control neither untag nor tag.\n");
+ }
+
+ return 0;
+}
+
+static int
+mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ u8 member = 0;
+ u8 etags = 0;
+ int i;
+
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS ||
+ val->len > MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ for (i = 0; i < val->len; i++) {
+ struct switch_port *p = &val->value.ports[i];
+
+ if (p->id >= MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ member |= BIT(p->id);
+
+ if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED))
+ etags |= BIT(p->id);
+ }
+ priv->vlan_entries[val->port_vlan].member = member;
+ priv->vlan_entries[val->port_vlan].etags = etags;
+
+ return 0;
+}
+
+static int
+mt7530_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ int vlan;
+ u16 vid;
+
+ vlan = val->port_vlan;
+ vid = (u16)val->value.i;
+
+ if (vlan < 0 || vlan >= MT7530_NUM_VLANS)
+ return -EINVAL;
+
+ if (vid < MT7530_MIN_VID || vid > MT7530_MAX_VID)
+ return -EINVAL;
+
+ priv->vlan_entries[vlan].vid = vid;
+ return 0;
+}
+
+static int
+mt7530_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ u32 vid;
+ int vlan;
+
+ vlan = val->port_vlan;
+
+ vid = mt7530_r32(priv, REG_ESW_VLAN_VTIM(vlan));
+ if (vlan & 1)
+ vid = vid >> 12;
+ vid &= 0xfff;
+
+ val->value.i = vid;
+ return 0;
+}
+
+static int
+mt7530_apply_config(struct switch_dev *dev)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ int i, j;
+ u8 tag_ports;
+ u8 untag_ports;
+
+ if (!priv->global_vlan_enable) {
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00ff0000);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(priv, REG_ESW_PORT_PVC(i), 0x810000c0);
+
+ return 0;
+ }
+
+ /* set all ports as security mode */
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00ff0003);
+
+ /* check if a port is used in tag/untag vlan egress mode */
+ tag_ports = 0;
+ untag_ports = 0;
+
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
+ u8 member = priv->vlan_entries[i].member;
+ u8 etags = priv->vlan_entries[i].etags;
+
+ if (!member)
+ continue;
+
+ for (j = 0; j < MT7530_NUM_PORTS; j++) {
+ if (!(member & BIT(j)))
+ continue;
+
+ if (etags & BIT(j))
+ tag_ports |= 1u << j;
+ else
+ untag_ports |= 1u << j;
+ }
+ }
+
+ /* set all untag-only ports as transparent and the rest as user port */
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ u32 pvc_mode = 0x81000000;
+
+ if (untag_ports & BIT(i) && !(tag_ports & BIT(i)))
+ pvc_mode = 0x810000c0;
+
+ mt7530_w32(priv, REG_ESW_PORT_PVC(i), pvc_mode);
+ }
+
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
+ u16 vid = priv->vlan_entries[i].vid;
+ u8 member = priv->vlan_entries[i].member;
+ u8 etags = priv->vlan_entries[i].etags;
+ u32 val;
+
+ /* vid of vlan */
+ val = mt7530_r32(priv, REG_ESW_VLAN_VTIM(i));
+ if (i % 2 == 0) {
+ val &= 0xfff000;
+ val |= vid;
+ } else {
+ val &= 0xfff;
+ val |= (vid << 12);
+ }
+ mt7530_w32(priv, REG_ESW_VLAN_VTIM(i), val);
+
+ /* vlan port membership */
+ if (member)
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, REG_ESW_VLAN_VAWD1_IVL_MAC |
+ REG_ESW_VLAN_VAWD1_VTAG_EN | (member << 16) |
+ REG_ESW_VLAN_VAWD1_VALID);
+ else
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, 0);
+
+ /* egress mode */
+ val = 0;
+ for (j = 0; j < MT7530_NUM_PORTS; j++) {
+ if (etags & BIT(j))
+ val |= ETAG_CTRL_TAG << (j * 2);
+ else
+ val |= ETAG_CTRL_UNTAG << (j * 2);
+ }
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD2, val);
+
+ /* write to vlan table */
+ mt7530_vtcr(priv, 1, i);
+ }
+
+ /* Port Default PVID */
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ u32 val;
+ val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(i));
+ val &= ~0xfff;
+ val |= priv->port_entries[i].pvid;
+ mt7530_w32(priv, REG_ESW_PORT_PPBV1(i), val);
+ }
+
+ return 0;
+}
+
+static int
+mt7530_get_port_link(struct switch_dev *dev, int port,
+ struct switch_port_link *link)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ u32 speed, pmsr;
+
+ if (port < 0 || port >= MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ pmsr = mt7530_r32(priv, 0x3008 + (0x100 * port));
+
+ link->link = pmsr & 1;
+ link->duplex = (pmsr >> 1) & 1;
+ speed = (pmsr >> 2) & 3;
+
+ switch (speed) {
+ case 0:
+ link->speed = SWITCH_PORT_SPEED_10;
+ break;
+ case 1:
+ link->speed = SWITCH_PORT_SPEED_100;
+ break;
+ case 2:
+ case 3: /* forced gige speed can be 2 or 3 */
+ link->speed = SWITCH_PORT_SPEED_1000;
+ break;
+ default:
+ link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct switch_attr mt7530_global[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "VLAN mode (1:enabled)",
+ .max = 1,
+ .id = MT7530_ATTR_ENABLE_VLAN,
+ .get = mt7530_get_vlan_enable,
+ .set = mt7530_set_vlan_enable,
+ },
+};
+
+static u64 get_mib_counter(struct mt7530_priv *priv, int i, int port)
+{
+ unsigned int port_base;
+ u64 t;
+
+ port_base = MT7621_MIB_COUNTER_BASE +
+ MT7621_MIB_COUNTER_PORT_OFFSET * port;
+
+ t = mt7530_r32(priv, port_base + mt7621_mibs[i].offset);
+ if (mt7621_mibs[i].size == 2) {
+ u64 hi;
+
+ hi = mt7530_r32(priv, port_base + mt7621_mibs[i].offset + 4);
+ t |= hi << 32;
+ }
+
+ return t;
+}
+
+static int mt7621_sw_get_port_mib(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ static char buf[4096];
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ int i, len = 0;
+
+ if (val->port_vlan >= MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "Port %d MIB counters\n", val->port_vlan);
+
+ for (i = 0; i < sizeof(mt7621_mibs) / sizeof(*mt7621_mibs); ++i) {
+ u64 counter;
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%-11s: ", mt7621_mibs[i].name);
+ counter = get_mib_counter(priv, i, val->port_vlan);
+ len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
+ counter);
+ }
+
+ val->value.s = buf;
+ val->len = len;
+ return 0;
+}
+
+static const struct switch_attr mt7621_port[] = {
+ {
+ .type = SWITCH_TYPE_STRING,
+ .name = "mib",
+ .description = "Get MIB counters for port",
+ .get = mt7621_sw_get_port_mib,
+ .set = NULL,
+ },
+};
+
+static const struct switch_attr mt7530_port[] = {
+};
+
+static const struct switch_attr mt7530_vlan[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "vid",
+ .description = "VLAN ID (0-4094)",
+ .set = mt7530_set_vid,
+ .get = mt7530_get_vid,
+ .max = 4094,
+ },
+};
+
+static const struct switch_dev_ops mt7621_ops = {
+ .attr_global = {
+ .attr = mt7530_global,
+ .n_attr = ARRAY_SIZE(mt7530_global),
+ },
+ .attr_port = {
+ .attr = mt7621_port,
+ .n_attr = ARRAY_SIZE(mt7621_port),
+ },
+ .attr_vlan = {
+ .attr = mt7530_vlan,
+ .n_attr = ARRAY_SIZE(mt7530_vlan),
+ },
+ .get_vlan_ports = mt7530_get_vlan_ports,
+ .set_vlan_ports = mt7530_set_vlan_ports,
+ .get_port_pvid = mt7530_get_port_pvid,
+ .set_port_pvid = mt7530_set_port_pvid,
+ .get_port_link = mt7530_get_port_link,
+ .apply_config = mt7530_apply_config,
+ .reset_switch = mt7530_reset_switch,
+};
+
+static const struct switch_dev_ops mt7530_ops = {
+ .attr_global = {
+ .attr = mt7530_global,
+ .n_attr = ARRAY_SIZE(mt7530_global),
+ },
+ .attr_port = {
+ .attr = mt7530_port,
+ .n_attr = ARRAY_SIZE(mt7530_port),
+ },
+ .attr_vlan = {
+ .attr = mt7530_vlan,
+ .n_attr = ARRAY_SIZE(mt7530_vlan),
+ },
+ .get_vlan_ports = mt7530_get_vlan_ports,
+ .set_vlan_ports = mt7530_set_vlan_ports,
+ .get_port_pvid = mt7530_get_port_pvid,
+ .set_port_pvid = mt7530_set_port_pvid,
+ .get_port_link = mt7530_get_port_link,
+ .apply_config = mt7530_apply_config,
+ .reset_switch = mt7530_reset_switch,
+};
+
+int
+mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan)
+{
+ struct switch_dev *swdev;
+ struct mt7530_priv *mt7530;
+ struct mt7530_mapping *map;
+ int ret;
+
+ mt7530 = devm_kzalloc(dev, sizeof(struct mt7530_priv), GFP_KERNEL);
+ if (!mt7530)
+ return -ENOMEM;
+
+ mt7530->base = base;
+ mt7530->bus = bus;
+ mt7530->global_vlan_enable = vlan;
+
+ swdev = &mt7530->swdev;
+ if (bus) {
+ swdev->alias = "mt7530";
+ swdev->name = "mt7530";
+ } else if (IS_ENABLED(CONFIG_SOC_MT7621)) {
+ swdev->alias = "mt7621";
+ swdev->name = "mt7621";
+ } else {
+ swdev->alias = "mt7620";
+ swdev->name = "mt7620";
+ }
+ swdev->cpu_port = MT7530_CPU_PORT;
+ swdev->ports = MT7530_NUM_PORTS;
+ swdev->vlans = MT7530_NUM_VLANS;
+ if (IS_ENABLED(CONFIG_SOC_MT7621))
+ swdev->ops = &mt7621_ops;
+ else
+ swdev->ops = &mt7530_ops;
+
+ ret = register_switch(swdev, NULL);
+ if (ret) {
+ dev_err(dev, "failed to register mt7530\n");
+ return ret;
+ }
+
+
+ map = mt7530_find_mapping(dev->of_node);
+ if (map)
+ mt7530_apply_mapping(mt7530, map);
+ mt7530_apply_config(swdev);
+
+ /* magic vodoo */
+ if (!IS_ENABLED(CONFIG_SOC_MT7621) && bus && mt7530_r32(mt7530, REG_HWTRAP) != 0x1117edf) {
+ dev_info(dev, "fixing up MHWTRAP register - bootloader probably played with it\n");
+ mt7530_w32(mt7530, REG_HWTRAP, 0x1117edf);
+ }
+ dev_info(dev, "loaded %s driver\n", swdev->name);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mediatek/mt7530.h b/drivers/net/ethernet/mediatek/mt7530.h
new file mode 100644
index 0000000..1fc8c62
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mt7530.h
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef _MT7530_H__
+#define _MT7530_H__
+
+int mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan);
+
+#endif
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index dae7147..6299f87 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1308,8 +1308,13 @@ static int __init fe_init(struct net_device *dev)
}
err = fe_hw_init(dev);
- if (!err)
- return 0;
+ if (err)
+ goto err_phy_disconnect;
+
+ if ((priv->flags & FE_FLAG_HAS_SWITCH) && priv->soc->switch_config)
+ priv->soc->switch_config(priv);
+
+ return 0;
err_phy_disconnect:
if (priv->phy)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index ba5ba07..d5f8b87 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -383,6 +383,7 @@ struct fe_soc_data {
int (*fwd_config)(struct fe_priv *priv);
void (*tx_dma)(struct fe_tx_dma *txd);
int (*switch_init)(struct fe_priv *priv);
+ int (*switch_config)(struct fe_priv *priv);
void (*port_init)(struct fe_priv *priv, struct device_node *port);
int (*has_carrier)(struct fe_priv *priv);
int (*mdio_init)(struct fe_priv *priv);
diff --git a/drivers/net/ethernet/mediatek/soc_mt7620.c b/drivers/net/ethernet/mediatek/soc_mt7620.c
index 9ad6bc9..740dd90 100644
--- a/drivers/net/ethernet/mediatek/soc_mt7620.c
+++ b/drivers/net/ethernet/mediatek/soc_mt7620.c
@@ -313,6 +313,7 @@ static struct fe_soc_data mt7620_data = {
.fwd_config = mt7620_fwd_config,
.tx_dma = mt7620_tx_dma,
.switch_init = mtk_gsw_init,
+ .switch_config = mt7620_gsw_config,
.port_init = mt7620_port_init,
.reg_table = mt7620_reg_table,
.pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS,
--
1.7.10.4

View file

@ -0,0 +1,36 @@
From fce0d89383e0a2a694723989da608df1a464bae4 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 13 Dec 2015 17:45:19 +0100
Subject: [PATCH 1/2] net: mediatek: add of_get_mac_address_mtd() support
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index fb435fa..9999768 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1273,7 +1273,6 @@ static int __init fe_init(struct net_device *dev)
{
struct fe_priv *priv = netdev_priv(dev);
struct device_node *port;
- const char *mac_addr;
int err;
priv->soc->reset_fe();
@@ -1281,9 +1280,7 @@ static int __init fe_init(struct net_device *dev)
if (priv->soc->switch_init)
priv->soc->switch_init(priv);
- mac_addr = of_get_mac_address(priv->device->of_node);
- if (mac_addr)
- ether_addr_copy(dev->dev_addr, mac_addr);
+ of_get_mac_address_mtd(priv->device->of_node, dev->dev_addr);
/*If the mac address is invalid, use random mac address */
if (!is_valid_ether_addr(dev->dev_addr)) {
--
1.7.10.4

View file

@ -0,0 +1,27 @@
From 3c550ae0359be5bc4d5daa86a62f711a91ac8dde Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 13 Dec 2015 17:46:09 +0100
Subject: [PATCH 2/2] net:mediatke: add phy_ethtool_ioctl() support
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 9999768..84b1a43 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1342,6 +1342,9 @@ static int fe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -ENODEV;
switch (cmd) {
+ case SIOCETHTOOL:
+ return phy_ethtool_ioctl(priv->phy_dev,
+ (void *) ifr->ifr_data);
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
--
1.7.10.4