ARCH:=arm
BOARD:=mediatek
BOARDNAME:=Mediatek Ralink ARM
-FEATURES:=squashfs
+FEATURES:=squashfs jffs2
CPU_TYPE:=cortex-a7
MAINTAINER:=John Crispin <john@phrozen.org>
local board="$1"
case $board in
+ eMMC | \
+ NAND | \
mt7623_evb)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
CONFIG_MTD_BLOCK2MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_M25P80=y
+CONFIG_MTD_MT81xx_NOR=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ECC=y
-CONFIG_MTD_NAND_MTKSDG1=y
+CONFIG_MTD_NAND_MTK=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTK_INFRACFG=y
CONFIG_MTK_PMIC_WRAP=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_RCU=y
CONFIG_PRINTK_TIME=y
+CONFIG_PWM=y
+CONFIG_PWM_MEDIATEK=y
+# CONFIG_PWM_MTK_DISP is not set
+CONFIG_PWM_SYSFS=y
CONFIG_RATIONAL=y
CONFIG_RCU_CPU_STALL_TIMEOUT=21
# CONFIG_RCU_EXPERT is not set
--- /dev/null
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/mt2701-clk.h>
+#include <dt-bindings/power/mt2701-power.h>
+#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/reset-controller/mt2701-resets.h>
+#include <dt-bindings/pinctrl/mt7623-pinfunc.h>
+#include "skeleton64.dtsi"
+
+
+/ {
+ compatible = "mediatek,mt7623";
+ interrupt-parent = <&sysirq>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ enable-method = "mediatek,mt6589-smp";
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x0>;
+ clocks = <&infracfg CLK_INFRA_CPUSEL>,
+ <&apmixedsys CLK_APMIXED_MAINPLL>;
+ clock-names = "cpu", "intermediate";
+ operating-points = <
+ 598000 1150000
+ 747500 1150000
+ 1040000 1150000
+ 1196000 1200000
+ 1300000 1300000
+ >;
+ };
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x1>;
+ clocks = <&infracfg CLK_INFRA_CPUSEL>,
+ <&apmixedsys CLK_APMIXED_MAINPLL>;
+ clock-names = "cpu", "intermediate";
+ operating-points = <
+ 598000 1150000
+ 747500 1150000
+ 1040000 1150000
+ 1196000 1200000
+ 1300000 1300000
+ >;
+ };
+ cpu2: cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x2>;
+ clocks = <&infracfg CLK_INFRA_CPUSEL>,
+ <&apmixedsys CLK_APMIXED_MAINPLL>;
+ clock-names = "cpu", "intermediate";
+ operating-points = <
+ 598000 1150000
+ 747500 1150000
+ 1040000 1150000
+ 1196000 1200000
+ 1300000 1300000
+ >;
+ };
+ cpu3: cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x3>;
+ clocks = <&infracfg CLK_INFRA_CPUSEL>,
+ <&apmixedsys CLK_APMIXED_MAINPLL>;
+ clock-names = "cpu", "intermediate";
+ operating-points = <
+ 598000 1150000
+ 747500 1150000
+ 1040000 1150000
+ 1196000 1200000
+ 1300000 1300000
+ >;
+ };
+ };
+
+ system_clk: dummy13m {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ rtc_clk: dummy32k {
+ compatible = "fixed-clock";
+ clock-frequency = <32000>;
+ #clock-cells = <0>;
+ clock-output-names = "clk32k";
+ };
+
+ clk26m: dummy26m {
+ compatible = "fixed-clock";
+ clock-frequency = <26000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clk26m";
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
+ clock-frequency = <13000000>;
+ arm,cpu-registers-not-fw-configured;
+ };
+
+ topckgen: power-controller@10000000 {
+ compatible = "mediatek,mt7623-topckgen",
+ "mediatek,mt2701-topckgen",
+ "syscon";
+ reg = <0 0x10000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ infracfg: power-controller@10001000 {
+ compatible = "mediatek,mt7623-infracfg",
+ "mediatek,mt2701-infracfg",
+ "syscon";
+ reg = <0 0x10001000 0 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ pericfg: pericfg@10003000 {
+ compatible = "mediatek,mt7623-pericfg",
+ "mediatek,mt2701-pericfg",
+ "syscon";
+ reg = <0 0x10003000 0 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ pio: pinctrl@10005000 {
+ compatible = "mediatek,mt7623-pinctrl";
+ reg = <0 0x1000b000 0 0x1000>;
+ mediatek,pctl-regmap = <&syscfg_pctl_a>;
+ pins-are-numbered;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ interrupt-parent = <&gic>;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ syscfg_pctl_a: syscfg@10005000 {
+ compatible = "mediatek,mt7623-pctl-a-syscfg", "syscon";
+ reg = <0 0x10005000 0 0x1000>;
+ };
+
+ scpsys: scpsys@10006000 {
+ #power-domain-cells = <1>;
+ compatible = "mediatek,mt7623-scpsys",
+ "mediatek,mt2701-scpsys";
+ reg = <0 0x10006000 0 0x1000>;
+ infracfg = <&infracfg>;
+ clocks = <&clk26m>,
+ <&topckgen CLK_TOP_MM_SEL>;
+ clock-names = "mfg", "mm";
+ };
+
+ watchdog: watchdog@10007000 {
+ compatible = "mediatek,mt7623-wdt",
+ "mediatek,mt6589-wdt";
+ reg = <0 0x10007000 0 0x100>;
+ };
+
+ timer: timer@10008000 {
+ compatible = "mediatek,mt7623-timer",
+ "mediatek,mt6577-timer";
+ reg = <0 0x10008000 0 0x80>;
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&system_clk>, <&rtc_clk>;
+ clock-names = "system-clk", "rtc-clk";
+ };
+
+ pwrap: pwrap@1000d000 {
+ compatible = "mediatek,mt7623-pwrap",
+ "mediatek,mt2701-pwrap";
+ reg = <0 0x1000d000 0 0x1000>;
+ reg-names = "pwrap";
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
+ resets = <&infracfg MT2701_INFRA_PMIC_WRAP_RST>;
+ reset-names = "pwrap";
+ clocks = <&infracfg CLK_INFRA_PMICSPI>,
+ <&infracfg CLK_INFRA_PMICWRAP>;
+ clock-names = "spi", "wrap";
+ };
+
+ sysirq: interrupt-controller@10200100 {
+ compatible = "mediatek,mt7623-sysirq",
+ "mediatek,mt6577-sysirq";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&gic>;
+ reg = <0 0x10200100 0 0x1c>;
+ };
+
+ apmixedsys: apmixedsys@10209000 {
+ compatible = "mediatek,mt7623-apmixedsys",
+ "mediatek,mt2701-apmixedsys";
+ reg = <0 0x10209000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ gic: interrupt-controller@10211000 {
+ compatible = "arm,cortex-a7-gic";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&gic>;
+ reg = <0 0x10211000 0 0x1000>,
+ <0 0x10212000 0 0x1000>,
+ <0 0x10214000 0 0x2000>,
+ <0 0x10216000 0 0x2000>;
+ };
+
+ i2c0: i2c@11007000 {
+ compatible = "mediatek,mt7623-i2c",
+ "mediatek,mt6577-i2c";
+ reg = <0 0x11007000 0 0x70>,
+ <0 0x11000200 0 0x80>;
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_LOW>;
+ clock-div = <16>;
+ clocks = <&pericfg CLK_PERI_I2C0>,
+ <&pericfg CLK_PERI_AP_DMA>;
+ clock-names = "main", "dma";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@11008000 {
+ compatible = "mediatek,mt7623-i2c",
+ "mediatek,mt6577-i2c";
+ reg = <0 0x11008000 0 0x70>,
+ <0 0x11000280 0 0x80>;
+ interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_LOW>;
+ clock-div = <16>;
+ clocks = <&pericfg CLK_PERI_I2C1>,
+ <&pericfg CLK_PERI_AP_DMA>;
+ clock-names = "main", "dma";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@11009000 {
+ compatible = "mediatek,mt7623-i2c",
+ "mediatek,mt6577-i2c";
+ reg = <0 0x11009000 0 0x70>,
+ <0 0x11000300 0 0x80>;
+ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_LOW>;
+ clock-div = <16>;
+ clocks = <&pericfg CLK_PERI_I2C2>,
+ <&pericfg CLK_PERI_AP_DMA>;
+ clock-names = "main", "dma";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ uart0: serial@11002000 {
+ compatible = "mediatek,mt7623-uart",
+ "mediatek,mt6577-uart";
+ reg = <0 0x11002000 0 0x400>;
+ interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_UART0_SEL>,
+ <&pericfg CLK_PERI_UART0>;
+ clock-names = "baud", "bus";
+ status = "disabled";
+ };
+
+ uart1: serial@11003000 {
+ compatible = "mediatek,mt7623-uart",
+ "mediatek,mt6577-uart";
+ reg = <0 0x11003000 0 0x400>;
+ interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_UART1_SEL>,
+ <&pericfg CLK_PERI_UART1>;
+ clock-names = "baud", "bus";
+ status = "disabled";
+ };
+
+ uart2: serial@11004000 {
+ compatible = "mediatek,mt7623-uart",
+ "mediatek,mt6577-uart";
+ reg = <0 0x11004000 0 0x400>;
+ interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_UART2_SEL>,
+ <&pericfg CLK_PERI_UART2>;
+ clock-names = "baud", "bus";
+ status = "disabled";
+ };
+
+ uart3: serial@11005000 {
+ compatible = "mediatek,mt7623-uart",
+ "mediatek,mt6577-uart";
+ reg = <0 0x11005000 0 0x400>;
+ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_UART3_SEL>,
+ <&pericfg CLK_PERI_UART3>;
+ clock-names = "baud", "bus";
+ status = "disabled";
+ };
+
+ pwm: pwm@11006000 {
+ compatible = "mediatek,mt7623-pwm";
+
+ reg = <0 0x11006000 0 0x1000>;
+
+ resets = <&pericfg MT2701_PERI_PWM_SW_RST>;
+ reset-names = "pwm";
+
+ #pwm-cells = <2>;
+ clocks = <&topckgen CLK_TOP_PWM_SEL>,
+ <&pericfg CLK_PERI_PWM>,
+ <&pericfg CLK_PERI_PWM1>,
+ <&pericfg CLK_PERI_PWM2>,
+ <&pericfg CLK_PERI_PWM3>,
+ <&pericfg CLK_PERI_PWM4>,
+ <&pericfg CLK_PERI_PWM5>;
+ clock-names = "top", "main", "pwm1", "pwm2",
+ "pwm3", "pwm4", "pwm5";
+
+ status = "disabled";
+ };
+
+ spi: spi@1100a000 {
+ compatible = "mediatek,mt7623-spi", "mediatek,mt6589-spi";
+ reg = <0 0x1100a000 0 0x1000>;
+ interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_SPI0>;
+ clock-names = "main";
+
+ status = "disabled";
+ };
+
+ nandc: nfi@1100d000 {
+ compatible = "mediatek,mt2701-nfc";
+ reg = <0 0x1100d000 0 0x1000>;
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_IFR_MSC>;
+ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_NFI>,
+ <&pericfg CLK_PERI_NFI_PAD>;
+ clock-names = "nfi_clk", "pad_clk";
+ status = "disabled";
+ ecc-engine = <&bch>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ bch: ecc@1100e000 {
+ compatible = "mediatek,mt2701-ecc";
+ reg = <0 0x1100e000 0 0x1000>;
+ interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_NFI_ECC>;
+ clock-names = "nfiecc_clk";
+ status = "disabled";
+ };
+
+ mmc0: mmc@11230000 {
+ compatible = "mediatek,mt7623-mmc",
+ "mediatek,mt8135-mmc";
+ reg = <0 0x11230000 0 0x1000>;
+ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_MSDC30_0>,
+ <&topckgen CLK_TOP_MSDC30_0_SEL>;
+ clock-names = "source", "hclk";
+ status = "disabled";
+ };
+
+ mmc1: mmc@11240000 {
+ compatible = "mediatek,mt7623-mmc",
+ "mediatek,mt8135-mmc";
+ reg = <0 0x11240000 0 0x1000>;
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_MSDC30_1>,
+ <&topckgen CLK_TOP_MSDC30_1_SEL>;
+ clock-names = "source", "hclk";
+ status = "disabled";
+ };
+
+ usb1: usb@1a1c0000 {
+ compatible = "mediatek,mt2701-xhci",
+ "mediatek,mt8173-xhci";
+ reg = <0 0x1a1c0000 0 0x1000>,
+ <0 0x1a1c4700 0 0x0100>;
+ interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&hifsys CLK_HIFSYS_USB0PHY>,
+ <&topckgen CLK_TOP_ETHIF_SEL>;
+ clock-names = "sys_ck", "ethif";
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
+ phys = <&phy_port0 PHY_TYPE_USB3>;
+ status = "disabled";
+ };
+
+ u3phy1: usb-phy@1a1c4000 {
+ compatible = "mediatek,mt2701-u3phy",
+ "mediatek,mt8173-u3phy";
+ reg = <0 0x1a1c4000 0 0x0700>;
+ clocks = <&clk26m>;
+ clock-names = "u3phya_ref";
+ #phy-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ status = "disabled";
+
+ phy_port0: phy_port0: port@1a1c4800 {
+ reg = <0 0x1a1c4800 0 0x800>;
+ #phy-cells = <1>;
+ status = "okay";
+ };
+ };
+
+ usb2: usb@1a240000 {
+ compatible = "mediatek,mt2701-xhci",
+ "mediatek,mt8173-xhci";
+ reg = <0 0x1a240000 0 0x1000>,
+ <0 0x1a244700 0 0x0100>;
+ interrupts = <GIC_SPI 197 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&hifsys CLK_HIFSYS_USB1PHY>,
+ <&topckgen CLK_TOP_ETHIF_SEL>;
+ clock-names = "sys_ck", "ethif";
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
+ phys = <&u3phy2 0>;
+ status = "disabled";
+ };
+
+ u3phy2: usb-phy@1a244000 {
+ compatible = "mediatek,mt2701-u3phy",
+ "mediatek,mt8173-u3phy";
+ reg = <0 0x1a244000 0 0x0700>,
+ <0 0x1a244800 0 0x0800>;
+ clocks = <&clk26m>;
+ clock-names = "u3phya_ref";
+ #phy-cells = <1>;
+ status = "disabled";
+ };
+
+ hifsys: clock-controller@1a000000 {
+ compatible = "mediatek,mt7623-hifsys",
+ "mediatek,mt2701-hifsys",
+ "syscon";
+ reg = <0 0x1a000000 0 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ pcie: pcie@1a140000 {
+ compatible = "mediatek,mt7623-pcie";
+ device_type = "pci";
+ reg = <0 0x1a140000 0 0x8000>, /* PCI-Express registers */
+ <0 0x1a149000 0 0x1000>, /* PCI-Express PHY0 */
+ <0 0x1a14a000 0 0x1000>, /* PCI-Express PHY1 */
+ <0 0x1a244000 0 0x1000>; /* PCI-Express PHY2 */
+ reg-names = "pcie", "pcie phy0", "pcie phy1", "pcie phy2";
+ interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 195 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "pcie0", "pcie1", "pcie2";
+ clocks = <&topckgen CLK_TOP_ETHIF_SEL>;
+ clock-names = "pcie";
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
+ resets = <&hifsys MT2701_HIFSYS_PCIE0_RST>,
+ <&hifsys MT2701_HIFSYS_PCIE1_RST>,
+ <&hifsys MT2701_HIFSYS_PCIE2_RST>;
+ reset-names = "pcie0", "pcie1", "pcie2";
+
+ mediatek,hifsys = <&hifsys>;
+
+ bus-range = <0x00 0xff>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ ranges = <0x81000000 0 0x1a160000 0 0x1a160000 0 0x00010000 /* io space */
+ 0x83000000 0 0x60000000 0 0x60000000 0 0x10000000>; /* pci memory */
+
+ status = "disabled";
+
+ pcie@1,0 {
+ device_type = "pci";
+ reg = <0x0800 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+
+ pcie@2,0{
+ device_type = "pci";
+ reg = <0x1000 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+
+ pcie@3,0{
+ device_type = "pci";
+ reg = <0x1800 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
+
+ ethsys: syscon@1b000000 {
+ compatible = "mediatek,mt2701-ethsys", "syscon";
+ reg = <0 0x1b000000 0 0x1000>;
+ #reset-cells = <1>;
+ #clock-cells = <1>;
+ };
+
+ eth: ethernet@1b100000 {
+ compatible = "mediatek,mt7623-eth";
+ reg = <0 0x1b100000 0 0x20000>;
+
+ clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
+ <ðsys CLK_ETHSYS_ESW>,
+ <ðsys CLK_ETHSYS_GP2>,
+ <ðsys CLK_ETHSYS_GP1>;
+ clock-names = "ethif", "esw", "gp2", "gp1";
+ interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW
+ GIC_SPI 199 IRQ_TYPE_LEVEL_LOW
+ GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>;
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
+
+ resets = <ðsys 6>;
+ reset-names = "eth";
+
+ mediatek,ethsys = <ðsys>;
+ mediatek,pctl = <&syscfg_pctl_a>;
+
+ mediatek,switch = <&gsw>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ status = "disabled";
+
+ gmac1: mac@0 {
+ compatible = "mediatek,eth-mac";
+ reg = <0>;
+
+ status = "disabled";
+
+ phy-mode = "rgmii";
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ pause;
+ };
+ };
+
+ gmac2: mac@1 {
+ compatible = "mediatek,eth-mac";
+ reg = <1>;
+
+ status = "disabled";
+ };
+
+ mdio-bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy5: ethernet-phy@5 {
+ reg = <5>;
+ phy-mode = "rgmii-rxid";
+ };
+
+ phy1f: ethernet-phy@1f {
+ reg = <0x1f>;
+ phy-mode = "rgmii";
+ };
+ };
+ };
+
+ gsw: switch@1b100000 {
+ compatible = "mediatek,mt7623-gsw";
+ interrupt-parent = <&pio>;
+ interrupts = <168 IRQ_TYPE_EDGE_RISING>;
+ resets = <ðsys 2>;
+ reset-names = "eth";
+ clocks = <&apmixedsys CLK_APMIXED_TRGPLL>;
+ clock-names = "trgpll";
+ mt7530-supply = <&mt6323_vpa_reg>;
+ mediatek,pctl-regmap = <&syscfg_pctl_a>;
+ mediatek,ethsys = <ðsys>;
+ status = "disabled";
+ };
+};
/dts-v1/;
-#include "mt7623.dtsi"
+#include "_mt7623.dtsi"
#include <dt-bindings/gpio/gpio.h>
/ {
- model = "MediaTek MT7623 evaluation board";
+ model = "MediaTek MT7623 NAND evaluation board";
compatible = "mediatek,mt7623-evb", "mediatek,mt7623";
chosen {
output-low;
};
};
+
+ pwm_pins: pwm {
+ pins_pwm1 {
+ pinmux = <MT7623_PIN_204_PWM1_FUNC_PWM1>;
+ };
+
+ pins_pwm2 {
+ pinmux = <MT7623_PIN_205_PWM2_FUNC_PWM2>;
+ };
+ };
};
&nandc {
&gmac2 {
mac-address = [00 11 22 33 44 55];
status = "okay";
+
+ phy-mode = "rgmii";
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ pause;
+ };
};
&gsw {
mediatek,reset-pin = <&pio 15 0>;
status = "okay";
};
+
+&pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm_pins>;
+ status = "okay";
+};
/dts-v1/;
-#include "mt7623.dtsi"
+#include "_mt7623.dtsi"
#include <dt-bindings/gpio/gpio.h>
/ {
output-low;
};
};
+
+ pwm_pins: pwm {
+ pins_pwm1 {
+ pinmux = <MT7623_PIN_204_PWM1_FUNC_PWM1>;
+ };
+
+ pins_pwm2 {
+ pinmux = <MT7623_PIN_205_PWM2_FUNC_PWM2>;
+ };
+ };
};
&usb1 {
&gmac2 {
mac-address = [00 11 22 33 44 55];
status = "okay";
+ phy-handle = <&phy5>;
};
&gsw {
mediatek,reset-pin = <&pio 15 0>;
status = "okay";
};
+
+&pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm_pins>;
+ status = "okay";
+};
endif
mkdir -p "$(KDIR_TMP)/sysupgrade-$(1)/"
echo "BOARD=$(1)" > "$(KDIR_TMP)/sysupgrade-$(1)/CONTROL"
- $(CP) "$(KDIR)/root.squashfs" "$(KDIR_TMP)/sysupgrade-$(1)/root"
+ $(CP) "$(KDIR)/root.$(2)" "$(KDIR_TMP)/sysupgrade-$(1)/root"
$(CP) "$(KDIR)/uImage-$(1)" "$(KDIR_TMP)/sysupgrade-$(1)/kernel"
(cd "$(KDIR_TMP)"; $(TAR) cvf \
"$(BIN_DIR)/$(IMG_PREFIX)-$(1)-sysupgrade.tar" sysupgrade-$(1) \
$(call prepare_generic_squashfs,$(KDIR)/root.squashfs)
$(CP) $(KDIR)/root.squashfs $(BIN_DIR)/$(IMG_PREFIX)-root.squashfs
- $(call Image/Build/SysupgradeCombined,eMMC)
- $(call Image/Build/SysupgradeCombined,NAND)
+ $(call Image/Build/SysupgradeCombined,eMMC,squashfs)
+endef
+
+define Image/Build/jffs2-128k
+ $(CP) $(KDIR)/root.jffs2-128k $(BIN_DIR)/$(IMG_PREFIX)-root.jffs2
+
+ $(call Image/Build/SysupgradeCombined,NAND,jffs2-128k)
endef
define Image/Build
-From c30a296646a42302065ba452abe95b0b4b550883 Mon Sep 17 00:00:00 2001
+From 1e021917e634b173d466bf0dd3d2ae84e51a77ff Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 27 Jul 2014 09:38:50 +0100
-Subject: [PATCH 01/91] NET: multi phy support
+Subject: [PATCH 001/102] NET: multi phy support
Signed-off-by: John Crispin <blogic@openwrt.org>
---
include/linux/phy.h | 1 +
2 files changed, 7 insertions(+), 3 deletions(-)
+diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
+index 47cd306d..f69d12f 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
-@@ -888,7 +888,8 @@ void phy_state_machine(struct work_struc
+@@ -844,7 +844,8 @@ void phy_state_machine(struct work_struct *work)
/* If the link is down, give up on negotiation for now */
if (!phydev->link) {
phydev->state = PHY_NOLINK;
phydev->adjust_link(phydev->attached_dev);
break;
}
-@@ -971,7 +972,8 @@ void phy_state_machine(struct work_struc
+@@ -927,7 +928,8 @@ void phy_state_machine(struct work_struct *work)
netif_carrier_on(phydev->attached_dev);
} else {
phydev->state = PHY_NOLINK;
}
phydev->adjust_link(phydev->attached_dev);
-@@ -983,7 +985,8 @@ void phy_state_machine(struct work_struc
+@@ -939,7 +941,8 @@ void phy_state_machine(struct work_struct *work)
case PHY_HALTED:
if (phydev->link) {
phydev->link = 0;
phydev->adjust_link(phydev->attached_dev);
do_suspend = true;
}
+diff --git a/include/linux/phy.h b/include/linux/phy.h
+index 05fde31..276ab8a 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -377,6 +377,7 @@ struct phy_device {
enum phy_state state;
+--
+1.7.10.4
+
-From 2c93328ed05061a50e3bd4111379dbcf6946d3ac Mon Sep 17 00:00:00 2001
+From 1892fcf687116720d07135c83d489a23ec56a166 Mon Sep 17 00:00:00 2001
From: James Liao <jamesjj.liao@mediatek.com>
Date: Wed, 30 Dec 2015 14:41:43 +0800
-Subject: [PATCH 02/91] soc: mediatek: Separate scpsys driver common code
+Subject: [PATCH 002/102] soc: mediatek: Separate scpsys driver common code
Separate scpsys driver common code to mtk-scpsys.c, and move MT8173
platform code to mtk-scpsys-mt8173.c.
create mode 100644 drivers/soc/mediatek/mtk-scpsys-mt8173.c
create mode 100644 drivers/soc/mediatek/mtk-scpsys.h
+diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
+index 0a4ea80..eca6fb7 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -22,11 +22,20 @@ config MTK_PMIC_WRAP
+ driver.
+ The System Control Processor System (SCPSYS) has several power
+ management related tasks in the system.
+diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
+index 12998b0..3b22baa 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
+obj-$(CONFIG_MTK_SCPSYS_MT8173) += mtk-scpsys-mt8173.o
+diff --git a/drivers/soc/mediatek/mtk-scpsys-mt8173.c b/drivers/soc/mediatek/mtk-scpsys-mt8173.c
+new file mode 100644
+index 0000000..3c7b569
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-scpsys-mt8173.c
@@ -0,0 +1,179 @@
+};
+
+module_platform_driver_probe(scpsys_drv, scpsys_probe);
+diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
+index 4d4203c..a0943c5 100644
--- a/drivers/soc/mediatek/mtk-scpsys.c
+++ b/drivers/soc/mediatek/mtk-scpsys.c
@@ -11,28 +11,14 @@
-#include <linux/regmap.h>
#include <linux/soc/mediatek/infracfg.h>
-#include <dt-bindings/power/mt8173-power.h>
-
+-
-#define SPM_VDE_PWR_CON 0x0210
-#define SPM_MFG_PWR_CON 0x0214
-#define SPM_VEN_PWR_CON 0x0230
-#define SPM_MFG_2D_PWR_CON 0x02c0
-#define SPM_MFG_ASYNC_PWR_CON 0x02c4
-#define SPM_USB_PWR_CON 0x02cc
++
+#include "mtk-scpsys.h"
+
#define SPM_PWR_STATUS 0x060c
static int scpsys_domain_is_on(struct scp_domain *scpd)
{
struct scp *scp = scpd->scp;
-@@ -398,63 +237,89 @@ static bool scpsys_active_wakeup(struct
+@@ -398,63 +237,89 @@ static bool scpsys_active_wakeup(struct device *dev)
return scpd->active_wakeup;
}
+ return ERR_PTR(-ENOMEM);
+
+ pd_data = &scp->pd_data;
-
-- for (i = 0; i < NUM_DOMAINS; i++) {
++
+ pd_data->domains = devm_kzalloc(&pdev->dev,
+ sizeof(*pd_data->domains) * num, GFP_KERNEL);
+ if (!pd_data->domains)
+ return ERR_PTR(-ENOMEM);
-+
+
+- for (i = 0; i < NUM_DOMAINS; i++) {
+ pd_data->num_domains = num;
+
+ init_clks(pdev, clk);
pd_data->domains[i] = genpd;
scpd->scp = scp;
-@@ -464,13 +329,25 @@ static int __init scpsys_probe(struct pl
+@@ -464,13 +329,25 @@ static int __init scpsys_probe(struct platform_device *pdev)
scpd->sram_pdn_ack_bits = data->sram_pdn_ack_bits;
scpd->bus_prot_mask = data->bus_prot_mask;
scpd->active_wakeup = data->active_wakeup;
/*
* Initially turn on all domains to make the domains usable
-@@ -489,37 +366,9 @@ static int __init scpsys_probe(struct pl
+@@ -489,37 +366,9 @@ static int __init scpsys_probe(struct platform_device *pdev)
* valid.
*/
-};
-
-module_platform_driver_probe(scpsys_drv, scpsys_probe);
+diff --git a/drivers/soc/mediatek/mtk-scpsys.h b/drivers/soc/mediatek/mtk-scpsys.h
+new file mode 100644
+index 0000000..466728d
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-scpsys.h
@@ -0,0 +1,54 @@
+ struct scp *scp, int num);
+
+#endif /* __DRV_SOC_MTK_H */
+--
+1.7.10.4
+
-From c359272f86805259c5801385d60fdeea9d629cf9 Mon Sep 17 00:00:00 2001
+From 6f87948c3a58f02f6a64eadda719317016739d5e Mon Sep 17 00:00:00 2001
From: James Liao <jamesjj.liao@mediatek.com>
Date: Wed, 30 Dec 2015 14:41:44 +0800
-Subject: [PATCH 03/91] soc: mediatek: Init MT8173 scpsys driver earlier
+Subject: [PATCH 003/102] soc: mediatek: Init MT8173 scpsys driver earlier
Some power domain comsumers may init before module_init.
So the power domain provider (scpsys) need to be initialized
drivers/soc/mediatek/mtk-scpsys-mt8173.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
+diff --git a/drivers/soc/mediatek/mtk-scpsys-mt8173.c b/drivers/soc/mediatek/mtk-scpsys-mt8173.c
+index 3c7b569..827e696 100644
--- a/drivers/soc/mediatek/mtk-scpsys-mt8173.c
+++ b/drivers/soc/mediatek/mtk-scpsys-mt8173.c
-@@ -176,4 +176,15 @@ static struct platform_driver scpsys_drv
+@@ -176,4 +176,15 @@ static struct platform_driver scpsys_drv = {
},
};
+
+subsys_initcall(scpsys_drv_init);
+module_exit(scpsys_drv_exit);
+--
+1.7.10.4
+
-From f371844374fff273f817d6c43f679606417af59e Mon Sep 17 00:00:00 2001
+From 7c5b29de78f1b15c5bde40a6ca4510fc09588457 Mon Sep 17 00:00:00 2001
From: Shunli Wang <shunli.wang@mediatek.com>
Date: Wed, 30 Dec 2015 14:41:45 +0800
-Subject: [PATCH 04/91] soc: mediatek: Add MT2701 power dt-bindings
+Subject: [PATCH 004/102] soc: mediatek: Add MT2701 power dt-bindings
Add power dt-bindings for MT2701.
1 file changed, 27 insertions(+)
create mode 100644 include/dt-bindings/power/mt2701-power.h
+diff --git a/include/dt-bindings/power/mt2701-power.h b/include/dt-bindings/power/mt2701-power.h
+new file mode 100644
+index 0000000..64cc826
--- /dev/null
+++ b/include/dt-bindings/power/mt2701-power.h
@@ -0,0 +1,27 @@
+#define MT2701_POWER_DOMAIN_IFR_MSC 8
+
+#endif /* _DT_BINDINGS_POWER_MT2701_POWER_H */
+--
+1.7.10.4
+
-From c6711565985f359d7d3c05f01f081e4c216902de Mon Sep 17 00:00:00 2001
+From 8aa49d107d8a22fd6cbf37174614baf32d0976e2 Mon Sep 17 00:00:00 2001
From: Shunli Wang <shunli.wang@mediatek.com>
Date: Wed, 30 Dec 2015 14:41:46 +0800
-Subject: [PATCH 05/91] soc: mediatek: Add MT2701/MT7623 scpsys driver
+Subject: [PATCH 005/102] soc: mediatek: Add MT2701/MT7623 scpsys driver
Add scpsys driver for MT2701 and MT7623.
3 files changed, 173 insertions(+)
create mode 100644 drivers/soc/mediatek/mtk-scpsys-mt2701.c
+diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
+index eca6fb7..92cf838 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -39,3 +39,14 @@ config MTK_SCPSYS_MT8173
+ domain driver.
+ The System Control Processor System (SCPSYS) has several power
+ management related tasks in the system.
+diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
+index 3b22baa..822986d 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
-@@ -2,3 +2,4 @@ obj-$(CONFIG_MTK_INFRACFG) += mtk-infrac
+@@ -2,3 +2,4 @@ obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
obj-$(CONFIG_MTK_SCPSYS_MT8173) += mtk-scpsys-mt8173.o
+obj-$(CONFIG_MTK_SCPSYS_MT2701) += mtk-scpsys-mt2701.o
+diff --git a/drivers/soc/mediatek/mtk-scpsys-mt2701.c b/drivers/soc/mediatek/mtk-scpsys-mt2701.c
+new file mode 100644
+index 0000000..339d5b8
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-scpsys-mt2701.c
@@ -0,0 +1,161 @@
+
+MODULE_DESCRIPTION("MediaTek MT2701 scpsys driver");
+MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
-From 0c39bcd17fa6ce723f56ad3756b4bb36c4690342 Mon Sep 17 00:00:00 2001
+From 69d4e250847f82a5896c41bcb5f1e793c5a8fbac Mon Sep 17 00:00:00 2001
From: James Liao <jamesjj.liao@mediatek.com>
Date: Tue, 5 Jan 2016 14:30:17 +0800
-Subject: [PATCH 06/91] clk: mediatek: Refine the makefile to support multiple
- clock drivers
+Subject: [PATCH 006/102] clk: mediatek: Refine the makefile to support
+ multiple clock drivers
Add a Kconfig to define clock configuration for each SoC, and
modify the Makefile to build drivers that only selected in config.
3 files changed, 27 insertions(+), 3 deletions(-)
create mode 100644 drivers/clk/mediatek/Kconfig
+diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
+index c3e3a02..b7a37dc 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -198,3 +198,4 @@ source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/samsung/Kconfig"
source "drivers/clk/tegra/Kconfig"
+source "drivers/clk/mediatek/Kconfig"
+diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
+new file mode 100644
+index 0000000..dc224e6
--- /dev/null
+++ b/drivers/clk/mediatek/Kconfig
@@ -0,0 +1,23 @@
+ default ARCH_MEDIATEK
+ ---help---
+ This driver supports Mediatek MT8173 clocks.
+diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
+index 95fdfac..32e7222 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,4 +1,4 @@
-obj-y += clk-mt8173.o
+obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
+obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
+--
+1.7.10.4
+
-From d7e96f87f66c571e9f4171ecd89c656fbd2de89b Mon Sep 17 00:00:00 2001
+From 7c98b20fa68a2a64bca69822eb7be4fa9b668fab Mon Sep 17 00:00:00 2001
From: James Liao <jamesjj.liao@mediatek.com>
Date: Tue, 5 Jan 2016 14:30:18 +0800
-Subject: [PATCH 07/91] dt-bindings: ARM: Mediatek: Document bindings for
+Subject: [PATCH 007/102] dt-bindings: ARM: Mediatek: Document bindings for
MT2701
This patch adds the binding documentation for apmixedsys, bdpsys,
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
+index 936166f..a701e19 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
-@@ -6,6 +6,7 @@ The Mediatek apmixedsys controller provi
+@@ -6,6 +6,7 @@ The Mediatek apmixedsys controller provides the PLLs to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8135-apmixedsys"
- "mediatek,mt8173-apmixedsys"
- #clock-cells: Must be 1
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt
+new file mode 100644
+index 0000000..4137196
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt
@@ -0,0 +1,22 @@
+ reg = <0 0x1c000000 0 0x1000>;
+ #clock-cells = <1>;
+};
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt
+new file mode 100644
+index 0000000..768f3a5
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt
@@ -0,0 +1,22 @@
+ reg = <0 0x1b000000 0 0x1000>;
+ #clock-cells = <1>;
+};
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt
+new file mode 100644
+index 0000000..b7a39b6
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt
@@ -0,0 +1,22 @@
+ reg = <0 0x1a000000 0 0x1000>;
+ #clock-cells = <1>;
+};
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
+index b1f2ce1..9bda7f7 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
-@@ -6,6 +6,7 @@ The Mediatek imgsys controller provides
+@@ -6,6 +6,7 @@ The Mediatek imgsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-imgsys", "syscon"
- #clock-cells: Must be 1
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
+index f6cd3e4..2f11a69 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
@@ -7,6 +7,7 @@ outputs to the system.
- "mediatek,mt8135-infracfg", "syscon"
- "mediatek,mt8173-infracfg", "syscon"
- #clock-cells: Must be 1
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
+index 4385946..c9d9d43 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
-@@ -6,6 +6,7 @@ The Mediatek mmsys controller provides v
+@@ -6,6 +6,7 @@ The Mediatek mmsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-mmsys", "syscon"
- #clock-cells: Must be 1
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
+index f25b854..d3454cd 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
@@ -7,6 +7,7 @@ outputs to the system.
- "mediatek,mt8135-pericfg", "syscon"
- "mediatek,mt8173-pericfg", "syscon"
- #clock-cells: Must be 1
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
+index f9e9179..602e5bc 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
-@@ -6,6 +6,7 @@ The Mediatek topckgen controller provide
+@@ -6,6 +6,7 @@ The Mediatek topckgen controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8135-topckgen"
- "mediatek,mt8173-topckgen"
- #clock-cells: Must be 1
+diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt
+index 1faacf1..f5b1e7d 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt
-@@ -6,6 +6,7 @@ The Mediatek vdecsys controller provides
+@@ -6,6 +6,7 @@ The Mediatek vdecsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-vdecsys", "syscon"
- #clock-cells: Must be 1
+--
+1.7.10.4
+
-From 2fcbc15da2f13164e0851b9c7fae290249f0b44d Mon Sep 17 00:00:00 2001
+From 190696e3995be38fa01490e4ab88ea2c859829c9 Mon Sep 17 00:00:00 2001
From: Shunli Wang <shunli.wang@mediatek.com>
Date: Tue, 5 Jan 2016 14:30:19 +0800
-Subject: [PATCH 08/91] clk: mediatek: Add dt-bindings for MT2701 clocks
+Subject: [PATCH 008/102] clk: mediatek: Add dt-bindings for MT2701 clocks
Add MT2701 clock dt-bindings, include topckgen, apmixedsys,
infracfg, pericfg and subsystem clocks.
1 file changed, 481 insertions(+)
create mode 100644 include/dt-bindings/clock/mt2701-clk.h
+diff --git a/include/dt-bindings/clock/mt2701-clk.h b/include/dt-bindings/clock/mt2701-clk.h
+new file mode 100644
+index 0000000..50972d1
--- /dev/null
+++ b/include/dt-bindings/clock/mt2701-clk.h
@@ -0,0 +1,481 @@
+#define CLK_BDP_NR 50
+
+#endif /* _DT_BINDINGS_CLK_MT2701_H */
+--
+1.7.10.4
+
-From f2c07eaa2df52f9acac9ffc3457d3d81079dd723 Mon Sep 17 00:00:00 2001
+From a4c507d052390b42d7e8c59241e3c336796f730f Mon Sep 17 00:00:00 2001
From: Shunli Wang <shunli.wang@mediatek.com>
Date: Tue, 5 Jan 2016 14:30:20 +0800
-Subject: [PATCH 09/91] clk: mediatek: Add MT2701 clock support
+Subject: [PATCH 009/102] clk: mediatek: Add MT2701 clock support
Add MT2701 clock support, include topckgen, apmixedsys,
infracfg, pericfg and subsystem clocks.
7 files changed, 1334 insertions(+), 3 deletions(-)
create mode 100644 drivers/clk/mediatek/clk-mt2701.c
+diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
+index dc224e6..6c7cdc0 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -6,6 +6,14 @@ config COMMON_CLK_MEDIATEK
config COMMON_CLK_MT8135
bool "Clock driver for Mediatek MT8135"
depends on COMMON_CLK
+diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
+index 32e7222..5b2b91b 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,4 +1,5 @@
+obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
+diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
+index 576bdb7..38badb4 100644
--- a/drivers/clk/mediatek/clk-gate.c
+++ b/drivers/clk/mediatek/clk-gate.c
-@@ -61,6 +61,26 @@ static void mtk_cg_clr_bit(struct clk_hw
+@@ -61,6 +61,26 @@ static void mtk_cg_clr_bit(struct clk_hw *hw)
regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
}
static int mtk_cg_enable(struct clk_hw *hw)
{
mtk_cg_clr_bit(hw);
-@@ -85,6 +105,30 @@ static void mtk_cg_disable_inv(struct cl
+@@ -85,6 +105,30 @@ static void mtk_cg_disable_inv(struct clk_hw *hw)
mtk_cg_clr_bit(hw);
}
const struct clk_ops mtk_clk_gate_ops_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable,
-@@ -97,6 +141,18 @@ const struct clk_ops mtk_clk_gate_ops_se
+@@ -97,6 +141,18 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
.disable = mtk_cg_disable_inv,
};
struct clk * __init mtk_clk_register_gate(
const char *name,
const char *parent_name,
+diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
+index 11e25c9..7f7ef34 100644
--- a/drivers/clk/mediatek/clk-gate.h
+++ b/drivers/clk/mediatek/clk-gate.h
-@@ -36,6 +36,8 @@ static inline struct mtk_clk_gate *to_cl
+@@ -36,6 +36,8 @@ static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw)
extern const struct clk_ops mtk_clk_gate_ops_setclr;
extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
struct clk *mtk_clk_register_gate(
const char *name,
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+new file mode 100644
+index 0000000..2f521f4
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701.c
@@ -0,0 +1,1210 @@
+}
+CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt2701-apmixedsys",
+ mtk_apmixedsys_init);
+diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
+index cf08db6..be19a41 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
-@@ -242,3 +242,28 @@ void __init mtk_clk_register_composites(
+@@ -242,3 +242,28 @@ void __init mtk_clk_register_composites(const struct mtk_composite *mcs,
clk_data->clks[mc->id] = clk;
}
}
+ clk_data->clks[mcd->id] = clk;
+ }
+}
+diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
+index 32d2e45..60701e8 100644
--- a/drivers/clk/mediatek/clk-mtk.h
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -110,7 +110,8 @@ struct mtk_composite {
struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
+--
+1.7.10.4
+
-From 8d134cbe750b59d15c591622d81e2e9daa09f0c4 Mon Sep 17 00:00:00 2001
+From 8bf0f2a1e8ff082de3f650211abd985ef68abe1b Mon Sep 17 00:00:00 2001
From: Shunli Wang <shunli.wang@mediatek.com>
Date: Tue, 5 Jan 2016 14:30:21 +0800
-Subject: [PATCH 10/91] reset: mediatek: mt2701 reset controller dt-binding
+Subject: [PATCH 010/102] reset: mediatek: mt2701 reset controller dt-binding
file
Dt-binding file about reset controller is used to provide
1 file changed, 74 insertions(+)
create mode 100644 include/dt-bindings/reset-controller/mt2701-resets.h
+diff --git a/include/dt-bindings/reset-controller/mt2701-resets.h b/include/dt-bindings/reset-controller/mt2701-resets.h
+new file mode 100644
+index 0000000..00efeb0
--- /dev/null
+++ b/include/dt-bindings/reset-controller/mt2701-resets.h
@@ -0,0 +1,74 @@
+#define MT2701_TOPRGU_BDP_DISP_RST 13
+
+#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT2701 */
+--
+1.7.10.4
+
-From b86d3303db25a8296e4c3de46ee1470f60f71b0c Mon Sep 17 00:00:00 2001
+From 3ba0020ea70ffb5503eff1823be7fa5ceda38286 Mon Sep 17 00:00:00 2001
From: Shunli Wang <shunli.wang@mediatek.com>
Date: Tue, 5 Jan 2016 14:30:22 +0800
-Subject: [PATCH 11/91] reset: mediatek: mt2701 reset driver
+Subject: [PATCH 011/102] reset: mediatek: mt2701 reset driver
In infrasys and perifsys, there are many reset
control bits for kinds of modules. These bits are
drivers/clk/mediatek/clk-mt2701.c | 4 ++++
1 file changed, 4 insertions(+)
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+index 2f521f4..39472e4 100644
--- a/drivers/clk/mediatek/clk-mt2701.c
+++ b/drivers/clk/mediatek/clk-mt2701.c
-@@ -665,6 +665,8 @@ static void __init mtk_infrasys_init(str
+@@ -665,6 +665,8 @@ static void __init mtk_infrasys_init(struct device_node *node)
if (r)
pr_err("%s(): could not register clock provider: %d\n",
__func__, r);
}
CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt2701-infracfg", mtk_infrasys_init);
-@@ -782,6 +784,8 @@ static void __init mtk_pericfg_init(stru
+@@ -782,6 +784,8 @@ static void __init mtk_pericfg_init(struct device_node *node)
if (r)
pr_err("%s(): could not register clock provider: %d\n",
__func__, r);
}
CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt2701-pericfg", mtk_pericfg_init);
+--
+1.7.10.4
+
-From 3b5df542d52b13a1b20d25311fa4c4029a3b83af Mon Sep 17 00:00:00 2001
+From 32fa899c6ab79953e4f470fb23c38bcc40edc5c8 Mon Sep 17 00:00:00 2001
From: Erin Lo <erin.lo@mediatek.com>
Date: Mon, 28 Dec 2015 15:09:02 +0800
-Subject: [PATCH 12/91] ARM: mediatek: Add MT2701 config options for mediatek
- SoCs.
+Subject: [PATCH 012/102] ARM: mediatek: Add MT2701 config options for
+ mediatek SoCs.
The upcoming MTK pinctrl driver have a big pin table for each SoC
and we don't want to bloat the kernel binary if we don't need it.
arch/arm/mach-mediatek/Kconfig | 4 ++++
1 file changed, 4 insertions(+)
+diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig
+index aeece17..37dd438 100644
--- a/arch/arm/mach-mediatek/Kconfig
+++ b/arch/arm/mach-mediatek/Kconfig
@@ -9,6 +9,10 @@ menuconfig ARCH_MEDIATEK
config MACH_MT6589
bool "MediaTek MT6589 SoCs support"
default ARCH_MEDIATEK
+--
+1.7.10.4
+
-From 1a254735cad9db5c8605c972b0f16b3929dc0d6e Mon Sep 17 00:00:00 2001
+From afcbed6f51e8c3a9195952b27c8aad047c314ed0 Mon Sep 17 00:00:00 2001
From: Biao Huang <biao.huang@mediatek.com>
Date: Mon, 28 Dec 2015 15:09:03 +0800
-Subject: [PATCH 13/91] dt-bindings: mediatek: Modify pinctrl bindings for
+Subject: [PATCH 013/102] dt-bindings: mediatek: Modify pinctrl bindings for
mt2701
Signed-off-by: Biao Huang <biao.huang@mediatek.com>
Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
+diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
+index 0480bc3..9ffb0b2 100644
--- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
-@@ -4,10 +4,11 @@ The Mediatek's Pin controller is used to
+@@ -4,10 +4,11 @@ The Mediatek's Pin controller is used to control SoC pins.
Required properties:
- compatible: value should be one of the following.
- pins-are-numbered: Specify the subnodes are using numbered pinmux to
specify pins.
- gpio-controller : Marks the device node as a gpio controller.
+--
+1.7.10.4
+
-From 416720ba33d4fd7d3166c17be7c13651cc08d408 Mon Sep 17 00:00:00 2001
+From 124894a4d1635915ff95c447767677b60fd27e9c Mon Sep 17 00:00:00 2001
From: Biao Huang <biao.huang@mediatek.com>
Date: Mon, 28 Dec 2015 15:09:04 +0800
-Subject: [PATCH 14/91] pinctrl: dt bindings: Add pinfunc header file for
+Subject: [PATCH 014/102] pinctrl: dt bindings: Add pinfunc header file for
mt2701
Add pinfunc header file, mt2701 related dts will include it
create mode 100644 drivers/pinctrl/mediatek/pinctrl-mt2701.c
create mode 100644 drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h
+diff --git a/arch/arm/boot/dts/mt2701-pinfunc.h b/arch/arm/boot/dts/mt2701-pinfunc.h
+new file mode 100644
+index 0000000..e24ebc8
--- /dev/null
+++ b/arch/arm/boot/dts/mt2701-pinfunc.h
@@ -0,0 +1,735 @@
+#define MT2701_PIN_278_JTAG_RESET__FUNC_JTAG_RESET (MTK_PIN_NO(278) | 1)
+
+#endif /* __DTS_MT2701_PINFUNC_H */
+diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
+index 02f6f92..13e9939 100644
--- a/drivers/pinctrl/mediatek/Kconfig
+++ b/drivers/pinctrl/mediatek/Kconfig
@@ -9,6 +9,12 @@ config PINCTRL_MTK_COMMON
config PINCTRL_MT8135
bool "Mediatek MT8135 pin control" if COMPILE_TEST && !MACH_MT8135
depends on OF
+diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
+index eb923d6..da30314 100644
--- a/drivers/pinctrl/mediatek/Makefile
+++ b/drivers/pinctrl/mediatek/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_PINCTRL_MT8135) += pinctrl-mt8135.o
obj-$(CONFIG_PINCTRL_MT8127) += pinctrl-mt8127.o
obj-$(CONFIG_PINCTRL_MT8173) += pinctrl-mt8173.o
+diff --git a/drivers/pinctrl/mediatek/pinctrl-mt2701.c b/drivers/pinctrl/mediatek/pinctrl-mt2701.c
+new file mode 100644
+index 0000000..4861b5d
--- /dev/null
+++ b/drivers/pinctrl/mediatek/pinctrl-mt2701.c
@@ -0,0 +1,586 @@
+}
+
+arch_initcall(mtk_pinctrl_init);
+diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+index 5c71727..05ba7a8 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -47,6 +47,8 @@
};
/*
-@@ -81,6 +83,9 @@ static int mtk_pmx_gpio_set_direction(st
+@@ -81,6 +83,9 @@ static int mtk_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset;
bit = BIT(offset & 0xf);
if (input)
/* Different SoC has different alignment offset. */
reg_addr = CLR_ADDR(reg_addr, pctl);
-@@ -347,6 +352,7 @@ static int mtk_pconf_parse_conf(struct p
+@@ -347,6 +352,7 @@ static int mtk_pconf_parse_conf(struct pinctrl_dev *pctldev,
ret = mtk_pconf_set_pull_select(pctl, pin, true, false, arg);
break;
case PIN_CONFIG_INPUT_ENABLE:
ret = mtk_pconf_set_ies_smt(pctl, pin, arg, param);
break;
case PIN_CONFIG_OUTPUT:
-@@ -354,6 +360,7 @@ static int mtk_pconf_parse_conf(struct p
+@@ -354,6 +360,7 @@ static int mtk_pconf_parse_conf(struct pinctrl_dev *pctldev,
ret = mtk_pmx_gpio_set_direction(pctldev, NULL, pin, false);
break;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
ret = mtk_pconf_set_ies_smt(pctl, pin, arg, param);
break;
case PIN_CONFIG_DRIVE_STRENGTH:
-@@ -667,9 +674,14 @@ static int mtk_pmx_set_mode(struct pinct
+@@ -667,9 +674,14 @@ static int mtk_pmx_set_mode(struct pinctrl_dev *pctldev,
unsigned int mask = (1L << GPIO_MODE_BITS) - 1;
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
bit = pin % MAX_GPIO_MODE_PER_REG;
mask <<= (GPIO_MODE_BITS * bit);
val = (mode << (GPIO_MODE_BITS * bit));
-@@ -746,6 +758,10 @@ static int mtk_gpio_get_direction(struct
+@@ -746,6 +758,10 @@ static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset;
bit = BIT(offset & 0xf);
regmap_read(pctl->regmap1, reg_addr, &read_val);
return !(read_val & bit);
}
+diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h
+index 55a5343..8543bc4 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h
@@ -209,7 +209,14 @@ struct mtk_eint_offsets {
unsigned int dir_offset;
unsigned int ies_offset;
unsigned int smt_offset;
+diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h
+new file mode 100644
+index 0000000..f906420
--- /dev/null
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h
@@ -0,0 +1,2323 @@
+};
+
+#endif /* __PINCTRL_MTK_MT2701_H */
+--
+1.7.10.4
+
-From ddc72b659b3642d0496dee4e1ee39416ca008053 Mon Sep 17 00:00:00 2001
+From 3800e5c33e5becbb56c6694008d1f3435fd78707 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Thu, 7 Jan 2016 23:42:06 +0100
-Subject: [PATCH 15/91] dt-bindings: mediatek: Modify pinctrl bindings for
+Subject: [PATCH 015/102] dt-bindings: mediatek: Modify pinctrl bindings for
mt7623
Signed-off-by: John Crispin <blogic@openwrt.org>
2 files changed, 522 insertions(+)
create mode 100644 include/dt-bindings/pinctrl/mt7623-pinfunc.h
+diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
+index 9ffb0b2..17631d0 100644
--- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
@@ -6,6 +6,7 @@ Required properties:
"mediatek,mt8127-pinctrl", compatible with mt8127 pinctrl.
"mediatek,mt8135-pinctrl", compatible with mt8135 pinctrl.
"mediatek,mt8173-pinctrl", compatible with mt8173 pinctrl.
+diff --git a/include/dt-bindings/pinctrl/mt7623-pinfunc.h b/include/dt-bindings/pinctrl/mt7623-pinfunc.h
+new file mode 100644
+index 0000000..891b173
--- /dev/null
+++ b/include/dt-bindings/pinctrl/mt7623-pinfunc.h
@@ -0,0 +1,521 @@
+
+#endif /* __DTS_MT7623_PINFUNC_H */
+
+--
+1.7.10.4
+
-From 1255eaacd6cc9d1fa6bb33185380efed22008baf Mon Sep 17 00:00:00 2001
+From 641ccb565a934ffaa30b828f2361e6f57325c70a Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sat, 27 Jun 2015 13:13:05 +0200
-Subject: [PATCH 16/91] pinctrl: dt bindings: Add pinctrl file for mt7623
+Subject: [PATCH 016/102] pinctrl: dt bindings: Add pinctrl file for mt7623
Add the driver and header files required to make pinctrl work on MediaTek
MT7623.
create mode 100644 drivers/pinctrl/mediatek/pinctrl-mt7623.c
create mode 100644 drivers/pinctrl/mediatek/pinctrl-mtk-mt7623.h
+diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
+index 13e9939..78654a8 100644
--- a/drivers/pinctrl/mediatek/Kconfig
+++ b/drivers/pinctrl/mediatek/Kconfig
@@ -15,6 +15,12 @@ config PINCTRL_MT2701
config PINCTRL_MT8135
bool "Mediatek MT8135 pin control" if COMPILE_TEST && !MACH_MT8135
depends on OF
+diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
+index da30314..1be2f3f 100644
--- a/drivers/pinctrl/mediatek/Makefile
+++ b/drivers/pinctrl/mediatek/Makefile
-@@ -3,6 +3,7 @@ obj-$(CONFIG_PINCTRL_MTK_COMMON) += pinc
+@@ -3,6 +3,7 @@ obj-$(CONFIG_PINCTRL_MTK_COMMON) += pinctrl-mtk-common.o
# SoC Drivers
obj-$(CONFIG_PINCTRL_MT2701) += pinctrl-mt2701.o
obj-$(CONFIG_PINCTRL_MT8135) += pinctrl-mt8135.o
obj-$(CONFIG_PINCTRL_MT8127) += pinctrl-mt8127.o
obj-$(CONFIG_PINCTRL_MT8173) += pinctrl-mt8173.o
+diff --git a/drivers/pinctrl/mediatek/pinctrl-mt7623.c b/drivers/pinctrl/mediatek/pinctrl-mt7623.c
+new file mode 100644
+index 0000000..bf0d05b
--- /dev/null
+++ b/drivers/pinctrl/mediatek/pinctrl-mt7623.c
@@ -0,0 +1,380 @@
+}
+
+arch_initcall(mtk_pinctrl_init);
+diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt7623.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt7623.h
+new file mode 100644
+index 0000000..fb63c01
--- /dev/null
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt7623.h
@@ -0,0 +1,1937 @@
+};
+
+#endif /* __PINCTRL_MTK_MT7623_H */
+diff --git a/include/dt-bindings/pinctrl/mt7623-pinfunc.h b/include/dt-bindings/pinctrl/mt7623-pinfunc.h
+index 891b173..eeb2380 100644
--- a/include/dt-bindings/pinctrl/mt7623-pinfunc.h
+++ b/include/dt-bindings/pinctrl/mt7623-pinfunc.h
@@ -505,6 +505,9 @@
#define MT7623_PIN_274_G2_RXDV_FUNC_GPIO274 (MTK_PIN_NO(274) | 0)
#define MT7623_PIN_274_G2_RXDV_FUNC_G2_RXDV (MTK_PIN_NO(274) | 1)
+--
+1.7.10.4
+
-From 294cf90337d70ad74edf147180bbeef837298bd0 Mon Sep 17 00:00:00 2001
+From f7121d2b19ddad33a09408a2c5923bfd95da8533 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 6 Jan 2016 20:06:49 +0100
-Subject: [PATCH 17/91] clk: add hifsys reset
+Subject: [PATCH 017/102] clk: add hifsys reset
Hi,
include/dt-bindings/reset-controller/mt2701-resets.h | 9 +++++++++
2 files changed, 11 insertions(+)
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+index 39472e4..0e40bb8 100644
--- a/drivers/clk/mediatek/clk-mt2701.c
+++ b/drivers/clk/mediatek/clk-mt2701.c
-@@ -1000,6 +1000,8 @@ static void __init mtk_hifsys_init(struc
+@@ -1000,6 +1000,8 @@ static void __init mtk_hifsys_init(struct device_node *node)
if (r)
pr_err("%s(): could not register clock provider: %d\n",
__func__, r);
}
CLK_OF_DECLARE(mtk_hifsys, "mediatek,mt2701-hifsys", mtk_hifsys_init);
+diff --git a/include/dt-bindings/reset-controller/mt2701-resets.h b/include/dt-bindings/reset-controller/mt2701-resets.h
+index 00efeb0..aaf0305 100644
--- a/include/dt-bindings/reset-controller/mt2701-resets.h
+++ b/include/dt-bindings/reset-controller/mt2701-resets.h
@@ -71,4 +71,13 @@
+#define MT2701_HIFSYS_PCIE2_RST 26
+
#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT2701 */
+--
+1.7.10.4
+
-From 84d37aeef94deae3ce87e677f6016a5d980429e8 Mon Sep 17 00:00:00 2001
+From ba126a519da8a036dae0032e9d5a89e47570e5fb Mon Sep 17 00:00:00 2001
From: "chunfeng.yun@mediatek.com" <chunfeng.yun@mediatek.com>
Date: Tue, 17 Nov 2015 17:18:39 +0800
-Subject: [PATCH 18/91] dt-bindings: Add a binding for Mediatek xHCI host
+Subject: [PATCH 018/102] dt-bindings: Add a binding for Mediatek xHCI host
controller
add a DT binding documentation of xHCI host controller for the
1 file changed, 51 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/mt8173-xhci.txt
+diff --git a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
+new file mode 100644
+index 0000000..a78f20b
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
@@ -0,0 +1,51 @@
+ mediatek,syscon-wakeup = <&pericfg>;
+ mediatek,wakeup-src = <1>;
+};
+--
+1.7.10.4
+
-From 651d8fff94718c7e48b8a40d7774878eb8ed62ee Mon Sep 17 00:00:00 2001
+From 8b8185586a13ebbd760e80bbe5f22f9417b50fd2 Mon Sep 17 00:00:00 2001
From: "chunfeng.yun@mediatek.com" <chunfeng.yun@mediatek.com>
Date: Tue, 17 Nov 2015 17:18:40 +0800
-Subject: [PATCH 19/91] xhci: mediatek: support MTK xHCI host controller
+Subject: [PATCH 019/102] xhci: mediatek: support MTK xHCI host controller
There some vendor quirks for MTK xhci host controller:
1. It defines some extra SW scheduling parameters for HW
create mode 100644 drivers/usb/host/xhci-mtk.c
create mode 100644 drivers/usb/host/xhci-mtk.h
+diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
+index 3bb0887..daa563f 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -41,6 +41,15 @@ config USB_XHCI_PLATFORM
config USB_XHCI_MVEBU
tristate "xHCI support for Marvell Armada 375/38x"
select USB_XHCI_PLATFORM
+diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
+index e7558ab..65a06b4 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -13,6 +13,9 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
+diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
+new file mode 100644
+index 0000000..c30de7c
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -0,0 +1,415 @@
+ }
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_drop_ep_quirk);
+diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
+new file mode 100644
+index 0000000..c9ab6a4
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk.c
@@ -0,0 +1,763 @@
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek xHCI Host Controller Driver");
+MODULE_LICENSE("GPL v2");
+diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
+new file mode 100644
+index 0000000..7da677c
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk.h
@@ -0,0 +1,162 @@
+#endif
+
+#endif /* _XHCI_MTK_H_ */
+diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
+index eeaa6c6..f1c21c4 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -68,6 +68,7 @@
/*
* Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
-@@ -3065,17 +3066,22 @@ static u32 xhci_td_remainder(struct xhci
+@@ -3075,17 +3076,22 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
{
u32 maxp, total_packet_count;
/* Queueing functions don't count the current TRB into transferred */
return (total_packet_count - ((transferred + trb_buff_len) / maxp));
}
-@@ -3463,7 +3469,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *
+@@ -3473,7 +3479,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
field |= 0x1;
/* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */
if (urb->transfer_buffer_length > 0) {
if (setup->bRequestType & USB_DIR_IN)
field |= TRB_TX_TYPE(TRB_DATA_IN);
+diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
+index 3f91270..15fedb2 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -31,6 +31,7 @@
#define DRIVER_AUTHOR "Sarah Sharp"
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
-@@ -635,7 +636,11 @@ int xhci_run(struct usb_hcd *hcd)
+@@ -634,7 +635,11 @@ int xhci_run(struct usb_hcd *hcd)
"// Set the interrupt modulation register");
temp = readl(&xhci->ir_set->irq_control);
temp &= ~ER_IRQ_INTERVAL_MASK;
writel(temp, &xhci->ir_set->irq_control);
/* Set the HCD state before we enable the irqs */
-@@ -1701,6 +1706,9 @@ int xhci_drop_endpoint(struct usb_hcd *h
+@@ -1698,6 +1703,9 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
-@@ -1796,6 +1804,15 @@ int xhci_add_endpoint(struct usb_hcd *hc
+@@ -1793,6 +1801,15 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
return -ENOMEM;
}
ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
+index 0b94512..40cf36e 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
-@@ -1631,6 +1631,7 @@ struct xhci_hcd {
+@@ -1630,6 +1630,7 @@ struct xhci_hcd {
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
#define XHCI_PME_STUCK_QUIRK (1 << 20)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
+--
+1.7.10.4
+
-From 31a22fbd0d3b187be61c4c5d22b19c95abb327c3 Mon Sep 17 00:00:00 2001
+From 645465d4c6dd46c5e6c9ac25cd42608b4201fde0 Mon Sep 17 00:00:00 2001
From: "chunfeng.yun@mediatek.com" <chunfeng.yun@mediatek.com>
Date: Tue, 17 Nov 2015 17:18:41 +0800
-Subject: [PATCH 20/91] arm64: dts: mediatek: add xHCI & usb phy for mt8173
+Subject: [PATCH 020/102] arm64: dts: mediatek: add xHCI & usb phy for mt8173
add xHCI and phy drivers for MT8173-EVB
arch/arm64/boot/dts/mediatek/mt8173.dtsi | 42 +++++++++++++++++++++++++++
2 files changed, 58 insertions(+)
+diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
+index 811cb76..9b1482a 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
@@ -13,6 +13,7 @@
+ vbus-supply = <&usb_p1_vbus>;
+ mediatek,wakeup-src = <1>;
+};
+diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+index 4dd5f93..c1fd275 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -14,6 +14,7 @@
mmsys: clock-controller@14000000 {
compatible = "mediatek,mt8173-mmsys", "syscon";
reg = <0 0x14000000 0 0x1000>;
+--
+1.7.10.4
+
-From 162deec293400cb132161606629654acaec7cb4b Mon Sep 17 00:00:00 2001
+From e111a35542ac14712026fe1a55236f76c7fc9048 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Tue, 5 Jan 2016 12:13:54 +0100
-Subject: [PATCH 21/91] Document: DT: Add bindings for mediatek MT7623 SoC
+Subject: [PATCH 021/102] Document: DT: Add bindings for mediatek MT7623 SoC
Platform
This adds a DT binding documentation for the MT7623 SoC from Mediatek.
Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt | 1 +
3 files changed, 6 insertions(+)
+diff --git a/Documentation/devicetree/bindings/arm/mediatek.txt b/Documentation/devicetree/bindings/arm/mediatek.txt
+index 618a9199..40e9d32 100644
--- a/Documentation/devicetree/bindings/arm/mediatek.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek.txt
@@ -10,6 +10,7 @@ compatible: Must contain one of
- MTK mt8127 tablet moose EVB:
Required root node properties:
- compatible = "mediatek,mt8127-moose", "mediatek,mt8127";
+diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt
+index 2d47add..474f0cf 100644
--- a/Documentation/devicetree/bindings/serial/mtk-uart.txt
+++ b/Documentation/devicetree/bindings/serial/mtk-uart.txt
@@ -2,6 +2,7 @@
* "mediatek,mt8135-uart" for MT8135 compatible UARTS
* "mediatek,mt8127-uart" for MT8127 compatible UARTS
* "mediatek,mt8173-uart" for MT8173 compatible UARTS
+diff --git a/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt b/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt
+index 64083bc..6bacda1b3 100644
--- a/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt
+++ b/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt
@@ -5,6 +5,7 @@ Required properties:
* "mediatek,mt8127-timer" for MT8127 compatible timers
* "mediatek,mt8135-timer" for MT8135 compatible timers
* "mediatek,mt8173-timer" for MT8173 compatible timers
+--
+1.7.10.4
+
-From fa5d94d6b4b314f751b1c32bb5a87a80b866d05e Mon Sep 17 00:00:00 2001
+From f232c3b36355974bf3442de3a4726d2e499ed3fe Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Tue, 5 Jan 2016 16:52:31 +0100
-Subject: [PATCH 22/91] soc: mediatek: add compat string for mt7623 to scpsys
+Subject: [PATCH 022/102] soc: mediatek: add compat string for mt7623 to
+ scpsys
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/soc/mediatek/mtk-scpsys-mt2701.c | 2 ++
1 file changed, 2 insertions(+)
+diff --git a/drivers/soc/mediatek/mtk-scpsys-mt2701.c b/drivers/soc/mediatek/mtk-scpsys-mt2701.c
+index 339d5b8..3a31946 100644
--- a/drivers/soc/mediatek/mtk-scpsys-mt2701.c
+++ b/drivers/soc/mediatek/mtk-scpsys-mt2701.c
-@@ -136,6 +136,8 @@ static const struct of_device_id of_scps
+@@ -136,6 +136,8 @@ static const struct of_device_id of_scpsys_match_tbl[] = {
{
.compatible = "mediatek,mt2701-scpsys",
}, {
/* sentinel */
}
};
+--
+1.7.10.4
+
-From 83ef9fb21a896ac03c3a78bc3ae0b21f3b0a43a3 Mon Sep 17 00:00:00 2001
+From 51d5ca9e151eb323bd965e72ad1e1dc93fcf7b13 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Tue, 5 Jan 2016 12:16:17 +0100
-Subject: [PATCH 23/91] ARM: dts: mediatek: add MT7623 basic support
+Subject: [PATCH 023/102] ARM: dts: mediatek: add MT7623 basic support
This adds basic chip support for Mediatek MT7623.
Signed-off-by: John Crispin <blogic@openwrt.org>
---
arch/arm/boot/dts/Makefile | 1 +
- arch/arm/boot/dts/mt7623-evb.dts | 474 +++++++++++++++++++++++++++++
- arch/arm/boot/dts/mt7623.dtsi | 593 +++++++++++++++++++++++++++++++++++++
+ arch/arm/boot/dts/mt7623-evb.dts | 421 ++++++++++++++++++++++++++
+ arch/arm/boot/dts/mt7623.dtsi | 601 +++++++++++++++++++++++++++++++++++++
arch/arm/mach-mediatek/Kconfig | 4 +
arch/arm/mach-mediatek/mediatek.c | 1 +
- 5 files changed, 1073 insertions(+)
+ 5 files changed, 1028 insertions(+)
create mode 100644 arch/arm/boot/dts/mt7623-evb.dts
create mode 100644 arch/arm/boot/dts/mt7623.dtsi
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index 30bbc37..2bce370 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -774,6 +774,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
mt8127-moose.dtb \
mt8135-evbp1.dtb
dtb-$(CONFIG_ARCH_ZX) += zx296702-ad1.dtb
+diff --git a/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts
+new file mode 100644
+index 0000000..5ad1448
--- /dev/null
+++ b/arch/arm/boot/dts/mt7623-evb.dts
-@@ -0,0 +1,474 @@
+@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: John Crispin <blogic@openwrt.org>
+ status = "okay";
+};
+
-+&mmc0 {
-+ status = "okay";
-+ pinctrl-names = "default", "state_uhs";
-+ pinctrl-0 = <&mmc0_pins_default>;
-+ pinctrl-1 = <&mmc0_pins_uhs>;
-+ bus-width = <8>;
-+ max-frequency = <50000000>;
-+ cap-mmc-highspeed;
-+ vmmc-supply = <&mt6323_vemc3v3_reg>;
-+ vqmmc-supply = <&mt6323_vio18_reg>;
-+ non-removable;
-+};
-+
-+&mmc1 {
-+ status = "okay";
-+ pinctrl-names = "default", "state_uhs";
-+ pinctrl-0 = <&mmc1_pins_default>;
-+ pinctrl-1 = <&mmc1_pins_uhs>;
-+ bus-width = <4>;
-+ max-frequency = <50000000>;
-+ cap-sd-highspeed;
-+ sd-uhs-sdr25;
-+// cd-gpios = <&pio 132 0>;
-+ vmmc-supply = <&mt6323_vmch_reg>;
-+ vqmmc-supply = <&mt6323_vmc_reg>;
-+};
-+
+&pio {
-+ mmc0_pins_default: mmc0default {
-+ pins_cmd_dat {
-+ pinmux = <MT7623_PIN_111_MSDC0_DAT7_FUNC_MSDC0_DAT7>,
-+ <MT7623_PIN_112_MSDC0_DAT6_FUNC_MSDC0_DAT6>,
-+ <MT7623_PIN_113_MSDC0_DAT5_FUNC_MSDC0_DAT5>,
-+ <MT7623_PIN_114_MSDC0_DAT4_FUNC_MSDC0_DAT4>,
-+ <MT7623_PIN_118_MSDC0_DAT3_FUNC_MSDC0_DAT3>,
-+ <MT7623_PIN_119_MSDC0_DAT2_FUNC_MSDC0_DAT2>,
-+ <MT7623_PIN_120_MSDC0_DAT1_FUNC_MSDC0_DAT1>,
-+ <MT7623_PIN_121_MSDC0_DAT0_FUNC_MSDC0_DAT0>,
-+ <MT7623_PIN_116_MSDC0_CMD_FUNC_MSDC0_CMD>;
-+ input-enable;
-+ bias-pull-up;
-+ };
-+
-+ pins_clk {
-+ pinmux = <MT7623_PIN_117_MSDC0_CLK_FUNC_MSDC0_CLK>;
-+ bias-pull-down;
-+ };
-+
-+ pins_rst {
-+ pinmux = <MT7623_PIN_115_MSDC0_RSTB_FUNC_MSDC0_RSTB>;
-+ bias-pull-up;
++ nand_pins_default: nanddefault {
++ pins_dat {
++ pinmux = <MT7623_PIN_111_MSDC0_DAT7_FUNC_NLD7>,
++ <MT7623_PIN_112_MSDC0_DAT6_FUNC_NLD6>,
++ <MT7623_PIN_114_MSDC0_DAT4_FUNC_NLD4>,
++ <MT7623_PIN_118_MSDC0_DAT3_FUNC_NLD3>,
++ <MT7623_PIN_121_MSDC0_DAT0_FUNC_NLD0>,
++ <MT7623_PIN_120_MSDC0_DAT1_FUNC_NLD1>,
++ <MT7623_PIN_113_MSDC0_DAT5_FUNC_NLD5>,
++ <MT7623_PIN_115_MSDC0_RSTB_FUNC_NLD8>,
++ <MT7623_PIN_119_MSDC0_DAT2_FUNC_NLD2>;
++ input-enable;
++ drive-strength = <MTK_DRIVE_8mA>;
++ bias-pull-up;
+ };
-+ };
+
-+ mmc0_pins_uhs: mmc0 {
-+ pins_cmd_dat {
-+ pinmux = <MT7623_PIN_111_MSDC0_DAT7_FUNC_MSDC0_DAT7>,
-+ <MT7623_PIN_112_MSDC0_DAT6_FUNC_MSDC0_DAT6>,
-+ <MT7623_PIN_113_MSDC0_DAT5_FUNC_MSDC0_DAT5>,
-+ <MT7623_PIN_114_MSDC0_DAT4_FUNC_MSDC0_DAT4>,
-+ <MT7623_PIN_118_MSDC0_DAT3_FUNC_MSDC0_DAT3>,
-+ <MT7623_PIN_119_MSDC0_DAT2_FUNC_MSDC0_DAT2>,
-+ <MT7623_PIN_120_MSDC0_DAT1_FUNC_MSDC0_DAT1>,
-+ <MT7623_PIN_121_MSDC0_DAT0_FUNC_MSDC0_DAT0>,
-+ <MT7623_PIN_116_MSDC0_CMD_FUNC_MSDC0_CMD>;
-+ input-enable;
-+ drive-strength = <MTK_DRIVE_2mA>;
-+ bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
-+ };
-+
-+ pins_clk {
-+ pinmux = <MT7623_PIN_117_MSDC0_CLK_FUNC_MSDC0_CLK>;
-+ drive-strength = <MTK_DRIVE_2mA>;
-+ bias-pull-down = <MTK_PUPD_SET_R1R0_01>;
-+ };
-+
-+ pins_rst {
-+ pinmux = <MT7623_PIN_115_MSDC0_RSTB_FUNC_MSDC0_RSTB>;
-+ bias-pull-up;
-+ };
-+ };
-+
-+ mmc1_pins_default: mmc1default {
-+ pins_cmd_dat {
-+ pinmux = <MT7623_PIN_107_MSDC1_DAT0_FUNC_MSDC1_DAT0>,
-+ <MT7623_PIN_108_MSDC1_DAT1_FUNC_MSDC1_DAT1>,
-+ <MT7623_PIN_109_MSDC1_DAT2_FUNC_MSDC1_DAT2>,
-+ <MT7623_PIN_110_MSDC1_DAT3_FUNC_MSDC1_DAT3>,
-+ <MT7623_PIN_105_MSDC1_CMD_FUNC_MSDC1_CMD>;
-+ input-enable;
-+ drive-strength = <MTK_DRIVE_4mA>;
++ pins_we {
++ pinmux = <MT7623_PIN_117_MSDC0_CLK_FUNC_NWEB>;
++ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-up = <MTK_PUPD_SET_R1R0_10>;
+ };
+
-+ pins_clk {
-+ pinmux = <MT7623_PIN_106_MSDC1_CLK_FUNC_MSDC1_CLK>;
-+ bias-pull-down;
-+ drive-strength = <MTK_DRIVE_4mA>;
-+ };
-+
-+// pins_insert {
-+// pinmux = <MT8173_PIN_132_I2S0_DATA1_FUNC_GPIO132>;
-+// bias-pull-up;
-+// };
-+ };
-+
-+ mmc1_pins_uhs: mmc1 {
-+ pins_cmd_dat {
-+ pinmux = <MT7623_PIN_107_MSDC1_DAT0_FUNC_MSDC1_DAT0>,
-+ <MT7623_PIN_108_MSDC1_DAT1_FUNC_MSDC1_DAT1>,
-+ <MT7623_PIN_109_MSDC1_DAT2_FUNC_MSDC1_DAT2>,
-+ <MT7623_PIN_110_MSDC1_DAT3_FUNC_MSDC1_DAT3>,
-+ <MT7623_PIN_105_MSDC1_CMD_FUNC_MSDC1_CMD>;
-+ input-enable;
-+ drive-strength = <MTK_DRIVE_4mA>;
-+ bias-pull-up = <MTK_PUPD_SET_R1R0_10>;
-+ };
-+
-+ pins_clk {
-+ pinmux = <MT7623_PIN_106_MSDC1_CLK_FUNC_MSDC1_CLK>;
-+ drive-strength = <MTK_DRIVE_4mA>;
++ pins_ale {
++ pinmux = <MT7623_PIN_116_MSDC0_CMD_FUNC_NALE>;
++ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
+ };
+ };
+ };
+};
+
++&nandc {
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <&nand_pins_default>;
++ nand@0 {
++ reg = <0>;
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@C0000 {
++ label = "uboot-env";
++ reg = <0xC0000 0x40000>;
++ };
++
++ partition@100000 {
++ label = "factory";
++ reg = <0x100000 0x40000>;
++ };
++
++ partition@140000 {
++ label = "kernel";
++ reg = <0x140000 0x2000000>;
++ };
++
++ partition@2140000 {
++ label = "recovery";
++ reg = <0x2140000 0x2000000>;
++ };
++
++ partition@4140000 {
++ label = "rootfs";
++ reg = <0x4140000 0x1000000>;
++ };
++ };
++ };
++};
++&bch {
++ status = "okay";
++};
++
+&usb1 {
+ vusb33-supply = <&mt6323_vusb_reg>;
+ vbus-supply = <&usb_p1_vbus>;
+ mediatek,reset-pin = <&pio 15 0>;
+ status = "okay";
+};
+diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
+new file mode 100644
+index 0000000..cbbdf16
--- /dev/null
+++ b/arch/arm/boot/dts/mt7623.dtsi
-@@ -0,0 +1,593 @@
+@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: John Crispin <blogic@openwrt.org>
+ compatible = "mediatek,mt2701-nfc";
+ reg = <0 0x1100d000 0 0x1000>;
+ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>;
++ power-domains = <&scpsys MT2701_POWER_DOMAIN_IFR_MSC>;
+ clocks = <&pericfg CLK_PERI_NFI>,
+ <&pericfg CLK_PERI_NFI_PAD>;
+ clock-names = "nfi_clk", "pad_clk";
+ compatible = "mediatek,eth-mac";
+ reg = <1>;
+
-+ phy-handle = <&phy5>;
+ status = "disabled";
++
++ phy-mode = "rgmii";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ pause;
++ };
+ };
+
+ mdio-bus {
+ status = "disabled";
+ };
+};
+diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig
+index 37dd438..7fb605e 100644
--- a/arch/arm/mach-mediatek/Kconfig
+++ b/arch/arm/mach-mediatek/Kconfig
@@ -21,6 +21,10 @@ config MACH_MT6592
config MACH_MT8127
bool "MediaTek MT8127 SoCs support"
default ARCH_MEDIATEK
+diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c
+index d019a08..bcfca37 100644
--- a/arch/arm/mach-mediatek/mediatek.c
+++ b/arch/arm/mach-mediatek/mediatek.c
-@@ -46,6 +46,7 @@ static void __init mediatek_timer_init(v
+@@ -46,6 +46,7 @@ static void __init mediatek_timer_init(void)
static const char * const mediatek_board_dt_compat[] = {
"mediatek,mt6589",
"mediatek,mt6592",
"mediatek,mt8127",
"mediatek,mt8135",
NULL,
+--
+1.7.10.4
+
-From 427a938858630fe4cec1b3829624676a4106d236 Mon Sep 17 00:00:00 2001
+From 05be818061b9f2a0fa5ad0cde6881917ff14a2f2 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 6 Jan 2016 21:55:10 +0100
-Subject: [PATCH 24/91] dt-bindings: add MediaTek PCIe binding documentation
+Subject: [PATCH 024/102] dt-bindings: add MediaTek PCIe binding documentation
Signed-off-by: John Crispin <blogic@openwrt.org>
---
1 file changed, 140 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/mediatek-pcie.txt
+diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.txt b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt
+new file mode 100644
+index 0000000..8fea3ed
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt
@@ -0,0 +1,140 @@
+ status = "okay";
+ };
+ };
+--
+1.7.10.4
+
-From 5571cc63036daf0e0a05f07b0137fee86d58acb0 Mon Sep 17 00:00:00 2001
+From 8ab1d4e0a9a68e03f472dee1c036a01d0198c20c Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Tue, 5 Jan 2016 20:20:04 +0100
-Subject: [PATCH 25/91] PCI: mediatek: add support for PCIe found on
+Subject: [PATCH 025/102] PCI: mediatek: add support for PCIe found on
MT7623/MT2701
Add PCIe controller support on MediaTek MT2701/MT7623. The driver supports
4 files changed, 654 insertions(+)
create mode 100644 drivers/pci/host/pcie-mediatek.c
+diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig
+index 7fb605e..a7fef77 100644
--- a/arch/arm/mach-mediatek/Kconfig
+++ b/arch/arm/mach-mediatek/Kconfig
@@ -24,6 +24,7 @@ config MACH_MT6592
config MACH_MT8127
bool "MediaTek MT8127 SoCs support"
+diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
+index f131ba9..912f0e1 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
-@@ -173,4 +173,15 @@ config PCI_HISI
+@@ -172,4 +172,15 @@ config PCI_HISI
help
Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC
+ PCIe include one Host/PCI bridge and 3 PCIe MAC.
+
endmenu
+diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
+index 9d4d3c6..3b53374 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
-@@ -20,3 +20,4 @@ obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-ip
+@@ -20,3 +20,4 @@ obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
+obj-$(CONFIG_PCIE_MTK) += pcie-mediatek.o
+diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
+new file mode 100644
+index 0000000..ef03952
--- /dev/null
+++ b/drivers/pci/host/pcie-mediatek.c
@@ -0,0 +1,641 @@
+}
+
+module_init(mtk_pcie_init);
+--
+1.7.10.4
+
-From a366216a08408949eca2d7823273da6826d3c483 Mon Sep 17 00:00:00 2001
+From 59aafd667d2880c90776931b6102b8252214d93c Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 21 Feb 2016 13:52:12 +0100
-Subject: [PATCH 26/91] scpsys: various fixes
+Subject: [PATCH 026/102] scpsys: various fixes
---
drivers/clk/mediatek/clk-mt2701.c | 2 ++
include/dt-bindings/power/mt2701-power.h | 4 ++--
3 files changed, 4 insertions(+), 10 deletions(-)
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+index 0e40bb8..812b347 100644
--- a/drivers/clk/mediatek/clk-mt2701.c
+++ b/drivers/clk/mediatek/clk-mt2701.c
-@@ -1043,6 +1043,8 @@ static void __init mtk_ethsys_init(struc
+@@ -1043,6 +1043,8 @@ static void __init mtk_ethsys_init(struct device_node *node)
if (r)
pr_err("%s(): could not register clock provider: %d\n",
__func__, r);
}
CLK_OF_DECLARE(mtk_ethsys, "mediatek,mt2701-ethsys", mtk_ethsys_init);
+diff --git a/drivers/soc/mediatek/mtk-scpsys-mt2701.c b/drivers/soc/mediatek/mtk-scpsys-mt2701.c
+index 3a31946..19489bc 100644
--- a/drivers/soc/mediatek/mtk-scpsys-mt2701.c
+++ b/drivers/soc/mediatek/mtk-scpsys-mt2701.c
-@@ -61,14 +61,6 @@ static const struct scp_domain_data scp_
+@@ -61,14 +61,6 @@ static const struct scp_domain_data scp_domain_data[] = {
.bus_prot_mask = MT2701_TOP_AXI_PROT_EN_DISP,
.active_wakeup = true,
},
[MT2701_POWER_DOMAIN_VDEC] = {
.name = "vdec",
.sta_mask = VDE_PWR_STA_MASK,
+diff --git a/include/dt-bindings/power/mt2701-power.h b/include/dt-bindings/power/mt2701-power.h
+index 64cc826..c168597 100644
--- a/include/dt-bindings/power/mt2701-power.h
+++ b/include/dt-bindings/power/mt2701-power.h
@@ -16,12 +16,12 @@
+#define MT2701_POWER_DOMAIN_IFR_MSC 2
#endif /* _DT_BINDINGS_POWER_MT2701_POWER_H */
+--
+1.7.10.4
+
-From 4d02177361d13355d98a38830c69bb9add3c109c Mon Sep 17 00:00:00 2001
+From 55231d8299d3dccde8588ed2e86c2bc0ef2e12ce Mon Sep 17 00:00:00 2001
From: Henry Chen <henryc.chen@mediatek.com>
Date: Mon, 4 Jan 2016 20:02:52 +0800
-Subject: [PATCH 27/91] soc: mediatek: PMIC wrap: Clear the vldclr if state
+Subject: [PATCH 027/102] soc: mediatek: PMIC wrap: Clear the vldclr if state
machine stay on FSM_VLDCLR state.
Sometimes PMIC is too busy to send data in time to cause pmic wrap timeout,
drivers/soc/mediatek/mtk-pmic-wrap.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 105597a..696071b 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
-@@ -412,6 +412,20 @@ static bool pwrap_is_fsm_vldclr(struct p
+@@ -412,6 +412,20 @@ static bool pwrap_is_fsm_vldclr(struct pmic_wrapper *wrp)
return PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR;
}
static bool pwrap_is_sync_idle(struct pmic_wrapper *wrp)
{
return pwrap_readl(wrp, PWRAP_WACS2_RDATA) & PWRAP_STATE_SYNC_IDLE0;
-@@ -445,8 +459,10 @@ static int pwrap_write(struct pmic_wrapp
+@@ -445,8 +459,10 @@ static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
int ret;
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
pwrap_writel(wrp, (1 << 31) | ((adr >> 1) << 16) | wdata,
PWRAP_WACS2_CMD);
-@@ -459,8 +475,10 @@ static int pwrap_read(struct pmic_wrappe
+@@ -459,8 +475,10 @@ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
int ret;
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
pwrap_writel(wrp, (adr >> 1) << 16, PWRAP_WACS2_CMD);
+--
+1.7.10.4
+
-From e4a5c39f75a11ecb78d1243b19b929af54f888fa Mon Sep 17 00:00:00 2001
+From d088a94afc768683a881b627b6737442158e7db6 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Tue, 5 Jan 2016 17:24:28 +0100
-Subject: [PATCH 28/91] ARM: mediatek: add MT7623 smp bringup code
+Subject: [PATCH 028/102] ARM: mediatek: add MT7623 smp bringup code
Add support for booting secondary CPUs on MT7623.
arch/arm/mach-mediatek/platsmp.c | 7 +++++++
1 file changed, 7 insertions(+)
+diff --git a/arch/arm/mach-mediatek/platsmp.c b/arch/arm/mach-mediatek/platsmp.c
+index 8141f3f..8151400 100644
--- a/arch/arm/mach-mediatek/platsmp.c
+++ b/arch/arm/mach-mediatek/platsmp.c
-@@ -44,6 +44,12 @@ static const struct mtk_smp_boot_info mt
+@@ -44,6 +44,12 @@ static const struct mtk_smp_boot_info mtk_mt6589_boot = {
{ 0x38, 0x3c, 0x40 },
};
static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = {
{ .compatible = "mediatek,mt8135", .data = &mtk_mt8135_tz_boot },
{ .compatible = "mediatek,mt8127", .data = &mtk_mt8135_tz_boot },
-@@ -51,6 +57,7 @@ static const struct of_device_id mtk_tz_
+@@ -51,6 +57,7 @@ static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = {
static const struct of_device_id mtk_smp_boot_infos[] __initconst = {
{ .compatible = "mediatek,mt6589", .data = &mtk_mt6589_boot },
};
static void __iomem *mtk_smp_base;
+--
+1.7.10.4
+
-From b4a6293df00036129d26a7f06bfb220ba5a73c42 Mon Sep 17 00:00:00 2001
+From b92861fbc79b3a7a9bc1c51e2dbfa2c191cc27ea Mon Sep 17 00:00:00 2001
From: Henry Chen <henryc.chen@mediatek.com>
Date: Thu, 21 Jan 2016 19:04:00 +0800
-Subject: [PATCH 29/91] soc: mediatek: PMIC wrap: clear the STAUPD_TRIG bit of
- WDT_SRC_EN
+Subject: [PATCH 029/102] soc: mediatek: PMIC wrap: clear the STAUPD_TRIG bit
+ of WDT_SRC_EN
Since STAUPD interrupts aren't handled on mt8173, disable watchdog timeout
monitor of STAUPD to avoid WDT_INT triggered by STAUPD.
drivers/soc/mediatek/mtk-pmic-wrap.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 696071b..0d9b19a 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -60,6 +60,15 @@
/* macro for slave device wrapper registers */
#define PWRAP_DEW_BASE 0xbc00
#define PWRAP_DEW_EVENT_OUT_EN (PWRAP_DEW_BASE + 0x0)
-@@ -822,7 +831,7 @@ MODULE_DEVICE_TABLE(of, of_pwrap_match_t
+@@ -822,7 +831,7 @@ MODULE_DEVICE_TABLE(of, of_pwrap_match_tbl);
static int pwrap_probe(struct platform_device *pdev)
{
struct pmic_wrapper *wrp;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
-@@ -912,7 +921,13 @@ static int pwrap_probe(struct platform_d
+@@ -912,7 +921,13 @@ static int pwrap_probe(struct platform_device *pdev)
/* Initialize watchdog, may not be done by the bootloader */
pwrap_writel(wrp, 0xf, PWRAP_WDT_UNIT);
pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
pwrap_writel(wrp, ~((1 << 31) | (1 << 1)), PWRAP_INT_EN);
+--
+1.7.10.4
+
-From 0befbd007b72ba2b14c65558d3bb72ea885496f6 Mon Sep 17 00:00:00 2001
+From f88ec31c6ba3a006d0be87ff1d99145f8cc85bee Mon Sep 17 00:00:00 2001
From: Louis Yu <louis.yu@mediatek.com>
Date: Thu, 7 Jan 2016 20:09:43 +0800
-Subject: [PATCH 30/91] ARM: mediatek: add mt2701 smp bringup code
+Subject: [PATCH 030/102] ARM: mediatek: add mt2701 smp bringup code
Add support for booting secondary CPUs on mt2701.
arch/arm/mach-mediatek/platsmp.c | 1 +
1 file changed, 1 insertion(+)
+diff --git a/arch/arm/mach-mediatek/platsmp.c b/arch/arm/mach-mediatek/platsmp.c
+index 8151400..2078f92d5 100644
--- a/arch/arm/mach-mediatek/platsmp.c
+++ b/arch/arm/mach-mediatek/platsmp.c
-@@ -53,6 +53,7 @@ static const struct mtk_smp_boot_info mt
+@@ -53,6 +53,7 @@ static const struct mtk_smp_boot_info mtk_mt7623_boot = {
static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = {
{ .compatible = "mediatek,mt8135", .data = &mtk_mt8135_tz_boot },
{ .compatible = "mediatek,mt8127", .data = &mtk_mt8135_tz_boot },
};
static const struct of_device_id mtk_smp_boot_infos[] __initconst = {
+--
+1.7.10.4
+
-From 9367fb14e1be8dd174f8d63ec83f7ee2d90ae733 Mon Sep 17 00:00:00 2001
+From 15f4d895578f02cbaed10b0f5f6853b873aba10b Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 13:12:19 +0100
-Subject: [PATCH 31/91] dt-bindings: ARM: Mediatek: add MT2701/7623 string to
- the PMIC wrapper doc
+Subject: [PATCH 031/102] dt-bindings: ARM: Mediatek: add MT2701/7623 string
+ to the PMIC wrapper doc
Signed-off-by: John Crispin <blogic@openwrt.org>
Acked-by: Rob Herring <robh@kernel.org>
Documentation/devicetree/bindings/soc/mediatek/pwrap.txt | 1 +
1 file changed, 1 insertion(+)
+diff --git a/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt b/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt
+index ddeb5b6..107700d 100644
--- a/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt
+++ b/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt
@@ -18,6 +18,7 @@ IP Pairing
"mediatek,mt8135-pwrap" for MT8135 SoCs
"mediatek,mt8173-pwrap" for MT8173 SoCs
- interrupts: IRQ for pwrap in SOC
+--
+1.7.10.4
+
-From 7b7d59b4219c30e1b9601300348f1431fdab7081 Mon Sep 17 00:00:00 2001
+From 64e8091be39c3f0a7bf4651bd2045b8c86429d55 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 06:42:01 +0100
-Subject: [PATCH 32/91] soc: mediatek: PMIC wrap: don't duplicate the wrapper
- data
+Subject: [PATCH 032/102] soc: mediatek: PMIC wrap: don't duplicate the
+ wrapper data
As we add support for more devices struct pmic_wrapper_type will grow and
we do not really want to start duplicating all the elements in
drivers/soc/mediatek/mtk-pmic-wrap.c | 22 ++++++++--------------
1 file changed, 8 insertions(+), 14 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 0d9b19a..340c4b5 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -376,9 +376,7 @@ struct pmic_wrapper {
}
static bool pwrap_is_fsm_idle(struct pmic_wrapper *wrp)
-@@ -697,7 +695,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -697,7 +695,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_WRAP_EN);
pwrap_writel(wrp, 1, PWRAP_WACS2_EN);
-@@ -742,7 +740,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -742,7 +740,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 0x1, PWRAP_CRC_EN);
pwrap_writel(wrp, 0x0, PWRAP_SIG_MODE);
pwrap_writel(wrp, PWRAP_DEW_CRC_VAL, PWRAP_SIG_ADR);
if (pwrap_is_mt8135(wrp))
pwrap_writel(wrp, 0x7, PWRAP_RRARB_EN);
-@@ -836,7 +834,6 @@ static int pwrap_probe(struct platform_d
+@@ -836,7 +834,6 @@ static int pwrap_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(of_pwrap_match_tbl, &pdev->dev);
struct resource *res;
wrp = devm_kzalloc(&pdev->dev, sizeof(*wrp), GFP_KERNEL);
-@@ -845,10 +842,7 @@ static int pwrap_probe(struct platform_d
+@@ -845,10 +842,7 @@ static int pwrap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wrp);
wrp->dev = &pdev->dev;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap");
+--
+1.7.10.4
+
-From 35d879d80437cc6ed811538903e115dbcda777ac Mon Sep 17 00:00:00 2001
+From 756b919b7874cc241a276b4fc5bbec5b3fb4bca8 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 05:27:17 +0100
-Subject: [PATCH 33/91] soc: mediatek: PMIC wrap: add wrapper callbacks for
+Subject: [PATCH 033/102] soc: mediatek: PMIC wrap: add wrapper callbacks for
init_reg_clock
Split init_reg_clock up into SoC specific callbacks. The patch also
drivers/soc/mediatek/mtk-pmic-wrap.c | 70 ++++++++++++++++++----------------
1 file changed, 38 insertions(+), 32 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 340c4b5..b22b664 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -354,24 +354,6 @@ enum pwrap_type {
static inline int pwrap_is_mt8135(struct pmic_wrapper *wrp)
{
return wrp->master->type == PWRAP_MT8135;
-@@ -578,20 +567,23 @@ static int pwrap_init_sidly(struct pmic_
+@@ -578,20 +567,23 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp)
return 0;
}
return 0;
}
-@@ -699,7 +691,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -699,7 +691,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_WACS2_EN);
if (ret)
return ret;
-@@ -814,6 +806,20 @@ static const struct regmap_config pwrap_
+@@ -814,6 +806,20 @@ static const struct regmap_config pwrap_regmap_config = {
.max_register = 0xffff,
};
static struct of_device_id of_pwrap_match_tbl[] = {
{
.compatible = "mediatek,mt8135-pwrap",
+--
+1.7.10.4
+
-From d82889cec95358b917fcf29fc3214980deb138b9 Mon Sep 17 00:00:00 2001
+From a1bbd630710d5da89a9c347c84d7badd30e7e68a Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 10:12:00 +0100
-Subject: [PATCH 34/91] soc: mediatek: PMIC wrap: split SoC specific init into
- callback
+Subject: [PATCH 034/102] soc: mediatek: PMIC wrap: split SoC specific init
+ into callback
This patch moves the SoC specific wrapper init code into separate callback
to avoid pwrap_init() getting too large. This is done by adding a new
drivers/soc/mediatek/mtk-pmic-wrap.c | 67 +++++++++++++++++++++-------------
1 file changed, 42 insertions(+), 25 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index b22b664..22c89e9 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -372,6 +372,7 @@ struct pmic_wrapper_type {
};
static inline int pwrap_is_mt8135(struct pmic_wrapper *wrp)
-@@ -665,6 +666,41 @@ static int pwrap_init_cipher(struct pmic
+@@ -665,6 +666,41 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
return 0;
}
static int pwrap_init(struct pmic_wrapper *wrp)
{
int ret;
-@@ -743,31 +779,10 @@ static int pwrap_init(struct pmic_wrappe
+@@ -743,31 +779,10 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 0x5, PWRAP_STAUPD_PRD);
pwrap_writel(wrp, 0xff, PWRAP_STAUPD_GRPEN);
}
/* Setup the init done registers */
-@@ -811,6 +826,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -811,6 +826,7 @@ static struct pmic_wrapper_type pwrap_mt8135 = {
.type = PWRAP_MT8135,
.arb_en_all = 0x1ff,
.init_reg_clock = pwrap_mt8135_init_reg_clock,
};
static struct pmic_wrapper_type pwrap_mt8173 = {
-@@ -818,6 +834,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -818,6 +834,7 @@ static struct pmic_wrapper_type pwrap_mt8173 = {
.type = PWRAP_MT8173,
.arb_en_all = 0x3f,
.init_reg_clock = pwrap_mt8173_init_reg_clock,
};
static struct of_device_id of_pwrap_match_tbl[] = {
+--
+1.7.10.4
+
-From 613acba0068461948e6b5283df03d7c1e1583a40 Mon Sep 17 00:00:00 2001
+From 274fd9ba57170de88bbdf522cbd6c290c2e51fb8 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 10:14:39 +0100
-Subject: [PATCH 35/91] soc: mediatek: PMIC wrap: WRAP_INT_EN needs a
+Subject: [PATCH 035/102] soc: mediatek: PMIC wrap: WRAP_INT_EN needs a
different bitmask for MT2701/7623
MT2701 and MT7623 use a different bitmask for PWRAP_INT_EN.
drivers/soc/mediatek/mtk-pmic-wrap.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 22c89e9..9df1135 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -371,6 +371,7 @@ struct pmic_wrapper_type {
int (*init_reg_clock)(struct pmic_wrapper *wrp);
int (*init_soc_specific)(struct pmic_wrapper *wrp);
};
-@@ -825,6 +826,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -825,6 +826,7 @@ static struct pmic_wrapper_type pwrap_mt8135 = {
.regs = mt8135_regs,
.type = PWRAP_MT8135,
.arb_en_all = 0x1ff,
.init_reg_clock = pwrap_mt8135_init_reg_clock,
.init_soc_specific = pwrap_mt8135_init_soc_specific,
};
-@@ -833,6 +835,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -833,6 +835,7 @@ static struct pmic_wrapper_type pwrap_mt8173 = {
.regs = mt8173_regs,
.type = PWRAP_MT8173,
.arb_en_all = 0x3f,
.init_reg_clock = pwrap_mt8173_init_reg_clock,
.init_soc_specific = pwrap_mt8173_init_soc_specific,
};
-@@ -946,7 +949,7 @@ static int pwrap_probe(struct platform_d
+@@ -946,7 +949,7 @@ static int pwrap_probe(struct platform_device *pdev)
PWRAP_WDT_SRC_MASK_NO_STAUPD : PWRAP_WDT_SRC_MASK_ALL;
pwrap_writel(wrp, wdt_src, PWRAP_WDT_SRC_EN);
pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH,
+--
+1.7.10.4
+
-From 1186088ab86b7286e1920dcbfbbbf2627a0daeda Mon Sep 17 00:00:00 2001
+From 511e697282c6425950b95373ac8dc59a42fd2485 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 10:21:42 +0100
-Subject: [PATCH 36/91] soc: mediatek: PMIC wrap: SPI_WRITE needs a different
- bitmask for MT2701/7623
+Subject: [PATCH 036/102] soc: mediatek: PMIC wrap: SPI_WRITE needs a
+ different bitmask for MT2701/7623
Different SoCs will use different bitmask for the SPI_WRITE command. This
patch defines the bitmask in the pmic_wrapper_type struct. This allows us
drivers/soc/mediatek/mtk-pmic-wrap.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 9df1135..8ce1bad 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -372,6 +372,7 @@ struct pmic_wrapper_type {
int (*init_reg_clock)(struct pmic_wrapper *wrp);
int (*init_soc_specific)(struct pmic_wrapper *wrp);
};
-@@ -511,15 +512,15 @@ static int pwrap_reset_spislave(struct p
+@@ -511,15 +512,15 @@ static int pwrap_reset_spislave(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_MAN_EN);
pwrap_writel(wrp, 0, PWRAP_DIO_EN);
PWRAP_MAN_CMD);
ret = pwrap_wait_for_state(wrp, pwrap_is_sync_idle);
-@@ -827,6 +828,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -827,6 +828,7 @@ static struct pmic_wrapper_type pwrap_mt8135 = {
.type = PWRAP_MT8135,
.arb_en_all = 0x1ff,
.int_en_all = ~(BIT(31) | BIT(1)),
.init_reg_clock = pwrap_mt8135_init_reg_clock,
.init_soc_specific = pwrap_mt8135_init_soc_specific,
};
-@@ -836,6 +838,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -836,6 +838,7 @@ static struct pmic_wrapper_type pwrap_mt8173 = {
.type = PWRAP_MT8173,
.arb_en_all = 0x3f,
.int_en_all = ~(BIT(31) | BIT(1)),
.init_reg_clock = pwrap_mt8173_init_reg_clock,
.init_soc_specific = pwrap_mt8173_init_soc_specific,
};
+--
+1.7.10.4
+
-From 95f72db32afd545b88eaa04802736f1f84242a9f Mon Sep 17 00:00:00 2001
+From 6aecbc79322efd3068c6140f74a68654fbe5b5f6 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 10:48:35 +0100
-Subject: [PATCH 37/91] soc: mediatek: PMIC wrap: move wdt_src into the
+Subject: [PATCH 037/102] soc: mediatek: PMIC wrap: move wdt_src into the
pmic_wrapper_type struct
Different SoCs will use different bitmask for the wdt_src. This patch
drivers/soc/mediatek/mtk-pmic-wrap.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 8ce1bad..aa54df3 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -373,6 +373,7 @@ struct pmic_wrapper_type {
int (*init_reg_clock)(struct pmic_wrapper *wrp);
int (*init_soc_specific)(struct pmic_wrapper *wrp);
};
-@@ -829,6 +830,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -829,6 +830,7 @@ static struct pmic_wrapper_type pwrap_mt8135 = {
.arb_en_all = 0x1ff,
.int_en_all = ~(BIT(31) | BIT(1)),
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.init_reg_clock = pwrap_mt8135_init_reg_clock,
.init_soc_specific = pwrap_mt8135_init_soc_specific,
};
-@@ -839,6 +841,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -839,6 +841,7 @@ static struct pmic_wrapper_type pwrap_mt8173 = {
.arb_en_all = 0x3f,
.int_en_all = ~(BIT(31) | BIT(1)),
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.init_reg_clock = pwrap_mt8173_init_reg_clock,
.init_soc_specific = pwrap_mt8173_init_soc_specific,
};
-@@ -858,7 +861,7 @@ MODULE_DEVICE_TABLE(of, of_pwrap_match_t
+@@ -858,7 +861,7 @@ MODULE_DEVICE_TABLE(of, of_pwrap_match_tbl);
static int pwrap_probe(struct platform_device *pdev)
{
struct pmic_wrapper *wrp;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
-@@ -948,9 +951,7 @@ static int pwrap_probe(struct platform_d
+@@ -948,9 +951,7 @@ static int pwrap_probe(struct platform_device *pdev)
* Since STAUPD was not used on mt8173 platform,
* so STAUPD of WDT_SRC which should be turned off
*/
pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN);
+--
+1.7.10.4
+
-From bb19fd13b1ed629873ea144b22c4764aa4baa5ef Mon Sep 17 00:00:00 2001
+From da09b34ad22e8f065a02af114668f7d86357244a Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 10:54:18 +0100
-Subject: [PATCH 38/91] soc: mediatek: PMIC wrap: remove pwrap_is_mt8135() and
- pwrap_is_mt8173()
+Subject: [PATCH 038/102] soc: mediatek: PMIC wrap: remove pwrap_is_mt8135()
+ and pwrap_is_mt8173()
With more SoCs being added the list of helper functions like these would
grow. To mitigate this problem we remove the existing helpers and change
drivers/soc/mediatek/mtk-pmic-wrap.c | 28 ++++++++++++----------------
1 file changed, 12 insertions(+), 16 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index aa54df3..a2bacda 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -374,20 +374,11 @@ struct pmic_wrapper_type {
static u32 pwrap_readl(struct pmic_wrapper *wrp, enum pwrap_regs reg)
{
return readl(wrp->base + wrp->master->regs[reg]);
-@@ -619,11 +610,14 @@ static int pwrap_init_cipher(struct pmic
+@@ -619,11 +610,14 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 0x1, PWRAP_CIPHER_KEY_SEL);
pwrap_writel(wrp, 0x2, PWRAP_CIPHER_IV_SEL);
}
/* Config cipher mode @PMIC */
-@@ -713,7 +707,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -713,7 +707,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
if (wrp->rstc_bridge)
reset_control_reset(wrp->rstc_bridge);
/* Enable DCM */
pwrap_writel(wrp, 3, PWRAP_DCM_EN);
pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD);
-@@ -773,7 +767,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -773,7 +767,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, PWRAP_DEW_CRC_VAL, PWRAP_SIG_ADR);
pwrap_writel(wrp, wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN);
pwrap_writel(wrp, 0x7, PWRAP_RRARB_EN);
pwrap_writel(wrp, 0x1, PWRAP_WACS0_EN);
-@@ -793,7 +787,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -793,7 +787,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_INIT_DONE0);
pwrap_writel(wrp, 1, PWRAP_INIT_DONE1);
writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE3);
writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE4);
}
-@@ -831,6 +825,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -831,6 +825,7 @@ static struct pmic_wrapper_type pwrap_mt8135 = {
.int_en_all = ~(BIT(31) | BIT(1)),
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.init_reg_clock = pwrap_mt8135_init_reg_clock,
.init_soc_specific = pwrap_mt8135_init_soc_specific,
};
-@@ -842,6 +837,7 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -842,6 +837,7 @@ static struct pmic_wrapper_type pwrap_mt8173 = {
.int_en_all = ~(BIT(31) | BIT(1)),
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_NO_STAUPD,
.init_reg_clock = pwrap_mt8173_init_reg_clock,
.init_soc_specific = pwrap_mt8173_init_soc_specific,
};
-@@ -889,7 +885,7 @@ static int pwrap_probe(struct platform_d
+@@ -889,7 +885,7 @@ static int pwrap_probe(struct platform_device *pdev)
return ret;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"pwrap-bridge");
wrp->bridge_base = devm_ioremap_resource(wrp->dev, res);
+--
+1.7.10.4
+
-From daa4d054bb0557799c8b324d7aa5f0a3a4a7b078 Mon Sep 17 00:00:00 2001
+From 21bdcd324f769545b1765fe391d939a1edd07cbb Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 09:55:08 +0100
-Subject: [PATCH 39/91] soc: mediatek: PMIC wrap: add a slave specific struct
+Subject: [PATCH 039/102] soc: mediatek: PMIC wrap: add a slave specific
+ struct
This patch adds a new struct pwrap_slv_type that we use to store the slave
specific data. The patch adds 2 new helper functions to access the dew
drivers/soc/mediatek/mtk-pmic-wrap.c | 159 ++++++++++++++++++++++++----------
1 file changed, 112 insertions(+), 47 deletions(-)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index a2bacda..bcc841e 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -69,33 +69,54 @@
struct clk *clk_spi;
struct clk *clk_wrap;
struct reset_control *rstc;
-@@ -544,7 +575,8 @@ static int pwrap_init_sidly(struct pmic_
+@@ -544,7 +575,8 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp)
for (i = 0; i < 4; i++) {
pwrap_writel(wrp, i, PWRAP_SIDLY);
if (rdata == PWRAP_DEW_READ_TEST_VAL) {
dev_dbg(wrp->dev, "[Read Test] pass, SIDLY=%x\n", i);
pass |= 1 << i;
-@@ -593,7 +625,8 @@ static bool pwrap_is_pmic_cipher_ready(s
+@@ -593,7 +625,8 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp)
u32 rdata;
int ret;
if (ret)
return 0;
-@@ -621,12 +654,12 @@ static int pwrap_init_cipher(struct pmic
+@@ -621,12 +654,12 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
}
/* Config cipher mode @PMIC */
/* wait for cipher data ready@AP */
ret = pwrap_wait_for_state(wrp, pwrap_is_cipher_ready);
-@@ -643,7 +676,7 @@ static int pwrap_init_cipher(struct pmic
+@@ -643,7 +676,7 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
}
/* wait for cipher mode idle */
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle);
if (ret) {
dev_err(wrp->dev, "cipher mode idle fail, ret=%d\n", ret);
-@@ -653,9 +686,11 @@ static int pwrap_init_cipher(struct pmic
+@@ -653,9 +686,11 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_CIPHER_MODE);
/* Write Test */
dev_err(wrp->dev, "rdata=0x%04X\n", rdata);
return -EFAULT;
}
-@@ -677,8 +712,10 @@ static int pwrap_mt8135_init_soc_specifi
+@@ -677,8 +712,10 @@ static int pwrap_mt8135_init_soc_specific(struct pmic_wrapper *wrp)
writel(0x7ff, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INT_EN);
/* enable PMIC event out and sources */
dev_err(wrp->dev, "enable dewrap fail\n");
return -EFAULT;
}
-@@ -689,8 +726,10 @@ static int pwrap_mt8135_init_soc_specifi
+@@ -689,8 +726,10 @@ static int pwrap_mt8135_init_soc_specific(struct pmic_wrapper *wrp)
static int pwrap_mt8173_init_soc_specific(struct pmic_wrapper *wrp)
{
/* PMIC_DEWRAP enables */
dev_err(wrp->dev, "enable dewrap fail\n");
return -EFAULT;
}
-@@ -734,7 +773,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -734,7 +773,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
return ret;
/* Enable dual IO mode */
/* Check IDLE & INIT_DONE in advance */
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle);
-@@ -746,7 +785,7 @@ static int pwrap_init(struct pmic_wrappe
+@@ -746,7 +785,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_DIO_EN);
/* Read Test */
if (rdata != PWRAP_DEW_READ_TEST_VAL) {
dev_err(wrp->dev, "Read test failed after switch to DIO mode: 0x%04x != 0x%04x\n",
PWRAP_DEW_READ_TEST_VAL, rdata);
-@@ -759,12 +798,13 @@ static int pwrap_init(struct pmic_wrappe
+@@ -759,12 +798,13 @@ static int pwrap_init(struct pmic_wrapper *wrp)
return ret;
/* Signature checking - using CRC */
pwrap_writel(wrp, wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN);
if (wrp->master->type == PWRAP_MT8135)
-@@ -818,6 +858,21 @@ static const struct regmap_config pwrap_
+@@ -818,6 +858,21 @@ static const struct regmap_config pwrap_regmap_config = {
.max_register = 0xffff,
};
static struct pmic_wrapper_type pwrap_mt8135 = {
.regs = mt8135_regs,
.type = PWRAP_MT8135,
-@@ -862,8 +917,17 @@ static int pwrap_probe(struct platform_d
+@@ -862,8 +917,17 @@ static int pwrap_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(of_pwrap_match_tbl, &pdev->dev);
wrp = devm_kzalloc(&pdev->dev, sizeof(*wrp), GFP_KERNEL);
if (!wrp)
return -ENOMEM;
-@@ -871,6 +935,7 @@ static int pwrap_probe(struct platform_d
+@@ -871,6 +935,7 @@ static int pwrap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wrp);
wrp->master = of_id->data;
wrp->dev = &pdev->dev;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap");
+--
+1.7.10.4
+
-From 15143b59a26a06e890e2ba3c9944b3f751ce39bd Mon Sep 17 00:00:00 2001
+From 4418ba9a0bb105f00259d10ceb16f9e27199e9b0 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 11:40:43 +0100
-Subject: [PATCH 40/91] soc: mediatek: PMIC wrap: add mt6323 slave support
+Subject: [PATCH 040/102] soc: mediatek: PMIC wrap: add mt6323 slave support
Add support for MT6323 slaves. This PMIC can be found on MT2701 and MT7623
EVB. The only function that we need to touch is pwrap_init_cipher().
drivers/soc/mediatek/mtk-pmic-wrap.c | 43 ++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index bcc841e..0e4ebb8 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -93,6 +93,27 @@ enum dew_regs {
PMIC_MT6397,
};
-@@ -661,6 +683,19 @@ static int pwrap_init_cipher(struct pmic
+@@ -661,6 +683,19 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_LOAD], 0x1);
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_START], 0x1);
/* wait for cipher data ready@AP */
ret = pwrap_wait_for_state(wrp, pwrap_is_cipher_ready);
if (ret) {
-@@ -858,6 +893,11 @@ static const struct regmap_config pwrap_
+@@ -858,6 +893,11 @@ static const struct regmap_config pwrap_regmap_config = {
.max_register = 0xffff,
};
static const struct pwrap_slv_type pmic_mt6397 = {
.dew_regs = mt6397_regs,
.type = PMIC_MT6397,
-@@ -865,6 +905,9 @@ static const struct pwrap_slv_type pmic_
+@@ -865,6 +905,9 @@ static const struct pwrap_slv_type pmic_mt6397 = {
static const struct of_device_id of_slave_match_tbl[] = {
{
.compatible = "mediatek,mt6397",
.data = &pmic_mt6397,
}, {
+--
+1.7.10.4
+
-From 2f5df30a7b913069c8fce22dc702e0d7c76ef361 Mon Sep 17 00:00:00 2001
+From 7736d97fe2c6c71c9009a1b45a94de06bfc94a37 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 20 Jan 2016 12:09:14 +0100
-Subject: [PATCH 41/91] soc: mediatek: PMIC wrap: add MT2701/7623 support
+Subject: [PATCH 041/102] soc: mediatek: PMIC wrap: add MT2701/7623 support
Add the registers, callbacks and data structures required to make the
wrapper work on MT2701 and MT7623.
drivers/soc/mediatek/mtk-pmic-wrap.c | 154 ++++++++++++++++++++++++++++++++++
1 file changed, 154 insertions(+)
+diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
+index 0e4ebb8..3c3e56d 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -52,6 +52,7 @@
PWRAP_MT8135,
PWRAP_MT8173,
};
-@@ -637,6 +732,31 @@ static int pwrap_mt8173_init_reg_clock(s
+@@ -637,6 +732,31 @@ static int pwrap_mt8173_init_reg_clock(struct pmic_wrapper *wrp)
return 0;
}
static bool pwrap_is_cipher_ready(struct pmic_wrapper *wrp)
{
return pwrap_readl(wrp, PWRAP_CIPHER_RDY) & 1;
-@@ -670,6 +790,7 @@ static int pwrap_init_cipher(struct pmic
+@@ -670,6 +790,7 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_CIPHER_LOAD);
pwrap_writel(wrp, 1, PWRAP_CIPHER_START);
break;
case PWRAP_MT8173:
pwrap_writel(wrp, 1, PWRAP_CIPHER_EN);
break;
-@@ -772,6 +893,24 @@ static int pwrap_mt8173_init_soc_specifi
+@@ -772,6 +893,24 @@ static int pwrap_mt8173_init_soc_specific(struct pmic_wrapper *wrp)
return 0;
}
static int pwrap_init(struct pmic_wrapper *wrp)
{
int ret;
-@@ -916,6 +1055,18 @@ static const struct of_device_id of_slav
+@@ -916,6 +1055,18 @@ static const struct of_device_id of_slave_match_tbl[] = {
};
MODULE_DEVICE_TABLE(of, of_slave_match_tbl);
static struct pmic_wrapper_type pwrap_mt8135 = {
.regs = mt8135_regs,
.type = PWRAP_MT8135,
-@@ -942,6 +1093,9 @@ static struct pmic_wrapper_type pwrap_mt
+@@ -942,6 +1093,9 @@ static struct pmic_wrapper_type pwrap_mt8173 = {
static struct of_device_id of_pwrap_match_tbl[] = {
{
.compatible = "mediatek,mt8135-pwrap",
.data = &pwrap_mt8135,
}, {
+--
+1.7.10.4
+
-From edc6e6a2f10f7b7fc94dc6147c86520e5a439d16 Mon Sep 17 00:00:00 2001
+From c14dc2993a272c706650502ec579ceabe5f2355e Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 10 Jan 2016 17:12:37 +0100
-Subject: [PATCH 42/91] dt-bindings: mfd: Add bindings for the MediaTek MT6323
- PMIC
+Subject: [PATCH 042/102] dt-bindings: mfd: Add bindings for the MediaTek
+ MT6323 PMIC
Signed-off-by: John Crispin <blogic@openwrt.org>
Acked-by: Rob Herring <robh@kernel.org>
Documentation/devicetree/bindings/mfd/mt6397.txt | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
+diff --git a/Documentation/devicetree/bindings/mfd/mt6397.txt b/Documentation/devicetree/bindings/mfd/mt6397.txt
+index 15043e6..949c85f 100644
--- a/Documentation/devicetree/bindings/mfd/mt6397.txt
+++ b/Documentation/devicetree/bindings/mfd/mt6397.txt
@@ -1,6 +1,6 @@
- Regulator
- RTC
- Audio codec
-@@ -8,14 +8,14 @@ MT6397 is a multifunction device with th
+@@ -8,14 +8,14 @@ MT6397 is a multifunction device with the following sub modules:
- Clock
It is interfaced to host controller using SPI interface by a proprietary hardware
- codec
Required properties:
- compatible: "mediatek,mt6397-codec"
+--
+1.7.10.4
+
-From f97549172878651725a719a4fc4b610613fe5843 Mon Sep 17 00:00:00 2001
+From 8269ed007349714e9ef0e3408a68159d763145dd Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Fri, 8 Jan 2016 08:33:17 +0100
-Subject: [PATCH 43/91] mfd: mt6397: int_con and int_status may vary in
+Subject: [PATCH 043/102] mfd: mt6397: int_con and int_status may vary in
location
MT6323 has the INT_CON and INT_STATUS located at a different position.
include/linux/mfd/mt6397/core.h | 2 ++
2 files changed, 19 insertions(+), 10 deletions(-)
+diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
+index 1749c1c..75ad0fe 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
-@@ -69,8 +69,10 @@ static void mt6397_irq_sync_unlock(struc
+@@ -69,8 +69,10 @@ static void mt6397_irq_sync_unlock(struct irq_data *data)
{
struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
mutex_unlock(&mt6397->irqlock);
}
-@@ -147,8 +149,8 @@ static irqreturn_t mt6397_irq_thread(int
+@@ -147,8 +149,8 @@ static irqreturn_t mt6397_irq_thread(int irq, void *data)
{
struct mt6397_chip *mt6397 = data;
return IRQ_HANDLED;
}
-@@ -177,8 +179,8 @@ static int mt6397_irq_init(struct mt6397
+@@ -177,8 +179,8 @@ static int mt6397_irq_init(struct mt6397_chip *mt6397)
mutex_init(&mt6397->irqlock);
/* Mask all interrupt sources */
mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node,
MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397);
-@@ -203,8 +205,8 @@ static int mt6397_irq_suspend(struct dev
+@@ -203,8 +205,8 @@ static int mt6397_irq_suspend(struct device *dev)
{
struct mt6397_chip *chip = dev_get_drvdata(dev);
enable_irq_wake(chip->irq);
-@@ -215,8 +217,8 @@ static int mt6397_irq_resume(struct devi
+@@ -215,8 +217,8 @@ static int mt6397_irq_resume(struct device *dev)
{
struct mt6397_chip *chip = dev_get_drvdata(dev);
disable_irq_wake(chip->irq);
-@@ -237,6 +239,11 @@ static int mt6397_probe(struct platform_
+@@ -237,6 +239,11 @@ static int mt6397_probe(struct platform_device *pdev)
return -ENOMEM;
mt6397->dev = &pdev->dev;
/*
* mt6397 MFD is child device of soc pmic wrapper.
* Regmap is set from its parent.
+diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h
+index 45b8e8a..d678f52 100644
--- a/include/linux/mfd/mt6397/core.h
+++ b/include/linux/mfd/mt6397/core.h
@@ -60,6 +60,8 @@ struct mt6397_chip {
};
#endif /* __MFD_MT6397_CORE_H__ */
+--
+1.7.10.4
+
-From 5fbdf1ebc267561781ce812793cd35e63fa39614 Mon Sep 17 00:00:00 2001
+From c6c447480e51301faa2254c7316ab075e20c4b0c Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Fri, 8 Jan 2016 08:41:52 +0100
-Subject: [PATCH 44/91] mfd: mt6397: add support for different Slave types
+Subject: [PATCH 044/102] mfd: mt6397: add support for different Slave types
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/mfd/mt6397-core.c | 58 ++++++++++++++++++++++++++++++++-------------
1 file changed, 41 insertions(+), 17 deletions(-)
+diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
+index 75ad0fe..aa91606 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -24,6 +24,9 @@
static const struct resource mt6397_rtc_resources[] = {
{
.start = MT6397_RTC_BASE,
-@@ -232,39 +235,60 @@ static SIMPLE_DEV_PM_OPS(mt6397_pm_ops,
+@@ -232,39 +235,60 @@ static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_irq_suspend,
static int mt6397_probe(struct platform_device *pdev)
{
int ret;
return ret;
}
+--
+1.7.10.4
+
-From 2a1c7879d8c3eac4313abc011adbefbc50fd5f92 Mon Sep 17 00:00:00 2001
+From 0ae7153c9f00361c3e6dac9da0c2d994557953f5 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Fri, 8 Jan 2016 04:09:43 +0100
-Subject: [PATCH 45/91] mfd: mt6397: add MT6323 support to MT6397 driver
+Subject: [PATCH 045/102] mfd: mt6397: add MT6323 support to MT6397 driver
Signed-off-by: John Crispin <blogic@openwrt.org>
---
create mode 100644 include/linux/mfd/mt6323/core.h
create mode 100644 include/linux/mfd/mt6323/registers.h
+diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
+index aa91606..8234cd3 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -19,11 +19,14 @@
#define MT6391_CID_CODE 0x91
#define MT6397_CID_CODE 0x97
-@@ -40,6 +43,13 @@ static const struct resource mt6397_rtc_
+@@ -40,6 +43,13 @@ static const struct resource mt6397_rtc_resources[] = {
},
};
static const struct mfd_cell mt6397_devs[] = {
{
.name = "mt6397-rtc",
-@@ -261,6 +271,15 @@ static int mt6397_probe(struct platform_
+@@ -261,6 +271,15 @@ static int mt6397_probe(struct platform_device *pdev)
}
switch (id & 0xff) {
case MT6397_CID_CODE:
case MT6391_CID_CODE:
pmic->int_con[0] = MT6397_INT_CON0;
-@@ -302,6 +321,7 @@ static int mt6397_remove(struct platform
+@@ -302,6 +321,7 @@ static int mt6397_remove(struct platform_device *pdev)
static const struct of_device_id mt6397_of_match[] = {
{ .compatible = "mediatek,mt6397" },
{ }
};
MODULE_DEVICE_TABLE(of, mt6397_of_match);
+diff --git a/include/linux/mfd/mt6323/core.h b/include/linux/mfd/mt6323/core.h
+new file mode 100644
+index 0000000..06d0ec3
--- /dev/null
+++ b/include/linux/mfd/mt6323/core.h
@@ -0,0 +1,36 @@
+};
+
+#endif /* __MFD_MT6323_CORE_H__ */
+diff --git a/include/linux/mfd/mt6323/registers.h b/include/linux/mfd/mt6323/registers.h
+new file mode 100644
+index 0000000..160f3c0
--- /dev/null
+++ b/include/linux/mfd/mt6323/registers.h
@@ -0,0 +1,408 @@
+#define MT6323_ACCDET_CON16 0x079A
+
+#endif /* __MFD_MT6323_REGISTERS_H__ */
+--
+1.7.10.4
+
-From 34177561c62ed881c862f9ece652ca1ca5994796 Mon Sep 17 00:00:00 2001
+From f536a600e0e20fd57475415ce5b3d909441d53b6 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 10 Jan 2016 17:31:46 +0100
-Subject: [PATCH 46/91] regulator: Add document for MT6323 regulator
+Subject: [PATCH 046/102] regulator: Add document for MT6323 regulator
Signed-off-by: John Crispin <blogic@openwrt.org>
Cc: devicetree@vger.kernel.org
1 file changed, 239 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/mt6323-regulator.txt
+diff --git a/Documentation/devicetree/bindings/regulator/mt6323-regulator.txt b/Documentation/devicetree/bindings/regulator/mt6323-regulator.txt
+new file mode 100644
+index 0000000..9fd95e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mt6323-regulator.txt
@@ -0,0 +1,239 @@
+ };
+ };
+ };
+--
+1.7.10.4
+
-From 2a33aa927dece6ac6d10caff48897c8ac6a66c1b Mon Sep 17 00:00:00 2001
+From 94c08223cd696d872cda7d9aa4e817956d0a0b84 Mon Sep 17 00:00:00 2001
From: Chen Zhong <chen.zhong@mediatek.com>
Date: Fri, 8 Jan 2016 04:17:37 +0100
-Subject: [PATCH 47/91] regulator: mt6323: Add support for MT6323 regulator
+Subject: [PATCH 047/102] regulator: mt6323: Add support for MT6323 regulator
The MT6323 is a regulator found on boards based on MediaTek MT7623 and
probably other SoCs. It is a so called pmic and connects as a slave to
create mode 100644 drivers/regulator/mt6323-regulator.c
create mode 100644 include/linux/regulator/mt6323-regulator.h
+diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
+index 8df0b0e..4aec931 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
-@@ -453,6 +453,15 @@ config REGULATOR_MT6311
+@@ -452,6 +452,15 @@ config REGULATOR_MT6311
This driver supports the control of different power rails of device
through regulator interface.
config REGULATOR_MT6397
tristate "MediaTek MT6397 PMIC"
depends on MFD_MT6397
+diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
+index 0f81749..b42a84e 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
-@@ -60,6 +60,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc137
+@@ -60,6 +60,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
+diff --git a/drivers/regulator/mt6323-regulator.c b/drivers/regulator/mt6323-regulator.c
+new file mode 100644
+index 0000000..28ebbda
--- /dev/null
+++ b/drivers/regulator/mt6323-regulator.c
@@ -0,0 +1,432 @@
+MODULE_AUTHOR("Chen Zhong <chen.zhong@mediatek.com>");
+MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6397 PMIC");
+MODULE_LICENSE("GPL v2");
+diff --git a/include/linux/regulator/mt6323-regulator.h b/include/linux/regulator/mt6323-regulator.h
+new file mode 100644
+index 0000000..67011cd
--- /dev/null
+++ b/include/linux/regulator/mt6323-regulator.h
@@ -0,0 +1,52 @@
+#define MT6323_MAX_REGULATOR MT6323_ID_RG_MAX
+
+#endif /* __LINUX_REGULATOR_MT6323_H */
+--
+1.7.10.4
+
-From caa2186644606dad07a603905ebabb8068828ebf Mon Sep 17 00:00:00 2001
+From 6efc8d9081b70dcf71d7e8efd7b51d48ee2541be Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 2 Mar 2016 07:18:52 +0100
-Subject: [PATCH 48/91] net-next: mediatek: document MediaTek SoC ethernet
+Subject: [PATCH 048/102] net-next: mediatek: document MediaTek SoC ethernet
binding
This adds the binding documentation for the MediaTek Ethernet
1 file changed, 77 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/mediatek-net.txt
+diff --git a/Documentation/devicetree/bindings/net/mediatek-net.txt b/Documentation/devicetree/bindings/net/mediatek-net.txt
+new file mode 100644
+index 0000000..5ca7929
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mediatek-net.txt
@@ -0,0 +1,77 @@
+ };
+ };
+};
+--
+1.7.10.4
+
-From 412449bacdb46b548fd08af19148019e2e979294 Mon Sep 17 00:00:00 2001
+From 8cc84aa65121135d7b120ce71b4f10f81230c818 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 2 Mar 2016 04:27:10 +0100
-Subject: [PATCH 49/91] net-next: mediatek: add support for MT7623 ethernet
+Subject: [PATCH 049/102] net-next: mediatek: add support for MT7623 ethernet
Add ethernet support for MediaTek SoCs from the MT7623 family. These have
dual GMAC. Depending on the exact version, there might be a built-in
create mode 100644 drivers/net/ethernet/mediatek/mtk_eth_soc.c
create mode 100644 drivers/net/ethernet/mediatek/mtk_eth_soc.h
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+new file mode 100644
+index 0000000..ba3afa5
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -0,0 +1,1807 @@
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Ethernet driver for MediaTek SoC");
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+new file mode 100644
+index 0000000..48a5292
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -0,0 +1,421 @@
+u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
+#endif /* MTK_ETH_H */
+--
+1.7.10.4
+
-From 8bc8e78ddec2c93d7fe3487dfdfeedd382e3b96f Mon Sep 17 00:00:00 2001
+From 31e907e5c3c2fc1c94d005bfccdd4a32b5a05f82 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 2 Mar 2016 04:32:43 +0100
-Subject: [PATCH 50/91] net-next: mediatek: add Kconfig and Makefile
+Subject: [PATCH 050/102] net-next: mediatek: add Kconfig and Makefile
This patch adds the Makefile and Kconfig required to make the driver build.
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 31c5e47..cd28b95 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -106,6 +106,7 @@ config LANTIQ_ETOP
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 071f84e..c62191f 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_JME) += jme.o
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..b0229f4
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/Kconfig
@@ -0,0 +1,17 @@
+ MediaTek MT2701/MT7623 chipset family.
+
+endif #NET_VENDOR_MEDIATEK
+diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
+new file mode 100644
+index 0000000..aa3f1c8
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -0,0 +1,5 @@
+#
+
+obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o
+--
+1.7.10.4
+
-From d9b93fb0d4021694a2b7e47981cd9de67e83aa05 Mon Sep 17 00:00:00 2001
+From 514e4ce65a5f1b5bfa3cbca153f672844f093f0e Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 2 Mar 2016 04:34:04 +0100
-Subject: [PATCH 51/91] net-next: mediatek: add an entry to MAINTAINERS
+Subject: [PATCH 051/102] net-next: mediatek: add an entry to MAINTAINERS
Add myself and Felix as the Maintainers for the MediaTek ethernet driver.
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 233f834..73f0592 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -6907,6 +6907,13 @@ F: include/uapi/linux/meye.h
+@@ -6902,6 +6902,13 @@ F: include/uapi/linux/meye.h
F: include/uapi/linux/ivtv*
F: include/uapi/linux/uvcvideo.h
MEDIATEK MT7601U WIRELESS LAN DRIVER
M: Jakub Kicinski <kubakici@wp.pl>
L: linux-wireless@vger.kernel.org
+--
+1.7.10.4
+
--- /dev/null
+From 5238c5d1d38661955ed3b52f45c46e00bfc9eb6e Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 7 Apr 2016 07:18:35 +0200
+Subject: [PATCH 052/102] clk: dont disable unused clocks
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/clk/clk.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
+index f13c3f4..5e9ddae 100644
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -233,7 +233,7 @@ unlock_out:
+ clk_enable_unlock(flags);
+ }
+
+-static bool clk_ignore_unused;
++static bool clk_ignore_unused = true;
+ static int __init clk_ignore_unused_setup(char *__unused)
+ {
+ clk_ignore_unused = true;
+--
+1.7.10.4
+
+++ /dev/null
-From 69a0df9dd942799651a7ec06b3cfe7fc43b2e32a Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Mon, 16 Nov 2015 14:37:35 +0100
-Subject: [PATCH 52/91] mtd: nand: add an mtd_to_nand() helper
-
-Some drivers are retrieving the nand_chip pointer using the container_of
-macro on a struct wrapping both the nand_chip and the mtd_info struct while
-the standard way of retrieving this pointer is through mtd->priv.
-Provide an helper to do that.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -719,6 +719,11 @@ struct nand_chip {
- void *priv;
- };
-
-+static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
-+{
-+ return mtd->priv;
-+}
-+
- /*
- * NAND Flash Manufacturer ID Codes
- */
--- /dev/null
+From c8fd103d6c07af5db47f061b70759b7c69169656 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 31 Mar 2016 06:46:51 +0200
+Subject: [PATCH 053/102] clk: mediatek: enable critical clocks
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/clk/mediatek/clk-mt2701.c | 22 ++++++++++++++++++++--
+ 1 file changed, 20 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+index 812b347..1634288 100644
+--- a/drivers/clk/mediatek/clk-mt2701.c
++++ b/drivers/clk/mediatek/clk-mt2701.c
+@@ -573,6 +573,20 @@ static const struct mtk_gate top_clks[] __initconst = {
+ GATE_TOP_AUD(CLK_TOP_AUD_I2S6_MCLK, "aud_i2s6_mclk", "aud_k6_src_div", 28),
+ };
+
++static struct clk_onecell_data *mt7623_top_clk_data __initdata;
++static struct clk_onecell_data *mt7623_pll_clk_data __initdata;
++
++static void __init mtk_clk_enable_critical(void)
++{
++ if (!mt7623_top_clk_data || !mt7623_pll_clk_data)
++ return;
++
++ clk_prepare_enable(mt7623_pll_clk_data->clks[CLK_APMIXED_ARMPLL]);
++ clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_MEM_SEL]);
++ clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_DDRPHYCFG_SEL]);
++ clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_RTC_SEL]);
++}
++
+ static void __init mtk_topckgen_init(struct device_node *node)
+ {
+ struct clk_onecell_data *clk_data;
+@@ -585,7 +599,7 @@ static void __init mtk_topckgen_init(struct device_node *node)
+ return;
+ }
+
+- clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
++ mt7623_top_clk_data = clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
+
+ mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
+ clk_data);
+@@ -606,6 +620,8 @@ static void __init mtk_topckgen_init(struct device_node *node)
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
++
++ mtk_clk_enable_critical();
+ }
+ CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt2701-topckgen", mtk_topckgen_init);
+
+@@ -1202,7 +1218,7 @@ static void __init mtk_apmixedsys_init(struct device_node *node)
+ struct clk_onecell_data *clk_data;
+ int r;
+
+- clk_data = mtk_alloc_clk_data(ARRAY_SIZE(apmixed_plls));
++ mt7623_pll_clk_data = clk_data = mtk_alloc_clk_data(ARRAY_SIZE(apmixed_plls));
+ if (!clk_data)
+ return;
+
+@@ -1213,6 +1229,8 @@ static void __init mtk_apmixedsys_init(struct device_node *node)
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
++
++ mtk_clk_enable_critical();
+ }
+ CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt2701-apmixedsys",
+ mtk_apmixedsys_init);
+--
+1.7.10.4
+
+++ /dev/null
-From 833645b92150d74642829c24c0ca1fbbdeccfb5c Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Tue, 1 Dec 2015 12:03:07 +0100
-Subject: [PATCH 53/91] mtd: nand: add nand_to_mtd() helper
-
-Add a new helper to retrieve the MTD device attached to a NAND chip.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -724,6 +724,11 @@ static inline struct nand_chip *mtd_to_n
- return mtd->priv;
- }
-
-+static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
-+{
-+ return &chip->mtd;
-+}
-+
- /*
- * NAND Flash Manufacturer ID Codes
- */
--- /dev/null
+From 1387d4f0ebf4b48c09f2ea0d27a02936c3fa0010 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 31 Mar 2016 02:26:37 +0200
+Subject: [PATCH 054/102] clk: mediatek: Export CPU mux clocks for CPU
+ frequency control
+
+This patch adds CPU mux clocks which are used by Mediatek cpufreq driver
+for intermediate clock source switching.
+
+Signed-off-by: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
+---
+ drivers/clk/mediatek/Makefile | 2 +-
+ drivers/clk/mediatek/clk-cpumux.c | 127 ++++++++++++++++++++++++++++++++
+ drivers/clk/mediatek/clk-cpumux.h | 22 ++++++
+ drivers/clk/mediatek/clk-mt2701.c | 8 ++
+ drivers/clk/mediatek/clk-mt8173.c | 23 ++++++
+ include/dt-bindings/clock/mt2701-clk.h | 3 +-
+ include/dt-bindings/clock/mt8173-clk.h | 4 +-
+ 7 files changed, 186 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/clk/mediatek/clk-cpumux.c
+ create mode 100644 drivers/clk/mediatek/clk-cpumux.h
+
+diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
+index 5b2b91b..76bfab6 100644
+--- a/drivers/clk/mediatek/Makefile
++++ b/drivers/clk/mediatek/Makefile
+@@ -1,4 +1,4 @@
+-obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
++obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o
+ obj-$(CONFIG_RESET_CONTROLLER) += reset.o
+ obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
+ obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
+diff --git a/drivers/clk/mediatek/clk-cpumux.c b/drivers/clk/mediatek/clk-cpumux.c
+new file mode 100644
+index 0000000..91b5238
+--- /dev/null
++++ b/drivers/clk/mediatek/clk-cpumux.c
+@@ -0,0 +1,127 @@
++/*
++ * Copyright (c) 2015 Linaro Ltd.
++ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/mfd/syscon.h>
++#include <linux/slab.h>
++
++#include "clk-mtk.h"
++#include "clk-cpumux.h"
++
++struct mtk_clk_cpumux {
++ struct clk_hw hw;
++ struct regmap *regmap;
++ u32 reg;
++ u32 mask;
++ u8 shift;
++};
++
++static inline struct mtk_clk_cpumux *to_clk_mux(struct clk_hw *_hw)
++{
++ return container_of(_hw, struct mtk_clk_cpumux, hw);
++}
++
++static u8 clk_cpumux_get_parent(struct clk_hw *hw)
++{
++ struct mtk_clk_cpumux *mux = to_clk_mux(hw);
++ int num_parents = clk_hw_get_num_parents(hw);
++ unsigned int val;
++
++ regmap_read(mux->regmap, mux->reg, &val);
++
++ val >>= mux->shift;
++ val &= mux->mask;
++
++ if (val >= num_parents)
++ return -EINVAL;
++
++ return val;
++}
++
++static int clk_cpumux_set_parent(struct clk_hw *hw, u8 index)
++{
++ struct mtk_clk_cpumux *mux = to_clk_mux(hw);
++ u32 mask, val;
++
++ val = index << mux->shift;
++ mask = mux->mask << mux->shift;
++
++ return regmap_update_bits(mux->regmap, mux->reg, mask, val);
++}
++
++static const struct clk_ops clk_cpumux_ops = {
++ .get_parent = clk_cpumux_get_parent,
++ .set_parent = clk_cpumux_set_parent,
++};
++
++static struct clk __init *mtk_clk_register_cpumux(const struct mtk_composite *mux,
++ struct regmap *regmap)
++{
++ struct mtk_clk_cpumux *cpumux;
++ struct clk *clk;
++ struct clk_init_data init;
++
++ cpumux = kzalloc(sizeof(*cpumux), GFP_KERNEL);
++ if (!cpumux)
++ return ERR_PTR(-ENOMEM);
++
++ init.name = mux->name;
++ init.ops = &clk_cpumux_ops;
++ init.parent_names = mux->parent_names;
++ init.num_parents = mux->num_parents;
++ init.flags = mux->flags;
++
++ cpumux->reg = mux->mux_reg;
++ cpumux->shift = mux->mux_shift;
++ cpumux->mask = BIT(mux->mux_width) - 1;
++ cpumux->regmap = regmap;
++ cpumux->hw.init = &init;
++
++ clk = clk_register(NULL, &cpumux->hw);
++ if (IS_ERR(clk))
++ kfree(cpumux);
++
++ return clk;
++}
++
++int __init mtk_clk_register_cpumuxes(struct device_node *node,
++ const struct mtk_composite *clks, int num,
++ struct clk_onecell_data *clk_data)
++{
++ int i;
++ struct clk *clk;
++ struct regmap *regmap;
++
++ regmap = syscon_node_to_regmap(node);
++ if (IS_ERR(regmap)) {
++ pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
++ PTR_ERR(regmap));
++ return PTR_ERR(regmap);
++ }
++
++ for (i = 0; i < num; i++) {
++ const struct mtk_composite *mux = &clks[i];
++
++ clk = mtk_clk_register_cpumux(mux, regmap);
++ if (IS_ERR(clk)) {
++ pr_err("Failed to register clk %s: %ld\n",
++ mux->name, PTR_ERR(clk));
++ continue;
++ }
++
++ clk_data->clks[mux->id] = clk;
++ }
++
++ return 0;
++}
+diff --git a/drivers/clk/mediatek/clk-cpumux.h b/drivers/clk/mediatek/clk-cpumux.h
+new file mode 100644
+index 0000000..52c769f
+--- /dev/null
++++ b/drivers/clk/mediatek/clk-cpumux.h
+@@ -0,0 +1,22 @@
++/*
++ * Copyright (c) 2015 Linaro Ltd.
++ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * 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.
++ */
++
++#ifndef __DRV_CLK_CPUMUX_H
++#define __DRV_CLK_CPUMUX_H
++
++int mtk_clk_register_cpumuxes(struct device_node *node,
++ const struct mtk_composite *clks, int num,
++ struct clk_onecell_data *clk_data);
++
++#endif /* __DRV_CLK_CPUMUX_H */
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+index 1634288..5c37fcb 100644
+--- a/drivers/clk/mediatek/clk-mt2701.c
++++ b/drivers/clk/mediatek/clk-mt2701.c
+@@ -18,6 +18,7 @@
+
+ #include "clk-mtk.h"
+ #include "clk-gate.h"
++#include "clk-cpumux.h"
+
+ #include <dt-bindings/clock/mt2701-clk.h>
+
+@@ -465,6 +466,10 @@ static const char * const cpu_parents[] __initconst = {
+ "mmpll"
+ };
+
++static const struct mtk_composite cpu_muxes[] __initconst = {
++ MUX(CLK_INFRA_CPUSEL, "infra_cpu_sel", cpu_parents, 0x0000, 2, 2),
++};
++
+ static const struct mtk_composite top_muxes[] __initconst = {
+ MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
+ 0x0040, 0, 3, INVALID_MUX_GATE_BIT),
+@@ -677,6 +682,9 @@ static void __init mtk_infrasys_init(struct device_node *node)
+ mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
+ clk_data);
+
++ mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
++ clk_data);
++
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
+index 227e356..b82c0e2 100644
+--- a/drivers/clk/mediatek/clk-mt8173.c
++++ b/drivers/clk/mediatek/clk-mt8173.c
+@@ -18,6 +18,7 @@
+
+ #include "clk-mtk.h"
+ #include "clk-gate.h"
++#include "clk-cpumux.h"
+
+ #include <dt-bindings/clock/mt8173-clk.h>
+
+@@ -526,6 +527,25 @@ static const char * const i2s3_b_ck_parents[] __initconst = {
+ "apll2_div5"
+ };
+
++static const char * const ca53_parents[] __initconst = {
++ "clk26m",
++ "armca7pll",
++ "mainpll",
++ "univpll"
++};
++
++static const char * const ca57_parents[] __initconst = {
++ "clk26m",
++ "armca15pll",
++ "mainpll",
++ "univpll"
++};
++
++static const struct mtk_composite cpu_muxes[] __initconst = {
++ MUX(CLK_INFRA_CA53SEL, "infra_ca53_sel", ca53_parents, 0x0000, 0, 2),
++ MUX(CLK_INFRA_CA57SEL, "infra_ca57_sel", ca57_parents, 0x0000, 2, 2),
++};
++
+ static const struct mtk_composite top_muxes[] __initconst = {
+ /* CLK_CFG_0 */
+ MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3),
+@@ -945,6 +965,9 @@ static void __init mtk_infrasys_init(struct device_node *node)
+ clk_data);
+ mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data);
+
++ mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
++ clk_data);
++
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+diff --git a/include/dt-bindings/clock/mt2701-clk.h b/include/dt-bindings/clock/mt2701-clk.h
+index 50972d1..a6c63b8 100644
+--- a/include/dt-bindings/clock/mt2701-clk.h
++++ b/include/dt-bindings/clock/mt2701-clk.h
+@@ -217,7 +217,8 @@
+ #define CLK_INFRA_PMICWRAP 17
+ #define CLK_INFRA_DDCCI 18
+ #define CLK_INFRA_CLK_13M 19
+-#define CLK_INFRA_NR 20
++#define CLK_INFRA_CPUSEL 20
++#define CLK_INFRA_NR 21
+
+ /* PERICFG */
+
+diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h
+index 7956ba1..c82ed7c 100644
+--- a/include/dt-bindings/clock/mt8173-clk.h
++++ b/include/dt-bindings/clock/mt8173-clk.h
+@@ -192,7 +192,9 @@
+ #define CLK_INFRA_PMICSPI 10
+ #define CLK_INFRA_PMICWRAP 11
+ #define CLK_INFRA_CLK_13M 12
+-#define CLK_INFRA_NR_CLK 13
++#define CLK_INFRA_CA53SEL 13
++#define CLK_INFRA_CA57SEL 14
++#define CLK_INFRA_NR_CLK 15
+
+ /* PERI_SYS */
+
+--
+1.7.10.4
+
+++ /dev/null
-From af8437ee10a6304da30ca479480102b464b39c82 Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Thu, 10 Dec 2015 09:00:39 +0100
-Subject: [PATCH 54/91] mtd: nand: add helpers to access ->priv
-
-Add two helpers to access the field reserved for private controller data.
-This makes it clearer what this field is reserved for and ease future
-refactoring.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -729,6 +729,16 @@ static inline struct mtd_info *nand_to_m
- return &chip->mtd;
- }
-
-+static inline void *nand_get_controller_data(struct nand_chip *chip)
-+{
-+ return chip->priv;
-+}
-+
-+static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
-+{
-+ chip->priv = priv;
-+}
-+
- /*
- * NAND Flash Manufacturer ID Codes
- */
--- /dev/null
+From 60f4e41b367bdb29530468c91c1e613b17a37755 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Mar 2016 23:48:53 +0200
+Subject: [PATCH 055/102] cpufreq: mediatek: add driver
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/cpufreq/Kconfig.arm | 9 +
+ drivers/cpufreq/Makefile | 1 +
+ drivers/cpufreq/mt7623-cpufreq.c | 389 ++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 399 insertions(+)
+ create mode 100644 drivers/cpufreq/mt7623-cpufreq.c
+
+diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
+index b1f8a73..baf945e 100644
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -81,6 +81,15 @@ config ARM_KIRKWOOD_CPUFREQ
+ This adds the CPUFreq driver for Marvell Kirkwood
+ SoCs.
+
++config ARM_MT7623_CPUFREQ
++ bool "Mediatek MT7623 CPUFreq support"
++ depends on ARCH_MEDIATEK && REGULATOR
++ depends on ARM || (ARM_CPU_TOPOLOGY && COMPILE_TEST)
++ depends on !CPU_THERMAL || THERMAL=y
++ select PM_OPP
++ help
++ This adds the CPUFreq driver support for Mediatek MT7623 SoC.
++
+ config ARM_MT8173_CPUFREQ
+ bool "Mediatek MT8173 CPUFreq support"
+ depends on ARCH_MEDIATEK && REGULATOR
+diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
+index c0af1a1..e198752 100644
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -57,6 +57,7 @@ obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o
+ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
+ obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o
+ obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
++obj-$(CONFIG_ARM_MT7623_CPUFREQ) += mt7623-cpufreq.o
+ obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt8173-cpufreq.o
+ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
+ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
+diff --git a/drivers/cpufreq/mt7623-cpufreq.c b/drivers/cpufreq/mt7623-cpufreq.c
+new file mode 100644
+index 0000000..8d154ce
+--- /dev/null
++++ b/drivers/cpufreq/mt7623-cpufreq.c
+@@ -0,0 +1,389 @@
++/*
++ * Copyright (c) 2015 Linaro Ltd.
++ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/cpu.h>
++#include <linux/cpu_cooling.h>
++#include <linux/cpufreq.h>
++#include <linux/cpumask.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_opp.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/thermal.h>
++
++#define VOLT_TOL (10000)
++
++/*
++ * When scaling the clock frequency of a CPU clock domain, the clock source
++ * needs to be switched to another stable PLL clock temporarily until
++ * the original PLL becomes stable at target frequency.
++ */
++struct mtk_cpu_dvfs_info {
++ struct device *cpu_dev;
++ struct regulator *proc_reg;
++ struct clk *cpu_clk;
++ struct clk *inter_clk;
++ struct thermal_cooling_device *cdev;
++ int intermediate_voltage;
++};
++
++static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
++{
++ return regulator_set_voltage(info->proc_reg, vproc,
++ vproc + VOLT_TOL);
++}
++
++static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
++ unsigned int index)
++{
++ struct cpufreq_frequency_table *freq_table = policy->freq_table;
++ struct clk *cpu_clk = policy->clk;
++ struct clk *armpll = clk_get_parent(cpu_clk);
++ struct mtk_cpu_dvfs_info *info = policy->driver_data;
++ struct device *cpu_dev = info->cpu_dev;
++ struct dev_pm_opp *opp;
++ long freq_hz, old_freq_hz;
++ int vproc, old_vproc, inter_vproc, target_vproc, ret;
++
++ inter_vproc = info->intermediate_voltage;
++
++ old_freq_hz = clk_get_rate(cpu_clk);
++ old_vproc = regulator_get_voltage(info->proc_reg);
++
++ freq_hz = freq_table[index].frequency * 1000;
++
++ rcu_read_lock();
++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
++ if (IS_ERR(opp)) {
++ rcu_read_unlock();
++ pr_err("cpu%d: failed to find OPP for %ld\n",
++ policy->cpu, freq_hz);
++ return PTR_ERR(opp);
++ }
++ vproc = dev_pm_opp_get_voltage(opp);
++ rcu_read_unlock();
++
++ /*
++ * If the new voltage or the intermediate voltage is higher than the
++ * current voltage, scale up voltage first.
++ */
++ target_vproc = (inter_vproc > vproc) ? inter_vproc : vproc;
++ if (old_vproc < target_vproc) {
++ ret = mtk_cpufreq_set_voltage(info, target_vproc);
++ if (ret) {
++ pr_err("cpu%d: failed to scale up voltage!\n",
++ policy->cpu);
++ mtk_cpufreq_set_voltage(info, old_vproc);
++ return ret;
++ }
++ }
++
++ /* Reparent the CPU clock to intermediate clock. */
++ ret = clk_set_parent(cpu_clk, info->inter_clk);
++ if (ret) {
++ pr_err("cpu%d: failed to re-parent cpu clock!\n",
++ policy->cpu);
++ mtk_cpufreq_set_voltage(info, old_vproc);
++ WARN_ON(1);
++ return ret;
++ }
++
++ /* Set the original PLL to target rate. */
++ ret = clk_set_rate(armpll, freq_hz);
++ if (ret) {
++ pr_err("cpu%d: failed to scale cpu clock rate!\n",
++ policy->cpu);
++ clk_set_parent(cpu_clk, armpll);
++ mtk_cpufreq_set_voltage(info, old_vproc);
++ return ret;
++ }
++
++ /* Set parent of CPU clock back to the original PLL. */
++ ret = clk_set_parent(cpu_clk, armpll);
++ if (ret) {
++ pr_err("cpu%d: failed to re-parent cpu clock!\n",
++ policy->cpu);
++ mtk_cpufreq_set_voltage(info, inter_vproc);
++ WARN_ON(1);
++ return ret;
++ }
++
++ /*
++ * If the new voltage is lower than the intermediate voltage or the
++ * original voltage, scale down to the new voltage.
++ */
++ if (vproc < inter_vproc || vproc < old_vproc) {
++ ret = mtk_cpufreq_set_voltage(info, vproc);
++ if (ret) {
++ pr_err("cpu%d: failed to scale down voltage!\n",
++ policy->cpu);
++ clk_set_parent(cpu_clk, info->inter_clk);
++ clk_set_rate(armpll, old_freq_hz);
++ clk_set_parent(cpu_clk, armpll);
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
++{
++ struct mtk_cpu_dvfs_info *info = policy->driver_data;
++ struct device_node *np = of_node_get(info->cpu_dev->of_node);
++
++ if (WARN_ON(!np))
++ return;
++
++ if (of_find_property(np, "#cooling-cells", NULL)) {
++ info->cdev = of_cpufreq_cooling_register(np,
++ policy->related_cpus);
++
++ if (IS_ERR(info->cdev)) {
++ dev_err(info->cpu_dev,
++ "running cpufreq without cooling device: %ld\n",
++ PTR_ERR(info->cdev));
++
++ info->cdev = NULL;
++ }
++ }
++
++ of_node_put(np);
++}
++
++static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
++{
++ struct device *cpu_dev;
++ struct regulator *proc_reg = ERR_PTR(-ENODEV);
++ struct clk *cpu_clk = ERR_PTR(-ENODEV);
++ struct clk *inter_clk = ERR_PTR(-ENODEV);
++ struct dev_pm_opp *opp;
++ unsigned long rate;
++ int ret;
++
++ cpu_dev = get_cpu_device(cpu);
++ if (!cpu_dev) {
++ pr_err("failed to get cpu%d device\n", cpu);
++ return -ENODEV;
++ }
++
++ cpu_clk = clk_get(cpu_dev, "cpu");
++ if (IS_ERR(cpu_clk)) {
++ if (PTR_ERR(cpu_clk) == -EPROBE_DEFER)
++ pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu);
++ else
++ pr_err("failed to get cpu clk for cpu%d\n", cpu);
++
++ ret = PTR_ERR(cpu_clk);
++ return ret;
++ }
++
++ inter_clk = clk_get(cpu_dev, "intermediate");
++ if (IS_ERR(inter_clk)) {
++ if (PTR_ERR(inter_clk) == -EPROBE_DEFER)
++ pr_warn("intermediate clk for cpu%d not ready, retry.\n",
++ cpu);
++ else
++ pr_err("failed to get intermediate clk for cpu%d\n",
++ cpu);
++
++ ret = PTR_ERR(inter_clk);
++ goto out_free_resources;
++ }
++
++ proc_reg = regulator_get_exclusive(cpu_dev, "proc");
++ if (IS_ERR(proc_reg)) {
++ if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
++ pr_warn("proc regulator for cpu%d not ready, retry.\n",
++ cpu);
++ else
++ pr_err("failed to get proc regulator for cpu%d\n",
++ cpu);
++
++ ret = PTR_ERR(proc_reg);
++ goto out_free_resources;
++ }
++
++ ret = dev_pm_opp_of_add_table(cpu_dev);
++ if (ret) {
++ pr_warn("no OPP table for cpu%d\n", cpu);
++ goto out_free_resources;
++ }
++
++ /* Search a safe voltage for intermediate frequency. */
++ rate = clk_get_rate(inter_clk);
++ rcu_read_lock();
++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
++ if (IS_ERR(opp)) {
++ rcu_read_unlock();
++ pr_err("failed to get intermediate opp for cpu%d\n", cpu);
++ ret = PTR_ERR(opp);
++ goto out_free_opp_table;
++ }
++ info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
++ rcu_read_unlock();
++
++ info->cpu_dev = cpu_dev;
++ info->proc_reg = proc_reg;
++ info->cpu_clk = cpu_clk;
++ info->inter_clk = inter_clk;
++
++ return 0;
++
++out_free_opp_table:
++ dev_pm_opp_of_remove_table(cpu_dev);
++
++out_free_resources:
++ if (!IS_ERR(proc_reg))
++ regulator_put(proc_reg);
++ if (!IS_ERR(cpu_clk))
++ clk_put(cpu_clk);
++ if (!IS_ERR(inter_clk))
++ clk_put(inter_clk);
++
++ return ret;
++}
++
++static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
++{
++ if (!IS_ERR(info->proc_reg))
++ regulator_put(info->proc_reg);
++ if (!IS_ERR(info->cpu_clk))
++ clk_put(info->cpu_clk);
++ if (!IS_ERR(info->inter_clk))
++ clk_put(info->inter_clk);
++
++ dev_pm_opp_of_remove_table(info->cpu_dev);
++}
++
++static int mtk_cpufreq_init(struct cpufreq_policy *policy)
++{
++ struct mtk_cpu_dvfs_info *info;
++ struct cpufreq_frequency_table *freq_table;
++ int ret;
++
++ info = kzalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return -ENOMEM;
++
++ ret = mtk_cpu_dvfs_info_init(info, policy->cpu);
++ if (ret) {
++ pr_err("%s failed to initialize dvfs info for cpu%d\n",
++ __func__, policy->cpu);
++ goto out_free_dvfs_info;
++ }
++
++ ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
++ if (ret) {
++ pr_err("failed to init cpufreq table for cpu%d: %d\n",
++ policy->cpu, ret);
++ goto out_release_dvfs_info;
++ }
++
++ ret = cpufreq_table_validate_and_show(policy, freq_table);
++ if (ret) {
++ pr_err("%s: invalid frequency table: %d\n", __func__, ret);
++ goto out_free_cpufreq_table;
++ }
++
++ /* CPUs in the same cluster share a clock and power domain. */
++ cpumask_setall(policy->cpus);
++ policy->driver_data = info;
++ policy->clk = info->cpu_clk;
++
++ return 0;
++
++out_free_cpufreq_table:
++ dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
++
++out_release_dvfs_info:
++ mtk_cpu_dvfs_info_release(info);
++
++out_free_dvfs_info:
++ kfree(info);
++
++ return ret;
++}
++
++static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
++{
++ struct mtk_cpu_dvfs_info *info = policy->driver_data;
++
++ cpufreq_cooling_unregister(info->cdev);
++ dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
++ mtk_cpu_dvfs_info_release(info);
++ kfree(info);
++
++ return 0;
++}
++
++static struct cpufreq_driver mt7623_cpufreq_driver = {
++ .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
++ .verify = cpufreq_generic_frequency_table_verify,
++ .target_index = mtk_cpufreq_set_target,
++ .get = cpufreq_generic_get,
++ .init = mtk_cpufreq_init,
++ .exit = mtk_cpufreq_exit,
++ .ready = mtk_cpufreq_ready,
++ .name = "mtk-cpufreq",
++ .attr = cpufreq_generic_attr,
++};
++
++static int mt7623_cpufreq_probe(struct platform_device *pdev)
++{
++ int ret;
++
++ ret = cpufreq_register_driver(&mt7623_cpufreq_driver);
++ if (ret)
++ pr_err("failed to register mtk cpufreq driver\n");
++
++ return ret;
++}
++
++static struct platform_driver mt7623_cpufreq_platdrv = {
++ .driver = {
++ .name = "mt7623-cpufreq",
++ },
++ .probe = mt7623_cpufreq_probe,
++};
++
++static int mt7623_cpufreq_driver_init(void)
++{
++ struct platform_device *pdev;
++ int err;
++
++ if (!of_machine_is_compatible("mediatek,mt7623"))
++ return -ENODEV;
++
++ err = platform_driver_register(&mt7623_cpufreq_platdrv);
++ if (err)
++ return err;
++
++ /*
++ * Since there's no place to hold device registration code and no
++ * device tree based way to match cpufreq driver yet, both the driver
++ * and the device registration codes are put here to handle defer
++ * probing.
++ */
++ pdev = platform_device_register_simple("mt7623-cpufreq", -1, NULL, 0);
++ if (IS_ERR(pdev)) {
++ pr_err("failed to register mtk-cpufreq platform device\n");
++ return PTR_ERR(pdev);
++ }
++
++ return 0;
++}
++device_initcall(mt7623_cpufreq_driver_init);
+--
+1.7.10.4
+
+++ /dev/null
-From f18fcf4468ffdce17747f3d331f998a7e9264142 Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Tue, 1 Dec 2015 12:03:06 +0100
-Subject: [PATCH 55/91] mtd: nand: embed an mtd_info structure into nand_chip
-
-Currently all NAND controller drivers are providing both the mtd_info and
-nand_chip struct and then let the NAND subsystem to initialize a few
-things before registering the mtd instance to the MTD layer.
-Embed an mtd_info field into nand_chip to add some consistency to all NAND
-controller drivers.
-This change will also help factorizing boilerplate code copied in all NAND
-drivers.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -540,6 +540,7 @@ struct nand_buffers {
-
- /**
- * struct nand_chip - NAND Private Flash Chip Data
-+ * @mtd: MTD device registered to the MTD framework
- * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
- * flash device
- * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
-@@ -640,6 +641,7 @@ struct nand_buffers {
- */
-
- struct nand_chip {
-+ struct mtd_info mtd;
- void __iomem *IO_ADDR_R;
- void __iomem *IO_ADDR_W;
-
--- /dev/null
+From f8cda0bc698706413b5dd6fde827f9a2601ac61b Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 31 Mar 2016 06:07:01 +0200
+Subject: [PATCH 056/102] arm: mediatek: make a7 timer work Signed-off-by:
+ John Crispin <blogic@openwrt.org>
+
+---
+ arch/arm/mach-mediatek/Kconfig | 1 +
+ arch/arm/mach-mediatek/mediatek.c | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig
+index a7fef77..2c05bc31 100644
+--- a/arch/arm/mach-mediatek/Kconfig
++++ b/arch/arm/mach-mediatek/Kconfig
+@@ -24,6 +24,7 @@ config MACH_MT6592
+ config MACH_MT7623
+ bool "MediaTek MT7623 SoCs support"
+ default ARCH_MEDIATEK
++ select HAVE_ARM_ARCH_TIMER
+ select MIGHT_HAVE_PCI
+
+ config MACH_MT8127
+diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c
+index bcfca37..7553a8c 100644
+--- a/arch/arm/mach-mediatek/mediatek.c
++++ b/arch/arm/mach-mediatek/mediatek.c
+@@ -29,6 +29,7 @@ static void __init mediatek_timer_init(void)
+ void __iomem *gpt_base;
+
+ if (of_machine_is_compatible("mediatek,mt6589") ||
++ of_machine_is_compatible("mediatek,mt7623") ||
+ of_machine_is_compatible("mediatek,mt8135") ||
+ of_machine_is_compatible("mediatek,mt8127")) {
+ /* turn on GPT6 which ungates arch timer clocks */
+--
+1.7.10.4
+
+++ /dev/null
-From 59d8570d4b61af8544fc295d5e83ab7c28294bb8 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 22 Mar 2016 03:52:07 +0100
-Subject: [PATCH 56/91] mtd: add get/set of_node/flash_node helpers
-
-We are going to begin using the mtd->dev.of_node field for MTD device
-nodes, so let's add helpers for it. Also, we'll be making some
-conversions on spi_nor (and nand_chip eventually) too, so get that ready
-with their own helpers.
-
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
-Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
----
- include/linux/mtd/mtd.h | 11 +++++++++++
- include/linux/mtd/nand.h | 11 +++++++++++
- include/linux/mtd/spi-nor.h | 11 +++++++++++
- 3 files changed, 33 insertions(+)
-
---- a/include/linux/mtd/mtd.h
-+++ b/include/linux/mtd/mtd.h
-@@ -258,6 +258,17 @@ struct mtd_info {
- int usecount;
- };
-
-+static inline void mtd_set_of_node(struct mtd_info *mtd,
-+ struct device_node *np)
-+{
-+ mtd->dev.of_node = np;
-+}
-+
-+static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
-+{
-+ return mtd->dev.of_node;
-+}
-+
- int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
- int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
- void **virt, resource_size_t *phys);
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -741,6 +741,17 @@ static inline void nand_set_controller_d
- chip->priv = priv;
- }
-
-+static inline void nand_set_flash_node(struct nand_chip *chip,
-+ struct device_node *np)
-+{
-+ chip->flash_node = np;
-+}
-+
-+static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
-+{
-+ return chip->flash_node;
-+}
-+
- /*
- * NAND Flash Manufacturer ID Codes
- */
---- a/include/linux/mtd/spi-nor.h
-+++ b/include/linux/mtd/spi-nor.h
-@@ -184,6 +184,17 @@ struct spi_nor {
- void *priv;
- };
-
-+static inline void spi_nor_set_flash_node(struct spi_nor *nor,
-+ struct device_node *np)
-+{
-+ nor->flash_node = np;
-+}
-+
-+static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
-+{
-+ return nor->flash_node;
-+}
-+
- /**
- * spi_nor_scan() - scan the SPI NOR
- * @nor: the spi_nor structure
+++ /dev/null
-From 0fe612b501f1d56d76b2858d2ae779c1e766d064 Mon Sep 17 00:00:00 2001
-From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
-Date: Wed, 2 Mar 2016 12:00:11 -0500
-Subject: [PATCH 57/91] mtd: mediatek: device tree docs for MTK Smart Device
- Gen1 NAND
-
-This patch adds documentation support for Smart Device Gen1 type of
-NAND controllers.
-
-Mediatek's SoC 2701 is one of the SoCs that implements this controller.
-
-Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
----
- .../devicetree/bindings/mtd/mtksdg1-nand.txt | 38 ++++++++++++++++++++
- 1 file changed, 38 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
-@@ -0,0 +1,38 @@
-+MTK Smart Device SoCs NAND controller DT binding
-+
-+Required properties:
-+- compatible: Should be "mediatek,mt2701-nfc".
-+- reg: The first contains base physical address and size of
-+ NAND controller's registers. The second contains base
-+ physical address and size of NAND ECC engine.
-+- interrupts: the NFC NFI interrupt, and the NFC ECC interrupt
-+- clocks: NAND controller clocks.
-+- clock-names: NAND controller clocks internal name.
-+- vmch-supply: NAND power supply.
-+- #address-cells: Partition address, should be set 1.
-+- #size-cells: Partition size, should be set 1.
-+
-+Optional properties:
-+
-+nand-on-flash-bbt: Use a flash based bad block table.
-+
-+Optional subnodes:
-+- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
-+
-+Example:
-+
-+ nand: nand@1100d000 {
-+ compatible = "mediatek,mt2701-nfc";
-+ reg = <0 0x1100d000 0 0x1000>, <0 0x1100e000 0 0x1000>;
-+ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>,
-+ <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
-+ clocks = <&pericfg CLK_PERI_NFI>, <&pericfg CLK_PERI_NFI_ECC>,
-+ <&pericfg CLK_PERI_NFI_PAD>;
-+ clock-names = "nfi_ck", "nfi_ecc_ck", "nfi_pad_ck";
-+ vmch-supply = <&mt6323_vmch_reg>;
-+ status = "disabled";
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ ...
-+ };
--- /dev/null
+From b9f9b937dd12dc57bd54a6c89b18eb40d4508424 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Tue, 15 Mar 2016 10:18:49 +0300
+Subject: [PATCH 057/102] net: mediatek: checking for IS_ERR() instead of NULL
+
+of_phy_connect() returns NULL on error, it never returns error pointers.
+
+Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index ba3afa5..9759fe5 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -186,9 +186,9 @@ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
+
+ phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
+ mtk_phy_link_adjust, 0, phy_mode);
+- if (IS_ERR(phydev)) {
++ if (!phydev) {
+ dev_err(eth->dev, "could not connect to PHY\n");
+- return PTR_ERR(phydev);
++ return -ENODEV;
+ }
+
+ dev_info(eth->dev,
+--
+1.7.10.4
+
+++ /dev/null
-From 24db36ad20239841b897efb41442841ebf5d2f78 Mon Sep 17 00:00:00 2001
-From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
-Date: Wed, 2 Mar 2016 12:00:12 -0500
-Subject: [PATCH 58/91] mtd: mediatek: driver for MTK Smart Device Gen1 NAND
-
-This patch adds support for mediatek's SDG1 NFC nand controller
-embedded in SoC 2701.
-
-UBIFS support has been successfully tested.
-
-Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
----
- drivers/mtd/nand/Kconfig | 6 +
- drivers/mtd/nand/Makefile | 1 +
- drivers/mtd/nand/mtksdg1_nand.c | 1535 +++++++++++++++++++++++++++++++++++
- drivers/mtd/nand/mtksdg1_nand_ecc.h | 75 ++
- drivers/mtd/nand/mtksdg1_nand_nfi.h | 119 +++
- 5 files changed, 1736 insertions(+)
- create mode 100644 drivers/mtd/nand/mtksdg1_nand.c
- create mode 100644 drivers/mtd/nand/mtksdg1_nand_ecc.h
- create mode 100644 drivers/mtd/nand/mtksdg1_nand_nfi.h
-
---- a/drivers/mtd/nand/Kconfig
-+++ b/drivers/mtd/nand/Kconfig
-@@ -546,4 +546,10 @@ config MTD_NAND_HISI504
- help
- Enables support for NAND controller on Hisilicon SoC Hip04.
-
-+config MTD_NAND_MTKSDG1
-+ tristate "Support for NAND controller on MTK Smart Device SoCs"
-+ depends on HAS_DMA
-+ help
-+ Enables support for NAND controller on MTK Smart Device SoCs.
-+
- endif # MTD_NAND
---- a/drivers/mtd/nand/Makefile
-+++ b/drivers/mtd/nand/Makefile
-@@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) +=
- obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
- obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
- obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
-+obj-$(CONFIG_MTD_NAND_MTKSDG1) += mtksdg1_nand.o
-
- nand-objs := nand_base.o nand_bbt.o nand_timings.o
---- /dev/null
-+++ b/drivers/mtd/nand/mtksdg1_nand.c
-@@ -0,0 +1,1535 @@
-+/*
-+ * MTK smart device NAND Flash controller driver.
-+ * Copyright (C) 2015-2016 MediaTek Inc.
-+ * Authors: Xiaolei Li <xiaolei.li@mediatek.com>
-+ * Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <linux/platform_device.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/interrupt.h>
-+#include <linux/of_mtd.h>
-+#include <linux/delay.h>
-+#include <linux/clk.h>
-+#include <linux/mtd/partitions.h>
-+#include <linux/mtd/nand.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/module.h>
-+
-+#include "mtksdg1_nand_nfi.h"
-+#include "mtksdg1_nand_ecc.h"
-+
-+#define MTK_IRQ_ECC "mtksdg1-nand-ecc"
-+#define MTK_IRQ_NFI "mtksdg1-nand-nfi"
-+#define MTK_NAME "mtksdg1-nand"
-+
-+#define KB(x) ((x) * 1024UL)
-+#define MB(x) (KB(x) * 1024UL)
-+
-+#define SECTOR_SHIFT (10)
-+#define SECTOR_SIZE (1UL << SECTOR_SHIFT)
-+#define BYTES_TO_SECTORS(x) ((x) >> SECTOR_SHIFT)
-+#define SECTORS_TO_BYTES(x) ((x) << SECTOR_SHIFT)
-+
-+#define MTK_TIMEOUT (500)
-+#define MTK_RESET_TIMEOUT (1 * HZ)
-+
-+#define MTK_ECC_PARITY_BITS (14)
-+#define MTK_NAND_MAX_CHIP (2)
-+
-+#define MTK_OOB_ON (1)
-+#define MTK_OOB_OFF (0)
-+
-+/* raw accesses do not use ECC (ecc = !raw) */
-+#define MTK_ECC_OFF (1)
-+#define MTK_ECC_ON (0)
-+
-+struct mtk_nfc_clk {
-+ struct clk *nfiecc_clk;
-+ struct clk *nfi_clk;
-+ struct clk *pad_clk;
-+};
-+
-+struct mtk_nfc_saved_reg {
-+ struct {
-+ u32 enccnfg;
-+ u32 deccnfg;
-+ } ecc;
-+ struct {
-+ u32 emp_thresh;
-+ u16 pagefmt;
-+ u32 acccon;
-+ u16 cnrnb;
-+ u16 csel;
-+ } nfi;
-+};
-+
-+struct mtk_nfc_host {
-+ struct mtk_nfc_clk clk;
-+ struct nand_chip chip;
-+ struct device *dev;
-+
-+ struct {
-+ struct completion complete;
-+ void __iomem *base;
-+ } nfi;
-+
-+ struct {
-+ struct completion complete;
-+ void __iomem *base;
-+ u32 dec_sec;
-+ } ecc;
-+
-+ u32 fdm_reg[MTKSDG1_NFI_FDM_REG_SIZE / sizeof(u32)];
-+ bool switch_oob;
-+ u32 row_nob;
-+ u8 *buffer;
-+
-+#ifdef CONFIG_PM_SLEEP
-+ struct mtk_nfc_saved_reg saved_reg;
-+#endif
-+};
-+
-+static struct nand_ecclayout nand_2k_64 = {
-+ .oobfree = { {0, 16} },
-+};
-+
-+static struct nand_ecclayout nand_4k_128 = {
-+ .oobfree = { {0, 32} },
-+};
-+
-+/* NFI register access */
-+static inline void mtk_nfi_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
-+{
-+ writel(val, host->nfi.base + reg);
-+}
-+static inline void mtk_nfi_writew(struct mtk_nfc_host *host, u16 val, u32 reg)
-+{
-+ writew(val, host->nfi.base + reg);
-+}
-+static inline u32 mtk_nfi_readl(struct mtk_nfc_host *host, u32 reg)
-+{
-+ return readl_relaxed(host->nfi.base + reg);
-+}
-+static inline u16 mtk_nfi_readw(struct mtk_nfc_host *host, u32 reg)
-+{
-+ return readw_relaxed(host->nfi.base + reg);
-+}
-+static inline u8 mtk_nfi_readb(struct mtk_nfc_host *host, u32 reg)
-+{
-+ return readb_relaxed(host->nfi.base + reg);
-+}
-+
-+/* ECC register access */
-+static inline void mtk_ecc_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
-+{
-+ writel(val, host->ecc.base + reg);
-+}
-+static inline void mtk_ecc_writew(struct mtk_nfc_host *host, u16 val, u32 reg)
-+{
-+ writew(val, host->ecc.base + reg);
-+}
-+static inline u32 mtk_ecc_readl(struct mtk_nfc_host *host, u32 reg)
-+{
-+ return readl_relaxed(host->ecc.base + reg);
-+}
-+static inline u16 mtk_ecc_readw(struct mtk_nfc_host *host, u32 reg)
-+{
-+ return readw_relaxed(host->ecc.base + reg);
-+}
-+
-+static void mtk_nfc_hw_reset(struct mtk_nfc_host *host)
-+{
-+ unsigned long timeout = MTK_RESET_TIMEOUT;
-+ struct device *dev = host->dev;
-+ u32 val;
-+
-+ /* reset the state machine, data fifo and fdm data */
-+ mtk_nfi_writel(host, CON_FIFO_FLUSH | CON_NFI_RST, MTKSDG1_NFI_CON);
-+ timeout += jiffies;
-+ do {
-+ val = mtk_nfi_readl(host, MTKSDG1_NFI_MASTER_STA);
-+ val &= MASTER_STA_MASK;
-+ if (!val)
-+ return;
-+ usleep_range(50, 100);
-+
-+ } while (time_before(jiffies, timeout));
-+
-+ dev_warn(dev, "nfi master active after in reset [0x%x] = 0x%x\n",
-+ MTKSDG1_NFI_MASTER_STA, val);
-+};
-+
-+static int mtk_nfc_set_command(struct mtk_nfc_host *host, u8 command)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ struct device *dev = host->dev;
-+ u32 val;
-+
-+ mtk_nfi_writel(host, command, MTKSDG1_NFI_CMD);
-+
-+ /* wait for the NFI core to enter command mode */
-+ timeout += jiffies;
-+ do {
-+ val = mtk_nfi_readl(host, MTKSDG1_NFI_STA);
-+ val &= STA_CMD;
-+ if (!val)
-+ return 0;
-+ cpu_relax();
-+
-+ } while (time_before(jiffies, timeout));
-+ dev_warn(dev, "nfi core timed out entering command mode\n");
-+
-+ return -EIO;
-+}
-+
-+static int mtk_nfc_set_address(struct mtk_nfc_host *host, u32 column, u32 row,
-+ u8 colnob, u8 row_nob)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ struct device *dev = host->dev;
-+ u32 addr_nob, val;
-+
-+ addr_nob = colnob | (row_nob << ADDR_ROW_NOB_SHIFT);
-+ mtk_nfi_writel(host, column, MTKSDG1_NFI_COLADDR);
-+ mtk_nfi_writel(host, row, MTKSDG1_NFI_ROWADDR);
-+ mtk_nfi_writel(host, addr_nob, MTKSDG1_NFI_ADDRNOB);
-+
-+ /* wait for the NFI core to enter address mode */
-+ timeout += jiffies;
-+ do {
-+ val = mtk_nfi_readl(host, MTKSDG1_NFI_STA);
-+ val &= STA_ADDR;
-+ if (!val)
-+ return 0;
-+ cpu_relax();
-+
-+ } while (time_before(jiffies, timeout));
-+
-+ dev_warn(dev, "nfi core timed out entering address mode\n");
-+
-+ return -EIO;
-+}
-+
-+static inline void mtk_ecc_encoder_idle(struct mtk_nfc_host *host)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ struct device *dev = host->dev;
-+ u32 val;
-+
-+ timeout += jiffies;
-+ do {
-+ val = mtk_ecc_readl(host, MTKSDG1_ECC_ENCIDLE);
-+ val &= ENC_IDLE;
-+ if (val)
-+ return;
-+ cpu_relax();
-+
-+ } while (time_before(jiffies, timeout));
-+
-+ dev_warn(dev, "hw init ecc encoder not idle\n");
-+}
-+
-+static inline void mtk_ecc_decoder_idle(struct mtk_nfc_host *host)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ struct device *dev = host->dev;
-+ u32 val;
-+
-+ timeout += jiffies;
-+ do {
-+ val = mtk_ecc_readw(host, MTKSDG1_ECC_DECIDLE);
-+ val &= DEC_IDLE;
-+ if (val)
-+ return;
-+ cpu_relax();
-+
-+ } while (time_before(jiffies, timeout));
-+
-+ dev_warn(dev, "hw init ecc decoder not idle\n");
-+}
-+
-+static int mtk_nfc_transfer_done(struct mtk_nfc_host *host, u32 sectors)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ u32 cnt;
-+
-+ /* wait for the sector count */
-+ timeout += jiffies;
-+ do {
-+ cnt = mtk_nfi_readl(host, MTKSDG1_NFI_ADDRCNTR);
-+ cnt &= CNTR_MASK;
-+ if (cnt >= sectors)
-+ return 0;
-+ cpu_relax();
-+
-+ } while (time_before(jiffies, timeout));
-+
-+ return -EIO;
-+}
-+
-+static int mtk_nfc_subpage_done(struct mtk_nfc_host *host, int sectors)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ u32 val;
-+
-+ timeout += jiffies;
-+ do {
-+ val = mtk_nfi_readl(host, MTKSDG1_NFI_BYTELEN);
-+ val &= CNTR_MASK;
-+ if (val >= sectors)
-+ return 0;
-+ cpu_relax();
-+
-+ } while (time_before(jiffies, timeout));
-+
-+ return -EIO;
-+}
-+
-+static inline int mtk_nfc_data_ready(struct mtk_nfc_host *host)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ u8 val;
-+
-+ timeout += jiffies;
-+ do {
-+ val = mtk_nfi_readw(host, MTKSDG1_NFI_PIO_DIRDY);
-+ val &= PIO_DI_RDY;
-+ if (val)
-+ return 0;
-+ cpu_relax();
-+
-+ } while (time_before(jiffies, timeout));
-+
-+ /* data _MUST_ not be accessed */
-+ return -EIO;
-+}
-+
-+static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
-+{
-+ struct nand_chip *chip = mtd_to_nand(mtd);
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ struct device *dev = host->dev;
-+ u32 dec_size, enc_size;
-+ u32 ecc_bit, ecc_level;
-+ u32 spare, fmt;
-+ u32 reg;
-+
-+ host->row_nob = 1;
-+ if (chip->chipsize > MB(32))
-+ host->row_nob = chip->chipsize > MB(128) ? 3 : 2;
-+
-+ spare = mtd->oobsize / BYTES_TO_SECTORS(mtd->writesize);
-+ switch (spare) {
-+ case 16:
-+ ecc_bit = ECC_CNFG_4BIT;
-+ ecc_level = 4;
-+ break;
-+ case 32:
-+ ecc_bit = ECC_CNFG_12BIT;
-+ ecc_level = 12;
-+ break;
-+ default:
-+ dev_err(dev, "invalid spare size per sector: %d\n", spare);
-+ return -EINVAL;
-+ }
-+
-+ chip->ecc.strength = ecc_level;
-+ chip->ecc.size = SECTOR_SIZE;
-+
-+ switch (mtd->writesize) {
-+ case KB(2):
-+ fmt = PAGEFMT_512_2K;
-+ chip->ecc.layout = &nand_2k_64;
-+ break;
-+ case KB(4):
-+ fmt = PAGEFMT_2K_4K;
-+ chip->ecc.layout = &nand_4k_128;
-+ break;
-+ case KB(8):
-+ fmt = PAGEFMT_4K_8K;
-+ break;
-+ default:
-+ dev_err(dev, "invalid page size: %d\n", mtd->writesize);
-+ return -EINVAL;
-+ }
-+
-+ /* configure PAGE FMT */
-+ reg = fmt;
-+ reg |= PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT;
-+ reg |= MTKSDG1_NFI_FDM_REG_SIZE << PAGEFMT_FDM_SHIFT;
-+ reg |= MTKSDG1_NFI_FDM_REG_SIZE << PAGEFMT_FDM_ECC_SHIFT;
-+ mtk_nfi_writew(host, reg, MTKSDG1_NFI_PAGEFMT);
-+
-+ /* configure ECC encoder (in bits) */
-+ enc_size = (SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE) << 3;
-+ reg = ecc_bit | ECC_NFI_MODE | (enc_size << ECC_MS_SHIFT);
-+ mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
-+
-+ /* configure ECC decoder (inbits) */
-+ dec_size = enc_size + ecc_level * MTK_ECC_PARITY_BITS;
-+ reg = ecc_bit | ECC_NFI_MODE | (dec_size << ECC_MS_SHIFT);
-+ reg |= (DEC_CNFG_CORRECT | DEC_EMPTY_EN);
-+ mtk_ecc_writel(host, reg, MTKSDG1_ECC_DECCNFG);
-+
-+ return 0;
-+}
-+
-+static void mtk_nfc_device_reset(struct mtk_nfc_host *host)
-+{
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ struct device *dev = host->dev;
-+ u16 chip;
-+ int rc;
-+
-+ mtk_nfc_hw_reset(host);
-+
-+ /* enable reset done interrupt */
-+ mtk_nfi_writew(host, INTR_RST_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+
-+ /* configure FSM for reset operation */
-+ mtk_nfi_writew(host, CNFG_OP_RESET, MTKSDG1_NFI_CNFG);
-+
-+ init_completion(&host->nfi.complete);
-+
-+ mtk_nfc_set_command(host, NAND_CMD_RESET);
-+ rc = wait_for_completion_timeout(&host->nfi.complete, timeout);
-+ if (!rc) {
-+ chip = mtk_nfi_readw(host, MTKSDG1_NFI_CSEL);
-+ dev_err(dev, "device(%d) reset timeout\n", chip);
-+ }
-+}
-+
-+static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip)
-+{
-+ struct nand_chip *nand = mtd_to_nand(mtd);
-+ struct mtk_nfc_host *host = nand_get_controller_data(nand);
-+
-+ if (chip < 0)
-+ return;
-+
-+ mtk_nfi_writel(host, chip, MTKSDG1_NFI_CSEL);
-+}
-+
-+static inline bool mtk_nfc_cmd_supported(unsigned command)
-+{
-+ switch (command) {
-+ case NAND_CMD_RESET:
-+ case NAND_CMD_READID:
-+ case NAND_CMD_STATUS:
-+ case NAND_CMD_READOOB:
-+ case NAND_CMD_ERASE1:
-+ case NAND_CMD_ERASE2:
-+ case NAND_CMD_SEQIN:
-+ case NAND_CMD_PAGEPROG:
-+ case NAND_CMD_CACHEDPROG:
-+ case NAND_CMD_READ0:
-+ return true;
-+ default:
-+ return false;
-+ }
-+}
-+
-+static void mtk_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
-+ int page_addr)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(mtd_to_nand(mtd));
-+ unsigned long const cmd_timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ struct completion *p = &host->nfi.complete;
-+ u32 val;
-+ int rc;
-+
-+ if (mtk_nfc_cmd_supported(command))
-+ mtk_nfc_hw_reset(host);
-+
-+ switch (command) {
-+ case NAND_CMD_RESET:
-+ mtk_nfc_device_reset(host);
-+ break;
-+ case NAND_CMD_READID:
-+ val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_SRD;
-+ mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+ mtk_nfc_set_command(host, NAND_CMD_READID);
-+ mtk_nfc_set_address(host, column, 0, 1, 0);
-+ mtk_nfi_writel(host, CON_SRD, MTKSDG1_NFI_CON);
-+ break;
-+ case NAND_CMD_STATUS:
-+ val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_SRD;
-+ mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+ mtk_nfc_set_command(host, NAND_CMD_STATUS);
-+ mtk_nfi_writel(host, CON_SRD, MTKSDG1_NFI_CON);
-+ break;
-+ case NAND_CMD_READOOB:
-+ val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_READ;
-+ mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+ mtk_nfc_set_command(host, NAND_CMD_READ0);
-+ column += mtd->writesize;
-+ mtk_nfc_set_address(host, column, page_addr, 2, host->row_nob);
-+ val = CON_BRD | (1 << CON_SEC_SHIFT);
-+ mtk_nfi_writel(host, val, MTKSDG1_NFI_CON);
-+ break;
-+ case NAND_CMD_ERASE1:
-+ mtk_nfi_writew(host, INTR_ERS_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+ mtk_nfi_writew(host, CNFG_OP_ERASE, MTKSDG1_NFI_CNFG);
-+ mtk_nfc_set_command(host, NAND_CMD_ERASE1);
-+ mtk_nfc_set_address(host, 0, page_addr, 0, host->row_nob);
-+ break;
-+ case NAND_CMD_ERASE2:
-+ init_completion(p);
-+ mtk_nfc_set_command(host, NAND_CMD_ERASE2);
-+ rc = wait_for_completion_timeout(p, cmd_timeout);
-+ if (!rc)
-+ dev_err(host->dev, "erase command timeout\n");
-+ break;
-+ case NAND_CMD_SEQIN:
-+ mtk_nfi_writew(host, CNFG_OP_PRGM, MTKSDG1_NFI_CNFG);
-+ mtk_nfc_set_command(host, NAND_CMD_SEQIN);
-+ mtk_nfc_set_address(host, column, page_addr, 2, host->row_nob);
-+ break;
-+ case NAND_CMD_PAGEPROG:
-+ case NAND_CMD_CACHEDPROG:
-+ mtk_nfi_writew(host, INTR_BUSY_RT_EN, MTKSDG1_NFI_INTR_EN);
-+ init_completion(p);
-+ mtk_nfc_set_command(host, command);
-+ rc = wait_for_completion_timeout(p, cmd_timeout);
-+ if (!rc)
-+ dev_err(host->dev, "pageprogr command timeout\n");
-+ break;
-+ case NAND_CMD_READ0:
-+ val = CNFG_OP_READ | CNFG_READ_EN;
-+ mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+ mtk_nfc_set_command(host, NAND_CMD_READ0);
-+ break;
-+ default:
-+ dev_warn(host->dev, "command 0x%x not supported\n", command);
-+ break;
-+ }
-+}
-+
-+static uint8_t mtk_nfc_read_byte(struct mtd_info *mtd)
-+{
-+ struct nand_chip *chip = mtd_to_nand(mtd);
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ int rc;
-+
-+ rc = mtk_nfc_data_ready(host);
-+ if (rc < 0) {
-+ dev_err(host->dev, "data not ready\n");
-+ return NAND_STATUS_FAIL;
-+ }
-+
-+ return mtk_nfi_readb(host, MTKSDG1_NFI_DATAR);
-+}
-+
-+static void mtk_nfc_write_fdm(struct nand_chip *chip, u32 sectors)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ u8 *src, *dst;
-+ int i, j, reg;
-+
-+ for (i = 0; i < sectors ; i++) {
-+ /* read FDM from OOB into private area */
-+ src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+ dst = (u8 *)host->fdm_reg;
-+ memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+
-+ /* write FDM to registers */
-+ for (j = 0; j < ARRAY_SIZE(host->fdm_reg); j++) {
-+ reg = MTKSDG1_NFI_FDM0L + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+ reg += j * sizeof(host->fdm_reg[0]);
-+ mtk_nfi_writel(host, host->fdm_reg[j], reg);
-+ }
-+ }
-+}
-+
-+static int mtk_nfc_write_page(struct mtd_info *mtd,
-+ struct nand_chip *chip, const uint8_t *buf,
-+ int oob_on, int page, int raw)
-+{
-+
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ struct completion *nfi = &host->nfi.complete;
-+ struct device *dev = host->dev;
-+ const bool use_ecc = !raw;
-+ void *q = (void *) buf;
-+ dma_addr_t dma_addr;
-+ size_t dmasize;
-+ u32 reg;
-+ int ret;
-+
-+ dmasize = mtd->writesize + (raw ? mtd->oobsize : 0);
-+
-+ dma_addr = dma_map_single(dev, q, dmasize, DMA_TO_DEVICE);
-+ if (dma_mapping_error(host->dev, dma_addr)) {
-+ dev_err(host->dev, "dma mapping error\n");
-+ return -EINVAL;
-+ }
-+
-+ reg = mtk_nfi_readw(host, MTKSDG1_NFI_CNFG);
-+ reg |= CNFG_AHB | CNFG_DMA_BURST_EN;
-+ if (use_ecc) {
-+ /**
-+ * OOB will be generated
-+ * - FDM: from register
-+ * - ECC: from HW
-+ */
-+ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
-+ mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+
-+ mtk_ecc_encoder_idle(host);
-+ mtk_ecc_writew(host, ENC_EN, MTKSDG1_ECC_ENCCON);
-+
-+ /* write OOB into the FDM registers (OOB area in MTK NAND) */
-+ if (oob_on)
-+ mtk_nfc_write_fdm(chip, chip->ecc.steps);
-+ } else {
-+ /* OOB is part of the DMA transfer */
-+ mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+ }
-+
-+ mtk_nfi_writel(host, chip->ecc.steps << CON_SEC_SHIFT, MTKSDG1_NFI_CON);
-+ mtk_nfi_writel(host, lower_32_bits(dma_addr), MTKSDG1_NFI_STRADDR);
-+ mtk_nfi_writew(host, INTR_AHB_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+
-+ init_completion(nfi);
-+
-+ /* start DMA */
-+ reg = mtk_nfi_readl(host, MTKSDG1_NFI_CON) | CON_BWR;
-+ mtk_nfi_writel(host, reg, MTKSDG1_NFI_CON);
-+
-+ ret = wait_for_completion_timeout(nfi, msecs_to_jiffies(MTK_TIMEOUT));
-+ if (!ret) {
-+ dev_err(dev, "program ahb done timeout\n");
-+ mtk_nfi_writew(host, 0, MTKSDG1_NFI_INTR_EN);
-+ ret = -ETIMEDOUT;
-+ goto timeout;
-+ }
-+
-+ ret = mtk_nfc_transfer_done(host, chip->ecc.steps);
-+ if (ret < 0)
-+ dev_err(dev, "hwecc write timeout\n");
-+timeout:
-+ dma_unmap_single(host->dev, dma_addr, dmasize, DMA_TO_DEVICE);
-+
-+ if (use_ecc) {
-+ mtk_ecc_encoder_idle(host);
-+ mtk_ecc_writew(host, ENC_DE, MTKSDG1_ECC_ENCCON);
-+ }
-+
-+ mtk_nfi_writel(host, 0, MTKSDG1_NFI_CON);
-+
-+ return ret;
-+}
-+
-+static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd,
-+ struct nand_chip *chip, const uint8_t *buf,
-+ int oob_on, int page)
-+{
-+ return mtk_nfc_write_page(mtd, chip, buf, oob_on, page, MTK_ECC_ON);
-+}
-+
-+static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+ const uint8_t *buf, int oob_on, int pg)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ uint8_t *src, *dst;
-+ size_t len;
-+ u32 i;
-+
-+ memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
-+
-+ /* MTK internal 4KB page data layout:
-+ * ----------------------------------
-+ * PAGE = 4KB, SECTOR = 1KB, OOB=128B
-+ * page = sector_oob1 + sector_oob2 + sector_oob3 + sector_oob4
-+ * sector_oob = data (1KB) + FDM (8B) + ECC parity (21B) + free (3B)
-+ *
-+ */
-+ len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
-+
-+ for (i = 0; i < chip->ecc.steps; i++) {
-+
-+ if (buf) {
-+ src = (uint8_t *) buf + i * SECTOR_SIZE;
-+ dst = host->buffer + i * len;
-+ memcpy(dst, src, SECTOR_SIZE);
-+ }
-+
-+ if (oob_on) {
-+ src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+ dst = host->buffer + i * len + SECTOR_SIZE;
-+ memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+ }
-+ }
-+
-+ return mtk_nfc_write_page(mtd, chip, host->buffer, MTK_OOB_OFF, pg,
-+ MTK_ECC_OFF);
-+}
-+
-+static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ struct completion *ecc = &host->ecc.complete;
-+ u32 reg, parity_bytes, i;
-+ dma_addr_t dma_addr;
-+ u32 *parity_region;
-+ int rc, ret = 0;
-+ size_t dmasize;
-+
-+ dmasize = SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE;
-+ dma_addr = dma_map_single(host->dev, data, dmasize, DMA_TO_DEVICE);
-+ if (dma_mapping_error(host->dev, dma_addr)) {
-+ dev_err(host->dev, "dma mapping error\n");
-+ return -EINVAL;
-+ }
-+
-+ /* enable the encoder in DMA mode to calculate the ECC bytes */
-+ reg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
-+ reg &= (~ECC_ENC_MODE_MASK);
-+ reg |= ECC_DMA_MODE;
-+ mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
-+
-+ mtk_ecc_writel(host, ENC_IRQEN, MTKSDG1_ECC_ENCIRQ_EN);
-+ mtk_ecc_writel(host, lower_32_bits(dma_addr), MTKSDG1_ECC_ENCDIADDR);
-+
-+ init_completion(ecc);
-+ mtk_ecc_writew(host, ENC_EN, MTKSDG1_ECC_ENCCON);
-+
-+ rc = wait_for_completion_timeout(ecc, msecs_to_jiffies(MTK_TIMEOUT));
-+ if (!rc) {
-+ dev_err(host->dev, "ecc encode done timeout\n");
-+ mtk_ecc_writel(host, 0, MTKSDG1_ECC_ENCIRQ_EN);
-+ ret = -ETIMEDOUT;
-+ goto timeout;
-+ }
-+
-+ mtk_ecc_encoder_idle(host);
-+
-+ /**
-+ * Program ECC bytes to OOB
-+ * per sector oob = FDM + ECC + SPARE
-+ */
-+
-+ parity_region = (u32 *) (data + SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE);
-+ parity_bytes = (chip->ecc.strength * MTK_ECC_PARITY_BITS + 7) >> 3;
-+
-+ /* write the parity bytes generated by the ECC back to the OOB region */
-+ for (i = 0; i < parity_bytes; i += sizeof(u32))
-+ *parity_region++ = mtk_ecc_readl(host, MTKSDG1_ECC_ENCPAR0 + i);
-+
-+timeout:
-+
-+ dma_unmap_single(host->dev, dma_addr, dmasize, DMA_TO_DEVICE);
-+
-+ mtk_ecc_writew(host, 0, MTKSDG1_ECC_ENCCON);
-+ reg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
-+ reg &= (~ECC_ENC_MODE_MASK);
-+ reg |= ECC_NFI_MODE;
-+ mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
-+
-+ return ret;
-+}
-+
-+static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd,
-+ struct nand_chip *chip, uint32_t offset, uint32_t data_len,
-+ const uint8_t *buf, int oob_on, int pg)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ uint8_t *src, *dst;
-+ u32 start, end;
-+ size_t len;
-+ int i, ret;
-+
-+ start = BYTES_TO_SECTORS(offset);
-+ end = BYTES_TO_SECTORS(offset + data_len + SECTOR_SIZE - 1);
-+
-+ len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
-+
-+ memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
-+ for (i = 0; i < chip->ecc.steps; i++) {
-+
-+ /* write data */
-+ src = (uint8_t *) buf + i * SECTOR_SIZE;
-+ dst = host->buffer + i * len;
-+ memcpy(dst, src, SECTOR_SIZE);
-+
-+ if (i < start)
-+ continue;
-+
-+ if (i >= end)
-+ continue;
-+
-+ /* write fdm */
-+ if (oob_on) {
-+ src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+ dst = host->buffer + i * len + SECTOR_SIZE;
-+ memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+ }
-+
-+ /* point to the start of data */
-+ src = host->buffer + i * len;
-+
-+ /* program the CRC back to the OOB */
-+ ret = mtk_nfc_sector_encode(chip, src);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ /* use the data in the private buffer (now with FDM and CRC) to perform
-+ * a raw write
-+ */
-+ src = host->buffer;
-+ return mtk_nfc_write_page(mtd, chip, src, MTK_OOB_OFF, pg, MTK_ECC_OFF);
-+}
-+
-+static int mtk_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+ int page)
-+{
-+ u8 *buf = chip->buffers->databuf;
-+ int ret;
-+
-+ memset(buf, 0xff, mtd->writesize);
-+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-+ ret = mtk_nfc_write_page_hwecc(mtd, chip, buf, MTK_OOB_ON, page);
-+ if (ret < 0)
-+ return -EIO;
-+
-+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-+ ret = chip->waitfunc(mtd, chip);
-+
-+ return ret & NAND_STATUS_FAIL ? -EIO : 0;
-+}
-+
-+static int mtk_nfc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+ int page)
-+{
-+ int ret;
-+
-+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-+ ret = mtk_nfc_write_page_raw(mtd, chip, NULL, MTK_OOB_ON, page);
-+ if (ret < 0)
-+ return -EIO;
-+
-+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-+ ret = chip->waitfunc(mtd, chip);
-+
-+ return ret & NAND_STATUS_FAIL ? -EIO : 0;
-+}
-+
-+static int mtk_nfc_ecc_check(struct mtd_info *mtd, struct nand_chip *chip,
-+ u32 sectors)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ u32 offset, i, err, max_bitflip;
-+
-+ max_bitflip = 0;
-+
-+ for (i = 0; i < sectors; i++) {
-+ offset = (i >> 2) << 2;
-+ err = mtk_ecc_readl(host, MTKSDG1_ECC_DECENUM0 + offset);
-+ err = err >> ((i % 4) * 8);
-+ err &= ERR_MASK;
-+ if (err == ERR_MASK) {
-+ /* uncorrectable errors */
-+ mtd->ecc_stats.failed++;
-+ continue;
-+ }
-+
-+ mtd->ecc_stats.corrected += err;
-+ max_bitflip = max_t(u32, max_bitflip, err);
-+ }
-+
-+ return max_bitflip;
-+}
-+
-+static void mtk_nfc_read_fdm(struct nand_chip *chip, u32 sectors)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ int i, j, reg;
-+ u8 *dst, *src;
-+
-+ for (i = 0; i < sectors; i++) {
-+ /* read FDM register into host memory */
-+ for (j = 0; j < ARRAY_SIZE(host->fdm_reg); j++) {
-+ reg = MTKSDG1_NFI_FDM0L + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+ reg += j * sizeof(host->fdm_reg[0]);
-+ host->fdm_reg[j] = mtk_nfi_readl(host, reg);
-+ }
-+
-+ /* copy FDM register from host to OOB */
-+ src = (u8 *)host->fdm_reg;
-+ dst = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+ memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+ }
-+}
-+
-+static int mtk_nfc_update_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+ u8 *buf, u32 sectors)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ int i, bitflips = 0;
-+
-+ /* if the page is empty, no bitflips and clear data and oob */
-+ if (mtk_nfi_readl(host, MTKSDG1_NFI_STA) & STA_EMP_PAGE) {
-+ memset(buf, 0xff, SECTORS_TO_BYTES(sectors));
-+
-+ /* empty page: update OOB with 0xFF */
-+ for (i = 0; i < sectors; i++) {
-+ memset(chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE,
-+ 0xff, MTKSDG1_NFI_FDM_REG_SIZE);
-+ }
-+ } else {
-+ /* update OOB with HW info */
-+ mtk_nfc_read_fdm(chip, sectors);
-+
-+ /* return the bitflips */
-+ bitflips = mtk_nfc_ecc_check(mtd, chip, sectors);
-+ }
-+
-+ return bitflips;
-+}
-+
-+static int mtk_nfc_block_markbad(struct mtd_info *mtd, loff_t ofs)
-+{
-+ struct nand_chip *chip = mtd_to_nand(mtd);
-+ u8 *buf = chip->buffers->databuf;
-+ int rc, i, pg;
-+
-+ /* block_markbad writes 0x00 at data and OOB */
-+ memset(buf, 0x00, mtd->writesize + mtd->oobsize);
-+
-+ /* Write to first/last page(s) if necessary */
-+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
-+ ofs += mtd->erasesize - mtd->writesize;
-+
-+ i = 0;
-+ do {
-+ pg = (int)(ofs >> chip->page_shift);
-+
-+ /**
-+ * write 0x00 to DATA & OOB in flash
-+ * No need to reorganize the page since it is all 0x00
-+ */
-+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, pg);
-+ rc = mtk_nfc_write_page(mtd, chip, buf, MTK_OOB_OFF, pg,
-+ MTK_ECC_OFF);
-+ if (rc < 0)
-+ return rc;
-+
-+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-+ rc = chip->waitfunc(mtd, chip);
-+ rc = rc & NAND_STATUS_FAIL ? -EIO : 0;
-+ if (rc < 0)
-+ return rc;
-+
-+ ofs += mtd->writesize;
-+ i++;
-+
-+ } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
-+
-+ return 0;
-+}
-+
-+static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
-+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
-+ int page, int raw)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+ u32 reg, column, spare, sectors, start, end;
-+ struct completion *nfi, *ecc;
-+ const bool use_ecc = !raw;
-+ int bitflips = -EIO;
-+ dma_addr_t dma_addr;
-+ size_t len;
-+ u8 *buf;
-+ int rc;
-+
-+ nfi = &host->nfi.complete;
-+ ecc = &host->ecc.complete;
-+
-+ start = BYTES_TO_SECTORS(data_offs);
-+ end = BYTES_TO_SECTORS(data_offs + readlen + SECTOR_SIZE - 1);
-+ sectors = end - start;
-+
-+ spare = mtd->oobsize / chip->ecc.steps;
-+ column = start * (SECTOR_SIZE + spare);
-+
-+ len = SECTORS_TO_BYTES(sectors) + (raw ? sectors * spare : 0);
-+ buf = bufpoi + SECTORS_TO_BYTES(start);
-+
-+ /* map the device memory */
-+ dma_addr = dma_map_single(host->dev, buf, len, DMA_FROM_DEVICE);
-+ if (dma_mapping_error(host->dev, dma_addr)) {
-+ dev_err(host->dev, "dma mapping error\n");
-+ return -EINVAL;
-+ }
-+
-+ /* configure the transfer */
-+ reg = mtk_nfi_readw(host, MTKSDG1_NFI_CNFG);
-+ reg |= CNFG_DMA_BURST_EN | CNFG_AHB;
-+ if (use_ecc) {
-+ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
-+ mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+
-+ /* enable encoder */
-+ mtk_ecc_decoder_idle(host);
-+ mtk_ecc_writel(host, DEC_EN, MTKSDG1_ECC_DECCON);
-+ } else
-+ mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+
-+ mtk_nfi_writel(host, sectors << CON_SEC_SHIFT, MTKSDG1_NFI_CON);
-+ mtk_nfi_writew(host, INTR_BUSY_RT_EN, MTKSDG1_NFI_INTR_EN);
-+
-+ init_completion(nfi);
-+
-+ mtk_nfc_set_address(host, column, page, 2, host->row_nob);
-+ mtk_nfc_set_command(host, NAND_CMD_READSTART);
-+ rc = wait_for_completion_timeout(nfi, timeout);
-+ if (!rc) {
-+ dev_err(host->dev, "read busy return timeout\n");
-+ goto error;
-+ }
-+
-+ mtk_nfi_writew(host, INTR_AHB_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+ mtk_nfi_writel(host, lower_32_bits(dma_addr), MTKSDG1_NFI_STRADDR);
-+
-+ if (use_ecc) {
-+ /* program ECC with sector count */
-+ host->ecc.dec_sec = sectors;
-+ init_completion(ecc);
-+ mtk_ecc_writew(host, DEC_IRQEN, MTKSDG1_ECC_DECIRQ_EN);
-+ }
-+
-+ init_completion(nfi);
-+
-+ /* start DMA */
-+ reg = mtk_nfi_readl(host, MTKSDG1_NFI_CON) | CON_BRD;
-+ mtk_nfi_writel(host, reg, MTKSDG1_NFI_CON);
-+
-+ rc = wait_for_completion_timeout(nfi, timeout);
-+ if (!rc)
-+ dev_warn(host->dev, "read ahb/dma done timeout\n");
-+
-+ /* DMA interrupt didn't trigger, check page done just in case */
-+ rc = mtk_nfc_subpage_done(host, sectors);
-+ if (rc < 0) {
-+ dev_err(host->dev, "subpage done timeout\n");
-+ goto error;
-+ }
-+
-+ /* raw transfer successful */
-+ bitflips = 0;
-+
-+ if (use_ecc) {
-+ rc = wait_for_completion_timeout(ecc, timeout);
-+ if (!rc) {
-+ dev_err(host->dev, "ecc decode timeout\n");
-+ host->ecc.dec_sec = 0;
-+ bitflips = -ETIMEDOUT;
-+ goto error;
-+ }
-+ bitflips = mtk_nfc_update_oob(mtd, chip, buf, sectors);
-+ }
-+
-+error:
-+ dma_unmap_single(host->dev, dma_addr, len, DMA_FROM_DEVICE);
-+
-+ if (use_ecc) {
-+ /* make sure the ECC dec irq is disabled */
-+ mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECIRQ_EN);
-+ mtk_ecc_decoder_idle(host);
-+
-+ /* disable ECC dec */
-+ mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECCON);
-+ }
-+
-+ mtk_nfi_writel(host, 0, MTKSDG1_NFI_CON);
-+
-+ return bitflips;
-+}
-+
-+static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd,
-+ struct nand_chip *chip, uint32_t data_offs,
-+ uint32_t readlen, uint8_t *bufpoi, int page)
-+{
-+ return mtk_nfc_read_subpage(mtd, chip, data_offs, readlen,
-+ bufpoi, page, MTK_ECC_ON);
-+}
-+
-+static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
-+ uint8_t *buf, int oob_on, int page)
-+{
-+ return mtk_nfc_read_subpage_hwecc(mtd, chip, 0, mtd->writesize,
-+ buf, page);
-+}
-+
-+static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+ uint8_t *buf, int oob_on, int page)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ uint8_t *src, *dst;
-+ int i, ret;
-+ size_t len;
-+
-+ dst = host->buffer;
-+ memset(dst, 0xff, mtd->writesize + mtd->oobsize);
-+ ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, dst, page, 1);
-+ if (ret < 0)
-+ return ret;
-+
-+ len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
-+
-+ /* copy to the output buffer */
-+ for (i = 0; i < chip->ecc.steps; i++) {
-+
-+ /* copy sector data */
-+ if (buf) {
-+ src = host->buffer + i * len;
-+ dst = buf + i * SECTOR_SIZE;
-+ memcpy(dst, src, SECTOR_SIZE);
-+ }
-+
-+ /* copy FDM data to OOB */
-+ if (oob_on) {
-+ src = host->buffer + i * len + SECTOR_SIZE;
-+ dst = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+ memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+ }
-+ }
-+
-+ return ret;
-+}
-+
-+static void mtk_nfc_switch_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+ uint8_t *buf)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ size_t spare;
-+ u32 sectors;
-+ u8 *bufpoi;
-+ int len;
-+
-+ spare = mtd->oobsize / chip->ecc.steps;
-+ sectors = mtd->writesize / (SECTOR_SIZE + spare);
-+
-+ /**
-+ * MTK: DATA+oob1, DATA+oob2, DATA+oob3 ...
-+ * LNX: DATA+OOB
-+ */
-+ /* point to the last oob_i from the NAND device*/
-+ bufpoi = buf + mtd->writesize - (sectors * spare);
-+ len = sizeof(host->fdm_reg);
-+
-+ /* copy NAND oob to private area */
-+ memcpy(host->fdm_reg, bufpoi, len);
-+
-+ /* copy oob_poi to NAND */
-+ memcpy(bufpoi, chip->oob_poi, len);
-+
-+ /* copy NAND oob to oob_poi */
-+ memcpy(chip->oob_poi, host->fdm_reg, sizeof(host->fdm_reg));
-+ memset(host->fdm_reg, 0x00, len);
-+}
-+
-+static int mtk_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+ int page)
-+{
-+ struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+ u8 *buf = chip->buffers->databuf;
-+ struct mtd_ecc_stats stats;
-+ int ret;
-+
-+ stats = mtd->ecc_stats;
-+
-+ memset(buf, 0xff, mtd->writesize);
-+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-+
-+ ret = mtk_nfc_read_page_hwecc(mtd, chip, buf, 1, page);
-+
-+ if (host->switch_oob)
-+ mtk_nfc_switch_oob(mtd, chip, buf);
-+
-+ if (ret < mtd->bitflip_threshold)
-+ mtd->ecc_stats.corrected = stats.corrected;
-+
-+ return ret;
-+}
-+
-+static int mtk_nfc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+ int page)
-+{
-+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-+
-+ return mtk_nfc_read_page_raw(mtd, chip, NULL, MTK_OOB_ON, page);
-+}
-+
-+static inline void mtk_nfc_hw_init(struct mtk_nfc_host *host)
-+{
-+ mtk_nfi_writel(host, 0x10804211, MTKSDG1_NFI_ACCCON);
-+ mtk_nfi_writew(host, 0xf1, MTKSDG1_NFI_CNRNB);
-+ mtk_nfc_hw_reset(host);
-+
-+ /* clear interrupt */
-+ mtk_nfi_readl(host, MTKSDG1_NFI_INTR_STA);
-+ mtk_nfi_writel(host, 0, MTKSDG1_NFI_INTR_EN);
-+
-+ /* ECC encoder init */
-+ mtk_ecc_encoder_idle(host);
-+ mtk_ecc_writew(host, ENC_DE, MTKSDG1_ECC_ENCCON);
-+
-+ /* ECC decoder init */
-+ mtk_ecc_decoder_idle(host);
-+ mtk_ecc_writel(host, DEC_DE, MTKSDG1_ECC_DECCON);
-+}
-+
-+static irqreturn_t mtk_nfi_irq(int irq, void *devid)
-+{
-+ struct mtk_nfc_host *host = devid;
-+ u16 sta, ien;
-+
-+ sta = mtk_nfi_readw(host, MTKSDG1_NFI_INTR_STA);
-+ ien = mtk_nfi_readw(host, MTKSDG1_NFI_INTR_EN);
-+
-+ if (!(sta & ien))
-+ return IRQ_NONE;
-+
-+ mtk_nfi_writew(host, ~sta & ien, MTKSDG1_NFI_INTR_EN);
-+ complete(&host->nfi.complete);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static irqreturn_t mtk_ecc_irq(int irq, void *devid)
-+{
-+ struct mtk_nfc_host *host = devid;
-+ u32 reg_val, mask;
-+
-+ reg_val = mtk_ecc_readw(host, MTKSDG1_ECC_DECIRQ_STA);
-+ if (reg_val & DEC_IRQEN) {
-+ if (host->ecc.dec_sec) {
-+ mask = 1 << (host->ecc.dec_sec - 1);
-+ reg_val = mtk_ecc_readw(host, MTKSDG1_ECC_DECDONE);
-+ if (mask & reg_val) {
-+ host->ecc.dec_sec = 0;
-+ complete(&host->ecc.complete);
-+ mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECIRQ_EN);
-+ }
-+ } else
-+ dev_warn(host->dev, "spurious DEC_IRQ\n");
-+
-+ return IRQ_HANDLED;
-+ }
-+
-+ reg_val = mtk_ecc_readl(host, MTKSDG1_ECC_ENCIRQ_STA);
-+ if (reg_val & ENC_IRQEN) {
-+ complete(&host->ecc.complete);
-+ mtk_ecc_writel(host, 0, MTKSDG1_ECC_ENCIRQ_EN);
-+
-+ return IRQ_HANDLED;
-+ }
-+
-+ return IRQ_NONE;
-+}
-+
-+static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk)
-+{
-+ int ret;
-+
-+ ret = clk_prepare_enable(clk->nfi_clk);
-+ if (ret) {
-+ dev_err(dev, "failed to enable nfi clk\n");
-+ return ret;
-+ }
-+
-+ ret = clk_prepare_enable(clk->nfiecc_clk);
-+ if (ret) {
-+ dev_err(dev, "failed to enable nfiecc clk\n");
-+ goto out_nfiecc_clk_disable;
-+ }
-+
-+ ret = clk_prepare_enable(clk->pad_clk);
-+ if (ret) {
-+ dev_err(dev, "failed to enable pad clk\n");
-+ goto out_pad_clk_disable;
-+ }
-+
-+ return 0;
-+
-+out_pad_clk_disable:
-+ clk_disable_unprepare(clk->nfiecc_clk);
-+
-+out_nfiecc_clk_disable:
-+ clk_disable_unprepare(clk->nfi_clk);
-+
-+ return ret;
-+}
-+
-+static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk)
-+{
-+ clk_disable_unprepare(clk->nfi_clk);
-+ clk_disable_unprepare(clk->nfiecc_clk);
-+ clk_disable_unprepare(clk->pad_clk);
-+}
-+
-+static int mtk_nfc_probe(struct platform_device *pdev)
-+{
-+ struct device *dev = &pdev->dev;
-+ struct device_node *np = dev->of_node;
-+ struct mtk_nfc_host *host;
-+ struct nand_chip *chip;
-+ struct mtd_info *mtd;
-+ struct resource *res;
-+ int ret, irq;
-+ size_t len;
-+
-+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
-+ if (!host)
-+ return -ENOMEM;
-+
-+ chip = &host->chip;
-+ mtd = nand_to_mtd(chip);
-+ host->dev = dev;
-+
-+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+ host->nfi.base = devm_ioremap_resource(dev, res);
-+ if (IS_ERR(host->nfi.base)) {
-+ ret = PTR_ERR(host->nfi.base);
-+ dev_err(dev, "no nfi base\n");
-+ return ret;
-+ }
-+
-+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-+ host->ecc.base = devm_ioremap_resource(dev, res);
-+ if (IS_ERR(host->ecc.base)) {
-+ ret = PTR_ERR(host->ecc.base);
-+ dev_err(dev, "no ecc base\n");
-+ return ret;
-+ }
-+
-+ host->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
-+ if (IS_ERR(host->clk.nfi_clk)) {
-+ dev_err(dev, "no clk\n");
-+ ret = PTR_ERR(host->clk.nfi_clk);
-+ return ret;
-+ }
-+
-+ host->clk.nfiecc_clk = devm_clk_get(dev, "nfiecc_clk");
-+ if (IS_ERR(host->clk.nfiecc_clk)) {
-+ dev_err(dev, "no ecc clk\n");
-+ ret = PTR_ERR(host->clk.nfiecc_clk);
-+ return ret;
-+ }
-+
-+ host->clk.pad_clk = devm_clk_get(dev, "pad_clk");
-+ if (IS_ERR(host->clk.pad_clk)) {
-+ dev_err(dev, "no pad clk\n");
-+ ret = PTR_ERR(host->clk.pad_clk);
-+ return ret;
-+ }
-+
-+ ret = mtk_nfc_enable_clk(dev, &host->clk);
-+ if (ret)
-+ return ret;
-+
-+ irq = platform_get_irq(pdev, 0);
-+ if (irq < 0) {
-+ dev_err(dev, "no nfi irq resource\n");
-+ ret = -EINVAL;
-+ goto clk_disable;
-+ }
-+
-+ ret = devm_request_irq(dev, irq, mtk_nfi_irq, 0x0, MTK_IRQ_NFI, host);
-+ if (ret) {
-+ dev_err(dev, "failed to request nfi irq\n");
-+ goto clk_disable;
-+ }
-+
-+ irq = platform_get_irq(pdev, 1);
-+ if (irq < 0) {
-+ dev_err(dev, "no ecc irq resource\n");
-+ ret = -EINVAL;
-+ goto clk_disable;
-+ }
-+
-+ ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, MTK_IRQ_ECC, host);
-+ if (ret) {
-+ dev_err(dev, "failed to request ecc irq\n");
-+ goto clk_disable;
-+ }
-+
-+ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
-+ if (ret) {
-+ dev_err(dev, "failed to set dma mask\n");
-+ goto clk_disable;
-+ }
-+
-+ platform_set_drvdata(pdev, host);
-+
-+ mtd_set_of_node(mtd, np);
-+ mtd->owner = THIS_MODULE;
-+ mtd->dev.parent = dev;
-+ mtd->name = MTK_NAME;
-+
-+ nand_set_controller_data(chip, host);
-+ chip->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ;
-+ chip->block_markbad = mtk_nfc_block_markbad;
-+ chip->select_chip = mtk_nfc_select_chip;
-+ chip->read_byte = mtk_nfc_read_byte;
-+ chip->cmdfunc = mtk_nfc_cmdfunc;
-+ chip->ecc.mode = NAND_ECC_HW;
-+ chip->ecc.write_subpage = mtk_nfc_write_subpage_hwecc;
-+ chip->ecc.write_page_raw = mtk_nfc_write_page_raw;
-+ chip->ecc.write_page = mtk_nfc_write_page_hwecc;
-+ chip->ecc.write_oob_raw = mtk_nfc_write_oob_raw;
-+ chip->ecc.write_oob = mtk_nfc_write_oob;
-+ chip->ecc.read_subpage = mtk_nfc_read_subpage_hwecc;
-+ chip->ecc.read_page_raw = mtk_nfc_read_page_raw;
-+ chip->ecc.read_oob_raw = mtk_nfc_read_oob_raw;
-+ chip->ecc.read_page = mtk_nfc_read_page_hwecc;
-+ chip->ecc.read_oob = mtk_nfc_read_oob;
-+
-+ mtk_nfc_hw_init(host);
-+
-+ ret = nand_scan_ident(mtd, MTK_NAND_MAX_CHIP, NULL);
-+ if (ret) {
-+ ret = -ENODEV;
-+ goto clk_disable;
-+ }
-+
-+ ret = mtk_nfc_hw_runtime_config(mtd);
-+ if (ret < 0) {
-+ dev_err(dev, "nand device not supported\n");
-+ goto clk_disable;
-+ }
-+
-+ len = mtd->writesize + mtd->oobsize;
-+ host->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
-+ if (!host->buffer) {
-+ ret = -ENOMEM;
-+ goto clk_disable;
-+ }
-+
-+ /* required to create bbt table if not present */
-+ host->switch_oob = true;
-+ ret = nand_scan_tail(mtd);
-+ if (ret) {
-+ ret = -ENODEV;
-+ goto clk_disable;
-+ }
-+ host->switch_oob = false;
-+
-+ ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
-+ if (ret) {
-+ dev_err(dev, "mtd parse partition error\n");
-+ goto nand_free;
-+ }
-+
-+ return 0;
-+
-+nand_free:
-+ nand_release(mtd);
-+
-+clk_disable:
-+ mtk_nfc_disable_clk(&host->clk);
-+
-+ return ret;
-+}
-+
-+static int mtk_nfc_remove(struct platform_device *pdev)
-+{
-+ struct mtk_nfc_host *host = platform_get_drvdata(pdev);
-+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
-+
-+ nand_release(mtd);
-+ mtk_nfc_disable_clk(&host->clk);
-+
-+ return 0;
-+}
-+
-+#ifdef CONFIG_PM_SLEEP
-+static int mtk_nfc_suspend(struct device *dev)
-+{
-+ struct mtk_nfc_host *host = dev_get_drvdata(dev);
-+ struct mtk_nfc_saved_reg *reg = &host->saved_reg;
-+
-+ reg->nfi.emp_thresh = mtk_nfi_readl(host, MTKSDG1_NFI_EMPTY_THRESH);
-+ reg->ecc.enccnfg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
-+ reg->ecc.deccnfg = mtk_ecc_readl(host, MTKSDG1_ECC_DECCNFG);
-+ reg->nfi.pagefmt = mtk_nfi_readw(host, MTKSDG1_NFI_PAGEFMT);
-+ reg->nfi.acccon = mtk_nfi_readl(host, MTKSDG1_NFI_ACCCON);
-+ reg->nfi.cnrnb = mtk_nfi_readw(host, MTKSDG1_NFI_CNRNB);
-+ reg->nfi.csel = mtk_nfi_readw(host, MTKSDG1_NFI_CSEL);
-+
-+ mtk_nfc_disable_clk(&host->clk);
-+
-+ return 0;
-+}
-+
-+static int mtk_nfc_resume(struct device *dev)
-+{
-+ struct mtk_nfc_host *host = dev_get_drvdata(dev);
-+ struct mtk_nfc_saved_reg *reg = &host->saved_reg;
-+ struct nand_chip *chip = &host->chip;
-+ struct mtd_info *mtd = nand_to_mtd(chip);
-+ int ret;
-+ u32 i;
-+
-+ udelay(200);
-+
-+ ret = mtk_nfc_enable_clk(dev, &host->clk);
-+ if (ret)
-+ return ret;
-+
-+ for (i = 0; i < chip->numchips; i++) {
-+ chip->select_chip(mtd, i);
-+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-+ }
-+
-+ mtk_nfi_writel(host, reg->nfi.emp_thresh, MTKSDG1_NFI_EMPTY_THRESH);
-+ mtk_nfi_writew(host, reg->nfi.pagefmt, MTKSDG1_NFI_PAGEFMT);
-+ mtk_ecc_writel(host, reg->ecc.enccnfg, MTKSDG1_ECC_ENCCNFG);
-+ mtk_ecc_writel(host, reg->ecc.deccnfg, MTKSDG1_ECC_DECCNFG);
-+ mtk_nfi_writel(host, reg->nfi.acccon, MTKSDG1_NFI_ACCCON);
-+ mtk_nfi_writew(host, reg->nfi.cnrnb, MTKSDG1_NFI_CNRNB);
-+ mtk_nfi_writew(host, reg->nfi.csel, MTKSDG1_NFI_CSEL);
-+
-+ return 0;
-+}
-+
-+static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume);
-+#endif
-+
-+static const struct of_device_id mtk_nfc_id_table[] = {
-+ { .compatible = "mediatek,mt2701-nfc" },
-+ {}
-+};
-+MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
-+
-+static struct platform_driver mtk_nfc_driver = {
-+ .probe = mtk_nfc_probe,
-+ .remove = mtk_nfc_remove,
-+ .driver = {
-+ .name = MTK_NAME,
-+ .of_match_table = mtk_nfc_id_table,
-+#ifdef CONFIG_PM_SLEEP
-+ .pm = &mtk_nfc_pm_ops,
-+#endif
-+ },
-+};
-+
-+module_platform_driver(mtk_nfc_driver);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
-+MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
-+
---- /dev/null
-+++ b/drivers/mtd/nand/mtksdg1_nand_ecc.h
-@@ -0,0 +1,75 @@
-+/*
-+ * MTK smart device ECC engine register.
-+ * Copyright (C) 2015-2016 MediaTek Inc.
-+ * Author: Xiaolei.Li <xiaolei.li@mediatek.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * 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.
-+ */
-+
-+#ifndef MTKSDG1_NAND_ECC_H
-+#define MTKSDG1_NAND_ECC_H
-+
-+/* ECC engine register definition */
-+#define MTKSDG1_ECC_ENCCON (0x00)
-+#define ENC_EN (1)
-+#define ENC_DE (0)
-+
-+#define MTKSDG1_ECC_ENCCNFG (0x04)
-+#define ECC_CNFG_4BIT (0)
-+#define ECC_CNFG_12BIT (4)
-+#define ECC_NFI_MODE BIT(5)
-+#define ECC_DMA_MODE (0)
-+#define ECC_ENC_MODE_MASK (0x3 << 5)
-+#define ECC_MS_SHIFT (16)
-+
-+#define MTKSDG1_ECC_ENCDIADDR (0x08)
-+
-+#define MTKSDG1_ECC_ENCIDLE (0x0C)
-+#define ENC_IDLE BIT(0)
-+
-+#define MTKSDG1_ECC_ENCPAR0 (0x10)
-+#define MTKSDG1_ECC_ENCSTA (0x7C)
-+
-+#define MTKSDG1_ECC_ENCIRQ_EN (0x80)
-+#define ENC_IRQEN BIT(0)
-+
-+#define MTKSDG1_ECC_ENCIRQ_STA (0x84)
-+
-+#define MTKSDG1_ECC_DECCON (0x100)
-+#define DEC_EN (1)
-+#define DEC_DE (0)
-+
-+#define MTKSDG1_ECC_DECCNFG (0x104)
-+#define DEC_EMPTY_EN BIT(31)
-+#define DEC_CNFG_FER (0x1 << 12)
-+#define DEC_CNFG_EL (0x2 << 12)
-+#define DEC_CNFG_CORRECT (0x3 << 12)
-+
-+#define MTKSDG1_ECC_DECIDLE (0x10C)
-+#define DEC_IDLE BIT(0)
-+
-+#define MTKSDG1_ECC_DECFER (0x110)
-+
-+#define MTKSDG1_ECC_DECENUM0 (0x114)
-+#define ERR_MASK (0x3f)
-+
-+#define MTKSDG1_ECC_DECDONE (0x124)
-+
-+#define MTKSDG1_ECC_DECEL0 (0x128)
-+
-+#define MTKSDG1_ECC_DECIRQ_EN (0x200)
-+#define DEC_IRQEN BIT(0)
-+
-+#define MTKSDG1_ECC_DECIRQ_STA (0x204)
-+
-+#define MTKSDG1_ECC_DECFSM (0x208)
-+#define DECFSM_MASK (0x7f0f0f0f)
-+#define DECFSM_IDLE (0x01010101)
-+#endif
---- /dev/null
-+++ b/drivers/mtd/nand/mtksdg1_nand_nfi.h
-@@ -0,0 +1,119 @@
-+/*
-+ * MTK smart device NAND Flash controller register.
-+ * Copyright (C) 2015-2016 MediaTek Inc.
-+ * Author: Xiaolei.Li <xiaolei.li@mediatek.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * 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.
-+ */
-+
-+#ifndef MTKSDG1_NAND_NFI_H
-+#define MTKSDG1_NAND_NFI_H
-+
-+/* NAND controller register definition */
-+#define MTKSDG1_NFI_CNFG (0x00)
-+#define CNFG_AHB BIT(0)
-+#define CNFG_READ_EN BIT(1)
-+#define CNFG_DMA_BURST_EN BIT(2)
-+#define CNFG_BYTE_RW BIT(6)
-+#define CNFG_HW_ECC_EN BIT(8)
-+#define CNFG_AUTO_FMT_EN BIT(9)
-+#define CNFG_OP_IDLE (0 << 12)
-+#define CNFG_OP_READ (1 << 12)
-+#define CNFG_OP_SRD (2 << 12)
-+#define CNFG_OP_PRGM (3 << 12)
-+#define CNFG_OP_ERASE (4 << 12)
-+#define CNFG_OP_RESET (5 << 12)
-+#define CNFG_OP_CUST (6 << 12)
-+
-+#define MTKSDG1_NFI_PAGEFMT (0x04)
-+#define PAGEFMT_FDM_ECC_SHIFT (12)
-+#define PAGEFMT_FDM_SHIFT (8)
-+#define PAGEFMT_SPARE_16 (0)
-+#define PAGEFMT_SPARE_32 (4)
-+#define PAGEFMT_SPARE_SHIFT (4)
-+#define PAGEFMT_SEC_SEL_512 BIT(2)
-+#define PAGEFMT_512_2K (0)
-+#define PAGEFMT_2K_4K (1)
-+#define PAGEFMT_4K_8K (2)
-+
-+/* NFI control */
-+#define MTKSDG1_NFI_CON (0x08)
-+#define CON_FIFO_FLUSH BIT(0)
-+#define CON_NFI_RST BIT(1)
-+#define CON_SRD BIT(4) /* single read */
-+#define CON_BRD BIT(8) /* burst read */
-+#define CON_BWR BIT(9) /* burst write */
-+#define CON_SEC_SHIFT (12)
-+
-+/* Timming control register */
-+#define MTKSDG1_NFI_ACCCON (0x0C)
-+
-+#define MTKSDG1_NFI_INTR_EN (0x10)
-+#define INTR_RD_DONE_EN BIT(0)
-+#define INTR_WR_DONE_EN BIT(1)
-+#define INTR_RST_DONE_EN BIT(2)
-+#define INTR_ERS_DONE_EN BIT(3)
-+#define INTR_BUSY_RT_EN BIT(4)
-+#define INTR_AHB_DONE_EN BIT(6)
-+
-+#define MTKSDG1_NFI_INTR_STA (0x14)
-+
-+#define MTKSDG1_NFI_CMD (0x20)
-+
-+#define MTKSDG1_NFI_ADDRNOB (0x30)
-+#define ADDR_ROW_NOB_SHIFT (4)
-+
-+#define MTKSDG1_NFI_COLADDR (0x34)
-+#define MTKSDG1_NFI_ROWADDR (0x38)
-+#define MTKSDG1_NFI_STRDATA (0x40)
-+#define MTKSDG1_NFI_CNRNB (0x44)
-+#define MTKSDG1_NFI_DATAW (0x50)
-+#define MTKSDG1_NFI_DATAR (0x54)
-+#define MTKSDG1_NFI_PIO_DIRDY (0x58)
-+#define PIO_DI_RDY (0x01)
-+
-+/* NFI state*/
-+#define MTKSDG1_NFI_STA (0x60)
-+#define STA_CMD BIT(0)
-+#define STA_ADDR BIT(1)
-+#define STA_DATAR BIT(2)
-+#define STA_DATAW BIT(3)
-+#define STA_EMP_PAGE BIT(12)
-+
-+#define MTKSDG1_NFI_FIFOSTA (0x64)
-+
-+#define MTKSDG1_NFI_ADDRCNTR (0x70)
-+#define CNTR_MASK GENMASK(16, 12)
-+
-+#define MTKSDG1_NFI_STRADDR (0x80)
-+#define MTKSDG1_NFI_BYTELEN (0x84)
-+#define MTKSDG1_NFI_CSEL (0x90)
-+#define MTKSDG1_NFI_IOCON (0x94)
-+
-+/* FDM data for sector: FDM0[L,H] - FDMF[L,H] */
-+#define MTKSDG1_NFI_FDM_MAX_SEC (0x10)
-+#define MTKSDG1_NFI_FDM_REG_SIZE (8)
-+#define MTKSDG1_NFI_FDM0L (0xA0)
-+#define MTKSDG1_NFI_FDM0M (0xA4)
-+
-+
-+#define MTKSDG1_NFI_FIFODATA0 (0x190)
-+#define MTKSDG1_NFI_DEBUG_CON1 (0x220)
-+#define MTKSDG1_NFI_MASTER_STA (0x224)
-+#define MASTER_STA_MASK (0x0FFF)
-+
-+#define MTKSDG1_NFI_RANDOM_CNFG (0x238)
-+#define MTKSDG1_NFI_EMPTY_THRESH (0x23C)
-+#define MTKSDG1_NFI_NAND_TYPE (0x240)
-+#define MTKSDG1_NFI_ACCCON1 (0x244)
-+#define MTKSDG1_NFI_DELAY_CTRL (0x248)
-+
-+#endif
-+
--- /dev/null
+From 6c12340c0c307d18b8d6120f64a8275b6d4d3e67 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Tue, 15 Mar 2016 10:19:04 +0300
+Subject: [PATCH 058/102] net: mediatek: unlock on error in mtk_tx_map()
+
+There was a missing unlock on the error path.
+
+Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 9759fe5..c2c2e206 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -661,6 +661,8 @@ err_dma:
+ itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
+ } while (itxd != txd);
+
++ spin_unlock_irqrestore(ð->page_lock, flags);
++
+ return -ENOMEM;
+ }
+
+--
+1.7.10.4
+
+++ /dev/null
-From 96bddff914c0cee1b16d809220e84b470b433122 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Thu, 31 Mar 2016 02:28:08 +0200
-Subject: [PATCH 59/91] mtd: nand: backport fixes
-
----
- drivers/mtd/nand/mtksdg1_nand.c | 9 ++++++++-
- 1 file changed, 8 insertions(+), 1 deletion(-)
-
---- a/drivers/mtd/nand/mtksdg1_nand.c
-+++ b/drivers/mtd/nand/mtksdg1_nand.c
-@@ -107,6 +107,9 @@ static struct nand_ecclayout nand_4k_128
- .oobfree = { {0, 32} },
- };
-
-+static const char * const part_probes[] = {
-+ "cmdlinepart", "RedBoot", "ofpart", NULL };
-+
- /* NFI register access */
- static inline void mtk_nfi_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
- {
-@@ -1298,6 +1301,7 @@ static int mtk_nfc_probe(struct platform
-
- chip = &host->chip;
- mtd = nand_to_mtd(chip);
-+ mtd->priv = chip;
- host->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-@@ -1428,7 +1432,10 @@ static int mtk_nfc_probe(struct platform
- }
- host->switch_oob = false;
-
-- ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
-+ ret = mtd_device_parse_register(mtd, part_probes,
-+ &(struct mtd_part_parser_data) {
-+ .of_node = pdev->dev.of_node,
-+ }, NULL, 0);
- if (ret) {
- dev_err(dev, "mtd parse partition error\n");
- goto nand_free;
--- /dev/null
+From a572747434b6153e75812c5466c0557e5ed69284 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 14 Mar 2016 15:07:10 +0100
+Subject: [PATCH 059/102] net: mediatek: use dma_addr_t correctly
+
+dma_alloc_coherent() expects a dma_addr_t pointer as its argument,
+not an 'unsigned int', and gcc correctly warns about broken
+code in the mtk_init_fq_dma function:
+
+drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_init_fq_dma':
+drivers/net/ethernet/mediatek/mtk_eth_soc.c:463:13: error: passing argument 3 of 'dma_alloc_coherent' from incompatible pointer type [-Werror=incompatible-pointer-types]
+
+This changes the type of the local variable to dma_addr_t.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index c2c2e206..a005bc4 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -453,7 +453,7 @@ static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
+ /* the qdma core needs scratch memory to be setup */
+ static int mtk_init_fq_dma(struct mtk_eth *eth)
+ {
+- unsigned int phy_ring_head, phy_ring_tail;
++ dma_addr_t phy_ring_head, phy_ring_tail;
+ int cnt = MTK_DMA_SIZE;
+ dma_addr_t dma_addr;
+ int i;
+--
+1.7.10.4
+
+++ /dev/null
-From cbcbd319d905cdcf4a71003b5634137fee03855b Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Thu, 7 Apr 2016 07:18:35 +0200
-Subject: [PATCH 60/91] clk: dont disable unused clocks
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/clk/clk.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/clk/clk.c
-+++ b/drivers/clk/clk.c
-@@ -233,7 +233,7 @@ unlock_out:
- clk_enable_unlock(flags);
- }
-
--static bool clk_ignore_unused;
-+static bool clk_ignore_unused = true;
- static int __init clk_ignore_unused_setup(char *__unused)
- {
- clk_ignore_unused = true;
--- /dev/null
+From 8473af12d5aa34613070447d6fd8f785f31301de Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 14 Mar 2016 15:07:11 +0100
+Subject: [PATCH 060/102] net: mediatek: remove incorrect dma_mask assignment
+
+Device drivers should not mess with the DMA mask directly,
+but instead call dma_set_mask() etc if needed.
+
+In case of the mtk_eth_soc driver, the mask already gets set
+correctly when the device is created, and setting it again
+is against the documented API.
+
+This removes the incorrect setting.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index a005bc4..fcd4ed7 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1678,9 +1678,6 @@ static int mtk_probe(struct platform_device *pdev)
+ struct mtk_eth *eth;
+ int err;
+
+- pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+- pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+-
+ device_reset(&pdev->dev);
+
+ match = of_match_device(of_mtk_match, &pdev->dev);
+--
+1.7.10.4
+
+++ /dev/null
-From 6610fdbea393a4a8ed956b2aaf7012bea3a5069e Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Thu, 31 Mar 2016 06:46:51 +0200
-Subject: [PATCH 61/91] clk: mediatek: enable critical clocks
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/clk/mediatek/clk-mt2701.c | 22 ++++++++++++++++++++--
- 1 file changed, 20 insertions(+), 2 deletions(-)
-
---- a/drivers/clk/mediatek/clk-mt2701.c
-+++ b/drivers/clk/mediatek/clk-mt2701.c
-@@ -573,6 +573,20 @@ static const struct mtk_gate top_clks[]
- GATE_TOP_AUD(CLK_TOP_AUD_I2S6_MCLK, "aud_i2s6_mclk", "aud_k6_src_div", 28),
- };
-
-+static struct clk_onecell_data *mt7623_top_clk_data __initdata;
-+static struct clk_onecell_data *mt7623_pll_clk_data __initdata;
-+
-+static void __init mtk_clk_enable_critical(void)
-+{
-+ if (!mt7623_top_clk_data || !mt7623_pll_clk_data)
-+ return;
-+
-+ clk_prepare_enable(mt7623_pll_clk_data->clks[CLK_APMIXED_ARMPLL]);
-+ clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_MEM_SEL]);
-+ clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_DDRPHYCFG_SEL]);
-+ clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_RTC_SEL]);
-+}
-+
- static void __init mtk_topckgen_init(struct device_node *node)
- {
- struct clk_onecell_data *clk_data;
-@@ -585,7 +599,7 @@ static void __init mtk_topckgen_init(str
- return;
- }
-
-- clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
-+ mt7623_top_clk_data = clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
-
- mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
- clk_data);
-@@ -606,6 +620,8 @@ static void __init mtk_topckgen_init(str
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-+
-+ mtk_clk_enable_critical();
- }
- CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt2701-topckgen", mtk_topckgen_init);
-
-@@ -1202,7 +1218,7 @@ static void __init mtk_apmixedsys_init(s
- struct clk_onecell_data *clk_data;
- int r;
-
-- clk_data = mtk_alloc_clk_data(ARRAY_SIZE(apmixed_plls));
-+ mt7623_pll_clk_data = clk_data = mtk_alloc_clk_data(ARRAY_SIZE(apmixed_plls));
- if (!clk_data)
- return;
-
-@@ -1213,6 +1229,8 @@ static void __init mtk_apmixedsys_init(s
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-+
-+ mtk_clk_enable_critical();
- }
- CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt2701-apmixedsys",
- mtk_apmixedsys_init);
--- /dev/null
+From 99159791184752ece724b741f9fa6334fdc67123 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 14 Mar 2016 15:07:12 +0100
+Subject: [PATCH 061/102] net: mediatek: check device_reset return code
+
+The device_reset() function may fail, so we have to check
+its return value, e.g. to make deferred probing work correctly.
+gcc warns about it because of the warn_unused_result attribute:
+
+drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_probe':
+drivers/net/ethernet/mediatek/mtk_eth_soc.c:1679:2: error: ignoring return value of 'device_reset', declared with attribute warn_unused_result [-Werror=unused-result]
+
+This adds the trivial error check to propagate the return value
+to the generic platform device probe code.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index fcd4ed7..7f2126b 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1678,7 +1678,9 @@ static int mtk_probe(struct platform_device *pdev)
+ struct mtk_eth *eth;
+ int err;
+
+- device_reset(&pdev->dev);
++ err = device_reset(&pdev->dev);
++ if (err)
++ return err;
+
+ match = of_match_device(of_mtk_match, &pdev->dev);
+ soc = (struct mtk_soc_data *)match->data;
+--
+1.7.10.4
+
+++ /dev/null
-From 2ed6efcef399d15910ff60eef72b4cf8e5265c47 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Thu, 31 Mar 2016 02:26:37 +0200
-Subject: [PATCH 62/91] clk: mediatek: Export CPU mux clocks for CPU frequency
- control
-
-This patch adds CPU mux clocks which are used by Mediatek cpufreq driver
-for intermediate clock source switching.
-
-Signed-off-by: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
----
- drivers/clk/mediatek/Makefile | 2 +-
- drivers/clk/mediatek/clk-cpumux.c | 127 ++++++++++++++++++++++++++++++++
- drivers/clk/mediatek/clk-cpumux.h | 22 ++++++
- drivers/clk/mediatek/clk-mt2701.c | 8 ++
- drivers/clk/mediatek/clk-mt8173.c | 23 ++++++
- include/dt-bindings/clock/mt2701-clk.h | 3 +-
- include/dt-bindings/clock/mt8173-clk.h | 4 +-
- 7 files changed, 186 insertions(+), 3 deletions(-)
- create mode 100644 drivers/clk/mediatek/clk-cpumux.c
- create mode 100644 drivers/clk/mediatek/clk-cpumux.h
-
---- a/drivers/clk/mediatek/Makefile
-+++ b/drivers/clk/mediatek/Makefile
-@@ -1,4 +1,4 @@
--obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
-+obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o
- obj-$(CONFIG_RESET_CONTROLLER) += reset.o
- obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
- obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
---- /dev/null
-+++ b/drivers/clk/mediatek/clk-cpumux.c
-@@ -0,0 +1,127 @@
-+/*
-+ * Copyright (c) 2015 Linaro Ltd.
-+ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <linux/clk-provider.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/slab.h>
-+
-+#include "clk-mtk.h"
-+#include "clk-cpumux.h"
-+
-+struct mtk_clk_cpumux {
-+ struct clk_hw hw;
-+ struct regmap *regmap;
-+ u32 reg;
-+ u32 mask;
-+ u8 shift;
-+};
-+
-+static inline struct mtk_clk_cpumux *to_clk_mux(struct clk_hw *_hw)
-+{
-+ return container_of(_hw, struct mtk_clk_cpumux, hw);
-+}
-+
-+static u8 clk_cpumux_get_parent(struct clk_hw *hw)
-+{
-+ struct mtk_clk_cpumux *mux = to_clk_mux(hw);
-+ int num_parents = clk_hw_get_num_parents(hw);
-+ unsigned int val;
-+
-+ regmap_read(mux->regmap, mux->reg, &val);
-+
-+ val >>= mux->shift;
-+ val &= mux->mask;
-+
-+ if (val >= num_parents)
-+ return -EINVAL;
-+
-+ return val;
-+}
-+
-+static int clk_cpumux_set_parent(struct clk_hw *hw, u8 index)
-+{
-+ struct mtk_clk_cpumux *mux = to_clk_mux(hw);
-+ u32 mask, val;
-+
-+ val = index << mux->shift;
-+ mask = mux->mask << mux->shift;
-+
-+ return regmap_update_bits(mux->regmap, mux->reg, mask, val);
-+}
-+
-+static const struct clk_ops clk_cpumux_ops = {
-+ .get_parent = clk_cpumux_get_parent,
-+ .set_parent = clk_cpumux_set_parent,
-+};
-+
-+static struct clk __init *mtk_clk_register_cpumux(const struct mtk_composite *mux,
-+ struct regmap *regmap)
-+{
-+ struct mtk_clk_cpumux *cpumux;
-+ struct clk *clk;
-+ struct clk_init_data init;
-+
-+ cpumux = kzalloc(sizeof(*cpumux), GFP_KERNEL);
-+ if (!cpumux)
-+ return ERR_PTR(-ENOMEM);
-+
-+ init.name = mux->name;
-+ init.ops = &clk_cpumux_ops;
-+ init.parent_names = mux->parent_names;
-+ init.num_parents = mux->num_parents;
-+ init.flags = mux->flags;
-+
-+ cpumux->reg = mux->mux_reg;
-+ cpumux->shift = mux->mux_shift;
-+ cpumux->mask = BIT(mux->mux_width) - 1;
-+ cpumux->regmap = regmap;
-+ cpumux->hw.init = &init;
-+
-+ clk = clk_register(NULL, &cpumux->hw);
-+ if (IS_ERR(clk))
-+ kfree(cpumux);
-+
-+ return clk;
-+}
-+
-+int __init mtk_clk_register_cpumuxes(struct device_node *node,
-+ const struct mtk_composite *clks, int num,
-+ struct clk_onecell_data *clk_data)
-+{
-+ int i;
-+ struct clk *clk;
-+ struct regmap *regmap;
-+
-+ regmap = syscon_node_to_regmap(node);
-+ if (IS_ERR(regmap)) {
-+ pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
-+ PTR_ERR(regmap));
-+ return PTR_ERR(regmap);
-+ }
-+
-+ for (i = 0; i < num; i++) {
-+ const struct mtk_composite *mux = &clks[i];
-+
-+ clk = mtk_clk_register_cpumux(mux, regmap);
-+ if (IS_ERR(clk)) {
-+ pr_err("Failed to register clk %s: %ld\n",
-+ mux->name, PTR_ERR(clk));
-+ continue;
-+ }
-+
-+ clk_data->clks[mux->id] = clk;
-+ }
-+
-+ return 0;
-+}
---- /dev/null
-+++ b/drivers/clk/mediatek/clk-cpumux.h
-@@ -0,0 +1,22 @@
-+/*
-+ * Copyright (c) 2015 Linaro Ltd.
-+ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * 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.
-+ */
-+
-+#ifndef __DRV_CLK_CPUMUX_H
-+#define __DRV_CLK_CPUMUX_H
-+
-+int mtk_clk_register_cpumuxes(struct device_node *node,
-+ const struct mtk_composite *clks, int num,
-+ struct clk_onecell_data *clk_data);
-+
-+#endif /* __DRV_CLK_CPUMUX_H */
---- a/drivers/clk/mediatek/clk-mt2701.c
-+++ b/drivers/clk/mediatek/clk-mt2701.c
-@@ -18,6 +18,7 @@
-
- #include "clk-mtk.h"
- #include "clk-gate.h"
-+#include "clk-cpumux.h"
-
- #include <dt-bindings/clock/mt2701-clk.h>
-
-@@ -465,6 +466,10 @@ static const char * const cpu_parents[]
- "mmpll"
- };
-
-+static const struct mtk_composite cpu_muxes[] __initconst = {
-+ MUX(CLK_INFRA_CPUSEL, "infra_cpu_sel", cpu_parents, 0x0000, 2, 2),
-+};
-+
- static const struct mtk_composite top_muxes[] __initconst = {
- MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
- 0x0040, 0, 3, INVALID_MUX_GATE_BIT),
-@@ -677,6 +682,9 @@ static void __init mtk_infrasys_init(str
- mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
- clk_data);
-
-+ mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
-+ clk_data);
-+
- r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
---- a/drivers/clk/mediatek/clk-mt8173.c
-+++ b/drivers/clk/mediatek/clk-mt8173.c
-@@ -18,6 +18,7 @@
-
- #include "clk-mtk.h"
- #include "clk-gate.h"
-+#include "clk-cpumux.h"
-
- #include <dt-bindings/clock/mt8173-clk.h>
-
-@@ -526,6 +527,25 @@ static const char * const i2s3_b_ck_pare
- "apll2_div5"
- };
-
-+static const char * const ca53_parents[] __initconst = {
-+ "clk26m",
-+ "armca7pll",
-+ "mainpll",
-+ "univpll"
-+};
-+
-+static const char * const ca57_parents[] __initconst = {
-+ "clk26m",
-+ "armca15pll",
-+ "mainpll",
-+ "univpll"
-+};
-+
-+static const struct mtk_composite cpu_muxes[] __initconst = {
-+ MUX(CLK_INFRA_CA53SEL, "infra_ca53_sel", ca53_parents, 0x0000, 0, 2),
-+ MUX(CLK_INFRA_CA57SEL, "infra_ca57_sel", ca57_parents, 0x0000, 2, 2),
-+};
-+
- static const struct mtk_composite top_muxes[] __initconst = {
- /* CLK_CFG_0 */
- MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3),
-@@ -945,6 +965,9 @@ static void __init mtk_infrasys_init(str
- clk_data);
- mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data);
-
-+ mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
-+ clk_data);
-+
- r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
---- a/include/dt-bindings/clock/mt2701-clk.h
-+++ b/include/dt-bindings/clock/mt2701-clk.h
-@@ -217,7 +217,8 @@
- #define CLK_INFRA_PMICWRAP 17
- #define CLK_INFRA_DDCCI 18
- #define CLK_INFRA_CLK_13M 19
--#define CLK_INFRA_NR 20
-+#define CLK_INFRA_CPUSEL 20
-+#define CLK_INFRA_NR 21
-
- /* PERICFG */
-
---- a/include/dt-bindings/clock/mt8173-clk.h
-+++ b/include/dt-bindings/clock/mt8173-clk.h
-@@ -192,7 +192,9 @@
- #define CLK_INFRA_PMICSPI 10
- #define CLK_INFRA_PMICWRAP 11
- #define CLK_INFRA_CLK_13M 12
--#define CLK_INFRA_NR_CLK 13
-+#define CLK_INFRA_CA53SEL 13
-+#define CLK_INFRA_CA57SEL 14
-+#define CLK_INFRA_NR_CLK 15
-
- /* PERI_SYS */
-
--- /dev/null
+From 387257cbd6f3f92de71e2f578d3a9414d0dada27 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Mar 2016 03:18:17 +0200
+Subject: [PATCH 062/102] net: mediatek: watchdog_timeo was not set
+
+The original commit failed to set watchdog_timeo. This patch sets
+watchdog_timeo to HZ.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 7f2126b..7e6d2e2 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1645,6 +1645,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+
+ SET_NETDEV_DEV(eth->netdev[id], eth->dev);
++ eth->netdev[id]->watchdog_timeo = HZ;
+ eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
+ eth->netdev[id]->base_addr = (unsigned long)eth->base;
+ eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
+--
+1.7.10.4
+
+++ /dev/null
-From 668d2c777a41440daa55435c2a217e61c23e4a30 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Wed, 30 Mar 2016 23:48:53 +0200
-Subject: [PATCH 63/91] cpufreq: mediatek: add driver
-
-Signed-off-by: John Crispin <john@phrozen.org>
----
- drivers/cpufreq/Kconfig.arm | 9 +
- drivers/cpufreq/Makefile | 1 +
- drivers/cpufreq/mt7623-cpufreq.c | 389 ++++++++++++++++++++++++++++++++++++++
- 3 files changed, 399 insertions(+)
- create mode 100644 drivers/cpufreq/mt7623-cpufreq.c
-
---- a/drivers/cpufreq/Kconfig.arm
-+++ b/drivers/cpufreq/Kconfig.arm
-@@ -81,6 +81,15 @@ config ARM_KIRKWOOD_CPUFREQ
- This adds the CPUFreq driver for Marvell Kirkwood
- SoCs.
-
-+config ARM_MT7623_CPUFREQ
-+ bool "Mediatek MT7623 CPUFreq support"
-+ depends on ARCH_MEDIATEK && REGULATOR
-+ depends on ARM || (ARM_CPU_TOPOLOGY && COMPILE_TEST)
-+ depends on !CPU_THERMAL || THERMAL=y
-+ select PM_OPP
-+ help
-+ This adds the CPUFreq driver support for Mediatek MT7623 SoC.
-+
- config ARM_MT8173_CPUFREQ
- bool "Mediatek MT8173 CPUFreq support"
- depends on ARCH_MEDIATEK && REGULATOR
---- a/drivers/cpufreq/Makefile
-+++ b/drivers/cpufreq/Makefile
-@@ -57,6 +57,7 @@ obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += h
- obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
- obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o
- obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
-+obj-$(CONFIG_ARM_MT7623_CPUFREQ) += mt7623-cpufreq.o
- obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt8173-cpufreq.o
- obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
- obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
---- /dev/null
-+++ b/drivers/cpufreq/mt7623-cpufreq.c
-@@ -0,0 +1,389 @@
-+/*
-+ * Copyright (c) 2015 Linaro Ltd.
-+ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <linux/clk.h>
-+#include <linux/cpu.h>
-+#include <linux/cpu_cooling.h>
-+#include <linux/cpufreq.h>
-+#include <linux/cpumask.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/pm_opp.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/slab.h>
-+#include <linux/thermal.h>
-+
-+#define VOLT_TOL (10000)
-+
-+/*
-+ * When scaling the clock frequency of a CPU clock domain, the clock source
-+ * needs to be switched to another stable PLL clock temporarily until
-+ * the original PLL becomes stable at target frequency.
-+ */
-+struct mtk_cpu_dvfs_info {
-+ struct device *cpu_dev;
-+ struct regulator *proc_reg;
-+ struct clk *cpu_clk;
-+ struct clk *inter_clk;
-+ struct thermal_cooling_device *cdev;
-+ int intermediate_voltage;
-+};
-+
-+static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
-+{
-+ return regulator_set_voltage(info->proc_reg, vproc,
-+ vproc + VOLT_TOL);
-+}
-+
-+static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
-+ unsigned int index)
-+{
-+ struct cpufreq_frequency_table *freq_table = policy->freq_table;
-+ struct clk *cpu_clk = policy->clk;
-+ struct clk *armpll = clk_get_parent(cpu_clk);
-+ struct mtk_cpu_dvfs_info *info = policy->driver_data;
-+ struct device *cpu_dev = info->cpu_dev;
-+ struct dev_pm_opp *opp;
-+ long freq_hz, old_freq_hz;
-+ int vproc, old_vproc, inter_vproc, target_vproc, ret;
-+
-+ inter_vproc = info->intermediate_voltage;
-+
-+ old_freq_hz = clk_get_rate(cpu_clk);
-+ old_vproc = regulator_get_voltage(info->proc_reg);
-+
-+ freq_hz = freq_table[index].frequency * 1000;
-+
-+ rcu_read_lock();
-+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
-+ if (IS_ERR(opp)) {
-+ rcu_read_unlock();
-+ pr_err("cpu%d: failed to find OPP for %ld\n",
-+ policy->cpu, freq_hz);
-+ return PTR_ERR(opp);
-+ }
-+ vproc = dev_pm_opp_get_voltage(opp);
-+ rcu_read_unlock();
-+
-+ /*
-+ * If the new voltage or the intermediate voltage is higher than the
-+ * current voltage, scale up voltage first.
-+ */
-+ target_vproc = (inter_vproc > vproc) ? inter_vproc : vproc;
-+ if (old_vproc < target_vproc) {
-+ ret = mtk_cpufreq_set_voltage(info, target_vproc);
-+ if (ret) {
-+ pr_err("cpu%d: failed to scale up voltage!\n",
-+ policy->cpu);
-+ mtk_cpufreq_set_voltage(info, old_vproc);
-+ return ret;
-+ }
-+ }
-+
-+ /* Reparent the CPU clock to intermediate clock. */
-+ ret = clk_set_parent(cpu_clk, info->inter_clk);
-+ if (ret) {
-+ pr_err("cpu%d: failed to re-parent cpu clock!\n",
-+ policy->cpu);
-+ mtk_cpufreq_set_voltage(info, old_vproc);
-+ WARN_ON(1);
-+ return ret;
-+ }
-+
-+ /* Set the original PLL to target rate. */
-+ ret = clk_set_rate(armpll, freq_hz);
-+ if (ret) {
-+ pr_err("cpu%d: failed to scale cpu clock rate!\n",
-+ policy->cpu);
-+ clk_set_parent(cpu_clk, armpll);
-+ mtk_cpufreq_set_voltage(info, old_vproc);
-+ return ret;
-+ }
-+
-+ /* Set parent of CPU clock back to the original PLL. */
-+ ret = clk_set_parent(cpu_clk, armpll);
-+ if (ret) {
-+ pr_err("cpu%d: failed to re-parent cpu clock!\n",
-+ policy->cpu);
-+ mtk_cpufreq_set_voltage(info, inter_vproc);
-+ WARN_ON(1);
-+ return ret;
-+ }
-+
-+ /*
-+ * If the new voltage is lower than the intermediate voltage or the
-+ * original voltage, scale down to the new voltage.
-+ */
-+ if (vproc < inter_vproc || vproc < old_vproc) {
-+ ret = mtk_cpufreq_set_voltage(info, vproc);
-+ if (ret) {
-+ pr_err("cpu%d: failed to scale down voltage!\n",
-+ policy->cpu);
-+ clk_set_parent(cpu_clk, info->inter_clk);
-+ clk_set_rate(armpll, old_freq_hz);
-+ clk_set_parent(cpu_clk, armpll);
-+ return ret;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
-+{
-+ struct mtk_cpu_dvfs_info *info = policy->driver_data;
-+ struct device_node *np = of_node_get(info->cpu_dev->of_node);
-+
-+ if (WARN_ON(!np))
-+ return;
-+
-+ if (of_find_property(np, "#cooling-cells", NULL)) {
-+ info->cdev = of_cpufreq_cooling_register(np,
-+ policy->related_cpus);
-+
-+ if (IS_ERR(info->cdev)) {
-+ dev_err(info->cpu_dev,
-+ "running cpufreq without cooling device: %ld\n",
-+ PTR_ERR(info->cdev));
-+
-+ info->cdev = NULL;
-+ }
-+ }
-+
-+ of_node_put(np);
-+}
-+
-+static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
-+{
-+ struct device *cpu_dev;
-+ struct regulator *proc_reg = ERR_PTR(-ENODEV);
-+ struct clk *cpu_clk = ERR_PTR(-ENODEV);
-+ struct clk *inter_clk = ERR_PTR(-ENODEV);
-+ struct dev_pm_opp *opp;
-+ unsigned long rate;
-+ int ret;
-+
-+ cpu_dev = get_cpu_device(cpu);
-+ if (!cpu_dev) {
-+ pr_err("failed to get cpu%d device\n", cpu);
-+ return -ENODEV;
-+ }
-+
-+ cpu_clk = clk_get(cpu_dev, "cpu");
-+ if (IS_ERR(cpu_clk)) {
-+ if (PTR_ERR(cpu_clk) == -EPROBE_DEFER)
-+ pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu);
-+ else
-+ pr_err("failed to get cpu clk for cpu%d\n", cpu);
-+
-+ ret = PTR_ERR(cpu_clk);
-+ return ret;
-+ }
-+
-+ inter_clk = clk_get(cpu_dev, "intermediate");
-+ if (IS_ERR(inter_clk)) {
-+ if (PTR_ERR(inter_clk) == -EPROBE_DEFER)
-+ pr_warn("intermediate clk for cpu%d not ready, retry.\n",
-+ cpu);
-+ else
-+ pr_err("failed to get intermediate clk for cpu%d\n",
-+ cpu);
-+
-+ ret = PTR_ERR(inter_clk);
-+ goto out_free_resources;
-+ }
-+
-+ proc_reg = regulator_get_exclusive(cpu_dev, "proc");
-+ if (IS_ERR(proc_reg)) {
-+ if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
-+ pr_warn("proc regulator for cpu%d not ready, retry.\n",
-+ cpu);
-+ else
-+ pr_err("failed to get proc regulator for cpu%d\n",
-+ cpu);
-+
-+ ret = PTR_ERR(proc_reg);
-+ goto out_free_resources;
-+ }
-+
-+ ret = dev_pm_opp_of_add_table(cpu_dev);
-+ if (ret) {
-+ pr_warn("no OPP table for cpu%d\n", cpu);
-+ goto out_free_resources;
-+ }
-+
-+ /* Search a safe voltage for intermediate frequency. */
-+ rate = clk_get_rate(inter_clk);
-+ rcu_read_lock();
-+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
-+ if (IS_ERR(opp)) {
-+ rcu_read_unlock();
-+ pr_err("failed to get intermediate opp for cpu%d\n", cpu);
-+ ret = PTR_ERR(opp);
-+ goto out_free_opp_table;
-+ }
-+ info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
-+ rcu_read_unlock();
-+
-+ info->cpu_dev = cpu_dev;
-+ info->proc_reg = proc_reg;
-+ info->cpu_clk = cpu_clk;
-+ info->inter_clk = inter_clk;
-+
-+ return 0;
-+
-+out_free_opp_table:
-+ dev_pm_opp_of_remove_table(cpu_dev);
-+
-+out_free_resources:
-+ if (!IS_ERR(proc_reg))
-+ regulator_put(proc_reg);
-+ if (!IS_ERR(cpu_clk))
-+ clk_put(cpu_clk);
-+ if (!IS_ERR(inter_clk))
-+ clk_put(inter_clk);
-+
-+ return ret;
-+}
-+
-+static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
-+{
-+ if (!IS_ERR(info->proc_reg))
-+ regulator_put(info->proc_reg);
-+ if (!IS_ERR(info->cpu_clk))
-+ clk_put(info->cpu_clk);
-+ if (!IS_ERR(info->inter_clk))
-+ clk_put(info->inter_clk);
-+
-+ dev_pm_opp_of_remove_table(info->cpu_dev);
-+}
-+
-+static int mtk_cpufreq_init(struct cpufreq_policy *policy)
-+{
-+ struct mtk_cpu_dvfs_info *info;
-+ struct cpufreq_frequency_table *freq_table;
-+ int ret;
-+
-+ info = kzalloc(sizeof(*info), GFP_KERNEL);
-+ if (!info)
-+ return -ENOMEM;
-+
-+ ret = mtk_cpu_dvfs_info_init(info, policy->cpu);
-+ if (ret) {
-+ pr_err("%s failed to initialize dvfs info for cpu%d\n",
-+ __func__, policy->cpu);
-+ goto out_free_dvfs_info;
-+ }
-+
-+ ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
-+ if (ret) {
-+ pr_err("failed to init cpufreq table for cpu%d: %d\n",
-+ policy->cpu, ret);
-+ goto out_release_dvfs_info;
-+ }
-+
-+ ret = cpufreq_table_validate_and_show(policy, freq_table);
-+ if (ret) {
-+ pr_err("%s: invalid frequency table: %d\n", __func__, ret);
-+ goto out_free_cpufreq_table;
-+ }
-+
-+ /* CPUs in the same cluster share a clock and power domain. */
-+ cpumask_setall(policy->cpus);
-+ policy->driver_data = info;
-+ policy->clk = info->cpu_clk;
-+
-+ return 0;
-+
-+out_free_cpufreq_table:
-+ dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
-+
-+out_release_dvfs_info:
-+ mtk_cpu_dvfs_info_release(info);
-+
-+out_free_dvfs_info:
-+ kfree(info);
-+
-+ return ret;
-+}
-+
-+static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
-+{
-+ struct mtk_cpu_dvfs_info *info = policy->driver_data;
-+
-+ cpufreq_cooling_unregister(info->cdev);
-+ dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
-+ mtk_cpu_dvfs_info_release(info);
-+ kfree(info);
-+
-+ return 0;
-+}
-+
-+static struct cpufreq_driver mt7623_cpufreq_driver = {
-+ .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
-+ .verify = cpufreq_generic_frequency_table_verify,
-+ .target_index = mtk_cpufreq_set_target,
-+ .get = cpufreq_generic_get,
-+ .init = mtk_cpufreq_init,
-+ .exit = mtk_cpufreq_exit,
-+ .ready = mtk_cpufreq_ready,
-+ .name = "mtk-cpufreq",
-+ .attr = cpufreq_generic_attr,
-+};
-+
-+static int mt7623_cpufreq_probe(struct platform_device *pdev)
-+{
-+ int ret;
-+
-+ ret = cpufreq_register_driver(&mt7623_cpufreq_driver);
-+ if (ret)
-+ pr_err("failed to register mtk cpufreq driver\n");
-+
-+ return ret;
-+}
-+
-+static struct platform_driver mt7623_cpufreq_platdrv = {
-+ .driver = {
-+ .name = "mt7623-cpufreq",
-+ },
-+ .probe = mt7623_cpufreq_probe,
-+};
-+
-+static int mt7623_cpufreq_driver_init(void)
-+{
-+ struct platform_device *pdev;
-+ int err;
-+
-+ if (!of_machine_is_compatible("mediatek,mt7623"))
-+ return -ENODEV;
-+
-+ err = platform_driver_register(&mt7623_cpufreq_platdrv);
-+ if (err)
-+ return err;
-+
-+ /*
-+ * Since there's no place to hold device registration code and no
-+ * device tree based way to match cpufreq driver yet, both the driver
-+ * and the device registration codes are put here to handle defer
-+ * probing.
-+ */
-+ pdev = platform_device_register_simple("mt7623-cpufreq", -1, NULL, 0);
-+ if (IS_ERR(pdev)) {
-+ pr_err("failed to register mtk-cpufreq platform device\n");
-+ return PTR_ERR(pdev);
-+ }
-+
-+ return 0;
-+}
-+device_initcall(mt7623_cpufreq_driver_init);
--- /dev/null
+From d8f3e96943334c91ecc0827ed0d3232068c389e6 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 22 Mar 2016 04:42:27 +0100
+Subject: [PATCH 063/102] net: mediatek: mtk_cal_txd_req() returns bad value
+
+The code used to also support the PDMA engine, which had 2 packet pointers
+per descriptor. Because of this we have to divide the result by 2 and round
+it up. This is no longer needed as the code only supports QDMA.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 7e6d2e2..4d8d0a3 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -681,7 +681,7 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
+ nfrags += skb_shinfo(skb)->nr_frags;
+ }
+
+- return DIV_ROUND_UP(nfrags, 2);
++ return nfrags;
+ }
+
+ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+--
+1.7.10.4
+
+++ /dev/null
-From 6eeadfb48dc5e73dae115fc0be9416e3d5fed84d Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Thu, 31 Mar 2016 06:07:01 +0200
-Subject: [PATCH 64/91] arm: mediatek: make a7 timer work Signed-off-by: John
- Crispin <blogic@openwrt.org>
-
----
- arch/arm/mach-mediatek/Kconfig | 1 +
- arch/arm/mach-mediatek/mediatek.c | 1 +
- 2 files changed, 2 insertions(+)
-
---- a/arch/arm/mach-mediatek/Kconfig
-+++ b/arch/arm/mach-mediatek/Kconfig
-@@ -24,6 +24,7 @@ config MACH_MT6592
- config MACH_MT7623
- bool "MediaTek MT7623 SoCs support"
- default ARCH_MEDIATEK
-+ select HAVE_ARM_ARCH_TIMER
- select MIGHT_HAVE_PCI
-
- config MACH_MT8127
---- a/arch/arm/mach-mediatek/mediatek.c
-+++ b/arch/arm/mach-mediatek/mediatek.c
-@@ -29,6 +29,7 @@ static void __init mediatek_timer_init(v
- void __iomem *gpt_base;
-
- if (of_machine_is_compatible("mediatek,mt6589") ||
-+ of_machine_is_compatible("mediatek,mt7623") ||
- of_machine_is_compatible("mediatek,mt8135") ||
- of_machine_is_compatible("mediatek,mt8127")) {
- /* turn on GPT6 which ungates arch timer clocks */
--- /dev/null
+From 2597d2cedba62b2a3fdca9c044187705f98a0372 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Fri, 25 Mar 2016 04:24:27 +0100
+Subject: [PATCH 064/102] net: mediatek: remove superflous reset call
+
+HW reset is triggered int he mtk_hw_init() function. There is no need to
+reset the core during probe.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 4d8d0a3..293ea59 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1679,10 +1679,6 @@ static int mtk_probe(struct platform_device *pdev)
+ struct mtk_eth *eth;
+ int err;
+
+- err = device_reset(&pdev->dev);
+- if (err)
+- return err;
+-
+ match = of_match_device(of_mtk_match, &pdev->dev);
+ soc = (struct mtk_soc_data *)match->data;
+
+--
+1.7.10.4
+
+++ /dev/null
-From 0b88e5873b97ab20566b51134123fda7050d4d08 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@oracle.com>
-Date: Tue, 15 Mar 2016 10:18:49 +0300
-Subject: [PATCH 65/91] net: mediatek: checking for IS_ERR() instead of NULL
-
-of_phy_connect() returns NULL on error, it never returns error pointers.
-
-Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
-Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -186,9 +186,9 @@ static int mtk_phy_connect_node(struct m
-
- phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
- mtk_phy_link_adjust, 0, phy_mode);
-- if (IS_ERR(phydev)) {
-+ if (!phydev) {
- dev_err(eth->dev, "could not connect to PHY\n");
-- return PTR_ERR(phydev);
-+ return -ENODEV;
- }
-
- dev_info(eth->dev,
--- /dev/null
+From afc838dde560ab584d3fb0e4b011e4a6770dab3d Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 16:41:07 +0200
+Subject: [PATCH 065/102] net: mediatek: fix stop and wakeup of queue
+
+The driver supports 2 MACs. Both run on the same DMA ring. If we go
+above/below the TX rings thershold value, we always need to wake/stop
+the queu of both devices. Not doing to can cause TX stalls and packet
+drops on one of the devices.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 37 +++++++++++++++++++--------
+ 1 file changed, 27 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 293ea59..04bdb9d 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -684,6 +684,28 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
+ return nfrags;
+ }
+
++static void mtk_wake_queue(struct mtk_eth *eth)
++{
++ int i;
++
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ if (!eth->netdev[i])
++ continue;
++ netif_wake_queue(eth->netdev[i]);
++ }
++}
++
++static void mtk_stop_queue(struct mtk_eth *eth)
++{
++ int i;
++
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ if (!eth->netdev[i])
++ continue;
++ netif_stop_queue(eth->netdev[i]);
++ }
++}
++
+ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+@@ -695,7 +717,7 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+
+ tx_num = mtk_cal_txd_req(skb);
+ if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
+- netif_stop_queue(dev);
++ mtk_stop_queue(eth);
+ netif_err(eth, tx_queued, dev,
+ "Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+@@ -720,10 +742,10 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ goto drop;
+
+ if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
+- netif_stop_queue(dev);
++ mtk_stop_queue(eth);
+ if (unlikely(atomic_read(&ring->free_count) >
+ ring->thresh))
+- netif_wake_queue(dev);
++ mtk_wake_queue(eth);
+ }
+
+ return NETDEV_TX_OK;
+@@ -897,13 +919,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ if (!total)
+ return 0;
+
+- for (i = 0; i < MTK_MAC_COUNT; i++) {
+- if (!eth->netdev[i] ||
+- unlikely(!netif_queue_stopped(eth->netdev[i])))
+- continue;
+- if (atomic_read(&ring->free_count) > ring->thresh)
+- netif_wake_queue(eth->netdev[i]);
+- }
++ if (atomic_read(&ring->free_count) > ring->thresh)
++ mtk_wake_queue(eth);
+
+ return total;
+ }
+--
+1.7.10.4
+
--- /dev/null
+From e2cc73e6ddb0cc39b8f58654a449651a621916a9 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 17:00:47 +0200
+Subject: [PATCH 066/102] net: mediatek: fix mtk_pending_work
+
+The driver supports 2 MACs. Both run on the same DMA ring. If we hit a TX
+timeout we need to stop both netdevs before retarting them again. If we
+dont do thsi, mtk_stop() wont shutdown DMA and the consecutive call to
+mtk_open() wont restart DMA and enable IRQs.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 30 +++++++++++++++++++--------
+ 1 file changed, 21 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 04bdb9d..26eeb1a 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1430,19 +1430,31 @@ static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+
+ static void mtk_pending_work(struct work_struct *work)
+ {
+- struct mtk_mac *mac = container_of(work, struct mtk_mac, pending_work);
+- struct mtk_eth *eth = mac->hw;
+- struct net_device *dev = eth->netdev[mac->id];
+- int err;
++ struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work);
++ int err, i;
++ unsigned long restart = 0;
+
+ rtnl_lock();
+- mtk_stop(dev);
+
+- err = mtk_open(dev);
+- if (err) {
+- netif_alert(eth, ifup, dev,
++ /* stop all devices to make sure that dma is properly shut down */
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ if (!netif_oper_up(eth->netdev[i]))
++ continue;
++ mtk_stop(eth->netdev[i]);
++ __set_bit(i, &restart);
++ }
++
++
++ /* restart DMA and enable IRQs */
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ if (!test_bit(i, &restart))
++ continue;
++ err = mtk_open(eth->netdev[i]);
++ if (err) {
++ netif_alert(eth, ifup, eth->netdev[i],
+ "Driver up/down cycle failed, closing device.\n");
+- dev_close(dev);
++ dev_close(eth->netdev[i]);
++ }
+ }
+ rtnl_unlock();
+ }
+--
+1.7.10.4
+
+++ /dev/null
-From 489994e9cb0d9f762c31e2af9205188ae8f3b013 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@oracle.com>
-Date: Tue, 15 Mar 2016 10:19:04 +0300
-Subject: [PATCH 66/91] net: mediatek: unlock on error in mtk_tx_map()
-
-There was a missing unlock on the error path.
-
-Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
-Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -661,6 +661,8 @@ err_dma:
- itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
- } while (itxd != txd);
-
-+ spin_unlock_irqrestore(ð->page_lock, flags);
-+
- return -ENOMEM;
- }
-
--- /dev/null
+From 6f152b2bdb295d86beb746494ef6fddf17986f8e Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 17:20:01 +0200
+Subject: [PATCH 067/102] net: mediatek: fix TX locking
+
+Inside the TX path there is a lock inside the tx_map function. This is
+however too late. The patch moves the lock to the start of the xmit
+function right before the free count check of the DMA ring happens.
+If we do not do this, the code becomes racy leading to TX stalls and
+dropped packets. This happens as there are 2 netdevs running on the
+same physical DMA ring.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 26eeb1a..67b18f9 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -536,7 +536,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_dma *itxd, *txd;
+ struct mtk_tx_buf *tx_buf;
+- unsigned long flags;
+ dma_addr_t mapped_addr;
+ unsigned int nr_frags;
+ int i, n_desc = 1;
+@@ -568,11 +567,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ return -ENOMEM;
+
+- /* normally we can rely on the stack not calling this more than once,
+- * however we have 2 queues running ont he same ring so we need to lock
+- * the ring access
+- */
+- spin_lock_irqsave(ð->page_lock, flags);
+ WRITE_ONCE(itxd->txd1, mapped_addr);
+ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+@@ -632,8 +626,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+ (!nr_frags * TX_DMA_LS0)));
+
+- spin_unlock_irqrestore(ð->page_lock, flags);
+-
+ netdev_sent_queue(dev, skb->len);
+ skb_tx_timestamp(skb);
+
+@@ -661,8 +653,6 @@ err_dma:
+ itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
+ } while (itxd != txd);
+
+- spin_unlock_irqrestore(ð->page_lock, flags);
+-
+ return -ENOMEM;
+ }
+
+@@ -712,14 +702,22 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ struct net_device_stats *stats = &dev->stats;
++ unsigned long flags;
+ bool gso = false;
+ int tx_num;
+
++ /* normally we can rely on the stack not calling this more than once,
++ * however we have 2 queues running ont he same ring so we need to lock
++ * the ring access
++ */
++ spin_lock_irqsave(ð->page_lock, flags);
++
+ tx_num = mtk_cal_txd_req(skb);
+ if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
+ mtk_stop_queue(eth);
+ netif_err(eth, tx_queued, dev,
+ "Tx Ring full when queue awake!\n");
++ spin_unlock_irqrestore(ð->page_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+@@ -747,10 +745,12 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ ring->thresh))
+ mtk_wake_queue(eth);
+ }
++ spin_unlock_irqrestore(ð->page_lock, flags);
+
+ return NETDEV_TX_OK;
+
+ drop:
++ spin_unlock_irqrestore(ð->page_lock, flags);
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+--
+1.7.10.4
+
+++ /dev/null
-From ac345476b98f3856bbf3938e114d4be799f8bd69 Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 14 Mar 2016 15:07:10 +0100
-Subject: [PATCH 67/91] net: mediatek: use dma_addr_t correctly
-
-dma_alloc_coherent() expects a dma_addr_t pointer as its argument,
-not an 'unsigned int', and gcc correctly warns about broken
-code in the mtk_init_fq_dma function:
-
-drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_init_fq_dma':
-drivers/net/ethernet/mediatek/mtk_eth_soc.c:463:13: error: passing argument 3 of 'dma_alloc_coherent' from incompatible pointer type [-Werror=incompatible-pointer-types]
-
-This changes the type of the local variable to dma_addr_t.
-
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -453,7 +453,7 @@ static inline void mtk_rx_get_desc(struc
- /* the qdma core needs scratch memory to be setup */
- static int mtk_init_fq_dma(struct mtk_eth *eth)
- {
-- unsigned int phy_ring_head, phy_ring_tail;
-+ dma_addr_t phy_ring_head, phy_ring_tail;
- int cnt = MTK_DMA_SIZE;
- dma_addr_t dma_addr;
- int i;
--- /dev/null
+From 29bc7a1e374425937b5dd2f316dbeef343d4c68a Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 17:24:24 +0200
+Subject: [PATCH 068/102] net: mediatek: move the pending_work struct to the
+ device generic struct
+
+The worker always touches both netdevs. It is ethernet core and not MAC
+specific. We only need one worker, which belongs into the ethernets core struct.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 10 ++++------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++--
+ 2 files changed, 6 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 67b18f9..bbcd607 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1193,7 +1193,7 @@ static void mtk_tx_timeout(struct net_device *dev)
+ eth->netdev[mac->id]->stats.tx_errors++;
+ netif_err(eth, tx_err, dev,
+ "transmit timed out\n");
+- schedule_work(&mac->pending_work);
++ schedule_work(ð->pending_work);
+ }
+
+ static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+@@ -1438,7 +1438,7 @@ static void mtk_pending_work(struct work_struct *work)
+
+ /* stop all devices to make sure that dma is properly shut down */
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+- if (!netif_oper_up(eth->netdev[i]))
++ if (!eth->netdev[i])
+ continue;
+ mtk_stop(eth->netdev[i]);
+ __set_bit(i, &restart);
+@@ -1464,15 +1464,13 @@ static int mtk_cleanup(struct mtk_eth *eth)
+ int i;
+
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+- struct mtk_mac *mac = netdev_priv(eth->netdev[i]);
+-
+ if (!eth->netdev[i])
+ continue;
+
+ unregister_netdev(eth->netdev[i]);
+ free_netdev(eth->netdev[i]);
+- cancel_work_sync(&mac->pending_work);
+ }
++ cancel_work_sync(ð->pending_work);
+
+ return 0;
+ }
+@@ -1660,7 +1658,6 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ mac->id = id;
+ mac->hw = eth;
+ mac->of_node = np;
+- INIT_WORK(&mac->pending_work, mtk_pending_work);
+
+ mac->hw_stats = devm_kzalloc(eth->dev,
+ sizeof(*mac->hw_stats),
+@@ -1762,6 +1759,7 @@ static int mtk_probe(struct platform_device *pdev)
+
+ eth->dev = &pdev->dev;
+ eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
++ INIT_WORK(ð->pending_work, mtk_pending_work);
+
+ err = mtk_hw_init(eth);
+ if (err)
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index 48a5292..eed626d 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -363,6 +363,7 @@ struct mtk_rx_ring {
+ * @clk_gp1: The gmac1 clock
+ * @clk_gp2: The gmac2 clock
+ * @mii_bus: If there is a bus we need to create an instance for it
++ * @pending_work: The workqueue used to reset the dma ring
+ */
+
+ struct mtk_eth {
+@@ -389,6 +390,7 @@ struct mtk_eth {
+ struct clk *clk_gp1;
+ struct clk *clk_gp2;
+ struct mii_bus *mii_bus;
++ struct work_struct pending_work;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
+@@ -398,7 +400,6 @@ struct mtk_eth {
+ * @hw: Backpointer to our main datastruture
+ * @hw_stats: Packet statistics counter
+ * @phy_dev: The attached PHY if available
+- * @pending_work: The workqueue used to reset the dma ring
+ */
+ struct mtk_mac {
+ int id;
+@@ -406,7 +407,6 @@ struct mtk_mac {
+ struct mtk_eth *hw;
+ struct mtk_hw_stats *hw_stats;
+ struct phy_device *phy_dev;
+- struct work_struct pending_work;
+ };
+
+ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+--
+1.7.10.4
+
+++ /dev/null
-From 8b6bb80616460eda2e70e358c5fb70c0f4d4d02f Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 14 Mar 2016 15:07:11 +0100
-Subject: [PATCH 68/91] net: mediatek: remove incorrect dma_mask assignment
-
-Device drivers should not mess with the DMA mask directly,
-but instead call dma_set_mask() etc if needed.
-
-In case of the mtk_eth_soc driver, the mask already gets set
-correctly when the device is created, and setting it again
-is against the documented API.
-
-This removes the incorrect setting.
-
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 ---
- 1 file changed, 3 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1678,9 +1678,6 @@ static int mtk_probe(struct platform_dev
- struct mtk_eth *eth;
- int err;
-
-- pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
-- pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
--
- device_reset(&pdev->dev);
-
- match = of_match_device(of_mtk_match, &pdev->dev);
+++ /dev/null
-From cd7ea7dae994beea798115f4c34c96f45cc028d1 Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 14 Mar 2016 15:07:12 +0100
-Subject: [PATCH 69/91] net: mediatek: check device_reset return code
-
-The device_reset() function may fail, so we have to check
-its return value, e.g. to make deferred probing work correctly.
-gcc warns about it because of the warn_unused_result attribute:
-
-drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_probe':
-drivers/net/ethernet/mediatek/mtk_eth_soc.c:1679:2: error: ignoring return value of 'device_reset', declared with attribute warn_unused_result [-Werror=unused-result]
-
-This adds the trivial error check to propagate the return value
-to the generic platform device probe code.
-
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1678,7 +1678,9 @@ static int mtk_probe(struct platform_dev
- struct mtk_eth *eth;
- int err;
-
-- device_reset(&pdev->dev);
-+ err = device_reset(&pdev->dev);
-+ if (err)
-+ return err;
-
- match = of_match_device(of_mtk_match, &pdev->dev);
- soc = (struct mtk_soc_data *)match->data;
--- /dev/null
+From 4742349c1595d38b3e3b463e66cf21af4217c869 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 7 Apr 2016 17:36:23 +0200
+Subject: [PATCH 069/102] net: mediatek: do not set the QID field in the TX
+ DMA descriptors
+
+The QID field gets set to the mac id. This made the DMA linked list queue
+the traffic of each MAC on a different internal queue. However during long
+term testing we found that this will cause traffic stalls as the multi
+queue setup requires a more complete initialisation which is not part of
+the upstream driver yet.
+
+This patch removes the code setting the QID field, resulting in all
+traffic ending up in queue 0 which works without any special setup.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index bbcd607..bab5d45 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -603,8 +603,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ WRITE_ONCE(txd->txd1, mapped_addr);
+ WRITE_ONCE(txd->txd3, (TX_DMA_SWC |
+ TX_DMA_PLEN0(frag_map_size) |
+- last_frag * TX_DMA_LS0) |
+- mac->id);
++ last_frag * TX_DMA_LS0));
+ WRITE_ONCE(txd->txd4, 0);
+
+ tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC;
+--
+1.7.10.4
+
--- /dev/null
+From 297ef52cd21e28da671996d7b4f39f268d2d0ec1 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 14:32:07 +0200
+Subject: [PATCH 070/102] net: mediatek: update the IRQ part of the binding
+ document
+
+The current binding document only describes a single interrupt. Update the
+document by adding the 2 other interrupts.
+
+The driver currently only uses a single interrupt. The HW is however able
+to using IRQ grouping to split TX and RX onto separate GIC irqs.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+ Acked-by: Rob Herring <robh@kernel.org>
+---
+ Documentation/devicetree/bindings/net/mediatek-net.txt | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/net/mediatek-net.txt b/Documentation/devicetree/bindings/net/mediatek-net.txt
+index 5ca7929..32eaaca 100644
+--- a/Documentation/devicetree/bindings/net/mediatek-net.txt
++++ b/Documentation/devicetree/bindings/net/mediatek-net.txt
+@@ -9,7 +9,8 @@ have dual GMAC each represented by a child node..
+ Required properties:
+ - compatible: Should be "mediatek,mt7623-eth"
+ - reg: Address and length of the register set for the device
+-- interrupts: Should contain the frame engines interrupt
++- interrupts: Should contain the three frame engines interrupts in numeric
++ order. These are fe_int0, fe_int1 and fe_int2.
+ - clocks: the clock used by the core
+ - clock-names: the names of the clock listed in the clocks property. These are
+ "ethif", "esw", "gp2", "gp1"
+@@ -42,7 +43,9 @@ eth: ethernet@1b100000 {
+ <ðsys CLK_ETHSYS_GP2>,
+ <ðsys CLK_ETHSYS_GP1>;
+ clock-names = "ethif", "esw", "gp2", "gp1";
+- interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW>;
++ interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW
++ GIC_SPI 199 IRQ_TYPE_LEVEL_LOW
++ GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>;
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
+ resets = <ðsys MT2701_ETHSYS_ETH_RST>;
+ reset-names = "eth";
+--
+1.7.10.4
+
+++ /dev/null
-From 5fac03871435c52f7f9b7f34aefb2774089d32f9 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Wed, 30 Mar 2016 03:18:17 +0200
-Subject: [PATCH 70/91] net: mediatek: watchdog_timeo was not set
-
-The original commit failed to set watchdog_timeo. This patch sets
-watchdog_timeo to HZ.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1645,6 +1645,7 @@ static int mtk_add_mac(struct mtk_eth *e
- mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
-
- SET_NETDEV_DEV(eth->netdev[id], eth->dev);
-+ eth->netdev[id]->watchdog_timeo = HZ;
- eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
- eth->netdev[id]->base_addr = (unsigned long)eth->base;
- eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
+++ /dev/null
-From ca0d5851de3763fe309d3083693f1a438c6e98c9 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 22 Mar 2016 04:42:27 +0100
-Subject: [PATCH 71/91] net: mediatek: mtk_cal_txd_req() returns bad value
-
-The code used to also support the PDMA engine, which had 2 packet pointers
-per descriptor. Because of this we have to divide the result by 2 and round
-it up. This is no longer needed as the code only supports QDMA.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -681,7 +681,7 @@ static inline int mtk_cal_txd_req(struct
- nfrags += skb_shinfo(skb)->nr_frags;
- }
-
-- return DIV_ROUND_UP(nfrags, 2);
-+ return nfrags;
- }
-
- static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
--- /dev/null
+From 6f5941c93bdf7649f392f1263b9068d360ceab4d Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 6 May 2016 02:55:48 +0200
+Subject: [PATCH 071/102] pwm: add pwm-mediatek
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ arch/arm/boot/dts/mt7623-evb.dts | 17 +++
+ arch/arm/boot/dts/mt7623.dtsi | 22 ++++
+ drivers/pwm/Kconfig | 9 ++
+ drivers/pwm/Makefile | 1 +
+ drivers/pwm/pwm-mediatek.c | 230 ++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 279 insertions(+)
+ create mode 100644 drivers/pwm/pwm-mediatek.c
+
+diff --git a/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts
+index 5ad1448..70bc6b1 100644
+--- a/arch/arm/boot/dts/mt7623-evb.dts
++++ b/arch/arm/boot/dts/mt7623-evb.dts
+@@ -341,6 +341,17 @@
+ output-low;
+ };
+ };
++
++ pwm_pins: pwm {
++ pins_pwm1 {
++ pinmux = <MT7623_PIN_204_PWM1_FUNC_PWM1>;
++ };
++
++ pins_pwm2 {
++ pinmux = <MT7623_PIN_205_PWM2_FUNC_PWM2>;
++ };
++ };
++
+ };
+
+ &nandc {
+@@ -419,3 +430,9 @@
+ mediatek,reset-pin = <&pio 15 0>;
+ status = "okay";
+ };
++
++&pwm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_pins>;
++ status = "okay";
++};
+diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
+index cbbdf16..3f50e7e 100644
+--- a/arch/arm/boot/dts/mt7623.dtsi
++++ b/arch/arm/boot/dts/mt7623.dtsi
+@@ -324,6 +324,28 @@
+ status = "disabled";
+ };
+
++ pwm: pwm@11006000 {
++ compatible = "mediatek,mt7623-pwm";
++
++ reg = <0 0x11006000 0 0x1000>;
++
++ resets = <&pericfg MT2701_PERI_PWM_SW_RST>;
++ reset-names = "pwm";
++
++ #pwm-cells = <2>;
++ clocks = <&topckgen CLK_TOP_PWM_SEL>,
++ <&pericfg CLK_PERI_PWM>,
++ <&pericfg CLK_PERI_PWM1>,
++ <&pericfg CLK_PERI_PWM2>,
++ <&pericfg CLK_PERI_PWM3>,
++ <&pericfg CLK_PERI_PWM4>,
++ <&pericfg CLK_PERI_PWM5>;
++ clock-names = "top", "main", "pwm1", "pwm2",
++ "pwm3", "pwm4", "pwm5";
++
++ status = "disabled";
++ };
++
+ spi: spi@1100a000 {
+ compatible = "mediatek,mt7623-spi", "mediatek,mt6589-spi";
+ reg = <0 0x1100a000 0 0x1000>;
+diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
+index 2f4641a..5860b1f 100644
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -260,6 +260,15 @@ config PWM_MTK_DISP
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-mtk-disp.
+
++config PWM_MEDIATEK
++ tristate "MediaTek PWM support"
++ depends on ARCH_MEDIATEK || COMPILE_TEST
++ help
++ Generic PWM framework driver for Mediatek ARM SoC.
++
++ To compile this driver as a module, choose M here: the module
++ will be called pwm-mxs.
++
+ config PWM_MXS
+ tristate "Freescale MXS PWM support"
+ depends on ARCH_MXS && OF
+diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
+index 69b8275..a90d5de 100644
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
+ obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
+ obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o
+ obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o
++obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o
+ obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o
+ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
+ obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
+diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c
+new file mode 100644
+index 0000000..9d8d16d
+--- /dev/null
++++ b/drivers/pwm/pwm-mediatek.c
+@@ -0,0 +1,230 @@
++/*
++ * Mediatek Pulse Width Modulator driver
++ *
++ * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
++ *
++ * This file is licensed under the terms of the GNU General Public
++ * License version 2. This program is licensed "as is" without any
++ * warranty of any kind, whether express or implied.
++ */
++
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/clk.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#define NUM_PWM 5
++
++/* PWM registers and bits definitions */
++#define PWMCON 0x00
++#define PWMHDUR 0x04
++#define PWMLDUR 0x08
++#define PWMGDUR 0x0c
++#define PWMWAVENUM 0x28
++#define PWMDWIDTH 0x2c
++#define PWMTHRES 0x30
++
++/**
++ * struct mtk_pwm_chip - struct representing pwm chip
++ *
++ * @mmio_base: base address of pwm chip
++ * @chip: linux pwm chip representation
++ */
++struct mtk_pwm_chip {
++ void __iomem *mmio_base;
++ struct pwm_chip chip;
++ struct clk *clk_top;
++ struct clk *clk_main;
++ struct clk *clk_pwm[NUM_PWM];
++};
++
++static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip)
++{
++ return container_of(chip, struct mtk_pwm_chip, chip);
++}
++
++static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num,
++ unsigned long offset)
++{
++ return ioread32(chip->mmio_base + 0x10 + (num * 0x40) + offset);
++}
++
++static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip,
++ unsigned int num, unsigned long offset,
++ unsigned long val)
++{
++ iowrite32(val, chip->mmio_base + 0x10 + (num * 0x40) + offset);
++}
++
++static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
++ int duty_ns, int period_ns)
++{
++ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
++ u32 resolution = 100 / 4;
++ u32 clkdiv = 0;
++
++ resolution = 1000000000 / (clk_get_rate(pc->clk_pwm[pwm->hwpwm]));
++
++ while (period_ns / resolution > 8191) {
++ clkdiv++;
++ resolution *= 2;
++ }
++
++ if (clkdiv > 7)
++ return -1;
++
++ mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | clkdiv);
++ mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution);
++ mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution);
++ return 0;
++}
++
++static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
++ u32 val;
++ int ret;
++
++ ret = clk_prepare(pc->clk_pwm[pwm->hwpwm]);
++ if (ret < 0)
++ return ret;
++
++ val = ioread32(pc->mmio_base);
++ val |= BIT(pwm->hwpwm);
++ iowrite32(val, pc->mmio_base);
++
++ return 0;
++}
++
++static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
++ u32 val;
++
++ val = ioread32(pc->mmio_base);
++ val &= ~BIT(pwm->hwpwm);
++ iowrite32(val, pc->mmio_base);
++ clk_unprepare(pc->clk_pwm[pwm->hwpwm]);
++}
++
++static const struct pwm_ops mtk_pwm_ops = {
++ .config = mtk_pwm_config,
++ .enable = mtk_pwm_enable,
++ .disable = mtk_pwm_disable,
++ .owner = THIS_MODULE,
++};
++
++static int mtk_pwm_probe(struct platform_device *pdev)
++{
++ struct mtk_pwm_chip *pc;
++ struct resource *r;
++ int ret;
++
++ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
++ if (!pc)
++ return -ENOMEM;
++
++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ pc->mmio_base = devm_ioremap_resource(&pdev->dev, r);
++ if (IS_ERR(pc->mmio_base))
++ return PTR_ERR(pc->mmio_base);
++
++ pc->clk_main = devm_clk_get(&pdev->dev, "main");
++ if (IS_ERR(pc->clk_main))
++ return PTR_ERR(pc->clk_main);
++
++ pc->clk_top = devm_clk_get(&pdev->dev, "top");
++ if (IS_ERR(pc->clk_top))
++ return PTR_ERR(pc->clk_top);
++
++ pc->clk_pwm[0] = devm_clk_get(&pdev->dev, "pwm1");
++ if (IS_ERR(pc->clk_pwm[0]))
++ return PTR_ERR(pc->clk_pwm[0]);
++
++ pc->clk_pwm[1] = devm_clk_get(&pdev->dev, "pwm2");
++ if (IS_ERR(pc->clk_pwm[1]))
++ return PTR_ERR(pc->clk_pwm[1]);
++
++ pc->clk_pwm[2] = devm_clk_get(&pdev->dev, "pwm3");
++ if (IS_ERR(pc->clk_pwm[2]))
++ return PTR_ERR(pc->clk_pwm[2]);
++
++ pc->clk_pwm[3] = devm_clk_get(&pdev->dev, "pwm4");
++ if (IS_ERR(pc->clk_pwm[3]))
++ return PTR_ERR(pc->clk_pwm[3]);
++
++ pc->clk_pwm[4] = devm_clk_get(&pdev->dev, "pwm5");
++ if (IS_ERR(pc->clk_pwm[4]))
++ return PTR_ERR(pc->clk_pwm[4]);
++
++ ret = clk_prepare(pc->clk_top);
++ if (ret < 0)
++ return ret;
++
++ ret = clk_prepare(pc->clk_main);
++ if (ret < 0)
++ goto disable_clk_top;
++
++ platform_set_drvdata(pdev, pc);
++
++ pc->chip.dev = &pdev->dev;
++ pc->chip.ops = &mtk_pwm_ops;
++ pc->chip.base = -1;
++ pc->chip.npwm = NUM_PWM;
++
++ ret = pwmchip_add(&pc->chip);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
++ goto disable_clk_main;
++ }
++
++ return 0;
++
++disable_clk_main:
++ clk_unprepare(pc->clk_main);
++disable_clk_top:
++ clk_unprepare(pc->clk_top);
++
++ return ret;
++}
++
++static int mtk_pwm_remove(struct platform_device *pdev)
++{
++ struct mtk_pwm_chip *pc = platform_get_drvdata(pdev);
++ int i;
++
++ for (i = 0; i < NUM_PWM; i++)
++ pwm_disable(&pc->chip.pwms[i]);
++
++ return pwmchip_remove(&pc->chip);
++}
++
++static const struct of_device_id mtk_pwm_of_match[] = {
++ { .compatible = "mediatek,mt7623-pwm" },
++ { }
++};
++
++MODULE_DEVICE_TABLE(of, mtk_pwm_of_match);
++
++static struct platform_driver mtk_pwm_driver = {
++ .driver = {
++ .name = "mtk-pwm",
++ .owner = THIS_MODULE,
++ .of_match_table = mtk_pwm_of_match,
++ },
++ .probe = mtk_pwm_probe,
++ .remove = mtk_pwm_remove,
++};
++
++module_platform_driver(mtk_pwm_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
++MODULE_ALIAS("platform:mtk-pwm");
+--
+1.7.10.4
+
--- /dev/null
+From a369af5149e6eb442b22ce89b564dd7a76e03638 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 26 Apr 2016 19:05:01 +0200
+Subject: [PATCH 072/102] mtd: backport v4.7-0day patches from Boris
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/mtd/Kconfig | 4 +-
+ drivers/mtd/cmdlinepart.c | 3 +-
+ drivers/mtd/devices/m25p80.c | 44 +--
+ drivers/mtd/maps/physmap_of.c | 6 +-
+ drivers/mtd/mtdchar.c | 123 ++++++--
+ drivers/mtd/mtdconcat.c | 2 +-
+ drivers/mtd/mtdcore.c | 428 ++++++++++++++++++++++++--
+ drivers/mtd/mtdcore.h | 7 +-
+ drivers/mtd/mtdpart.c | 161 ++++++----
+ drivers/mtd/mtdswap.c | 24 +-
+ drivers/mtd/nand/Kconfig | 21 +-
+ drivers/mtd/nand/Makefile | 2 +
+ drivers/mtd/nand/nand_base.c | 571 +++++++++++++++++++----------------
+ drivers/mtd/nand/nand_bbt.c | 34 +--
+ drivers/mtd/nand/nand_bch.c | 52 ++--
+ drivers/mtd/nand/nand_ecc.c | 6 +-
+ drivers/mtd/nand/nand_ids.c | 4 +-
+ drivers/mtd/nand/nandsim.c | 43 +--
+ drivers/mtd/ofpart.c | 53 ++--
+ drivers/mtd/spi-nor/Kconfig | 10 +-
+ drivers/mtd/spi-nor/Makefile | 1 +
+ drivers/mtd/spi-nor/mtk-quadspi.c | 485 +++++++++++++++++++++++++++++
+ drivers/mtd/spi-nor/spi-nor.c | 321 +++++++++++++-------
+ drivers/mtd/tests/mtd_nandecctest.c | 2 +-
+ drivers/mtd/tests/oobtest.c | 49 ++-
+ drivers/mtd/tests/pagetest.c | 3 +-
+ drivers/mtd/ubi/cdev.c | 4 +-
+ drivers/mtd/ubi/misc.c | 49 +++
+ drivers/mtd/ubi/ubi.h | 16 +-
+ drivers/mtd/ubi/upd.c | 2 +-
+ drivers/mtd/ubi/wl.c | 21 +-
+ include/linux/mtd/bbm.h | 1 -
+ include/linux/mtd/fsmc.h | 18 --
+ include/linux/mtd/inftl.h | 1 -
+ include/linux/mtd/map.h | 9 +-
+ include/linux/mtd/mtd.h | 80 ++++-
+ include/linux/mtd/nand.h | 94 ++++--
+ include/linux/mtd/nand_bch.h | 10 +-
+ include/linux/mtd/nftl.h | 1 -
+ include/linux/mtd/onenand.h | 2 -
+ include/linux/mtd/partitions.h | 27 +-
+ include/linux/mtd/sh_flctl.h | 4 +-
+ include/linux/mtd/sharpsl.h | 2 +-
+ include/linux/mtd/spi-nor.h | 23 +-
+ include/uapi/mtd/mtd-abi.h | 2 +-
+ 45 files changed, 2077 insertions(+), 748 deletions(-)
+ create mode 100644 drivers/mtd/spi-nor/mtk-quadspi.c
+
+diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
+index a03ad29..e83a279 100644
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -112,7 +112,7 @@ config MTD_CMDLINE_PARTS
+
+ config MTD_AFS_PARTS
+ tristate "ARM Firmware Suite partition parsing"
+- depends on ARM
++ depends on (ARM || ARM64)
+ ---help---
+ The ARM Firmware Suite allows the user to divide flash devices into
+ multiple 'images'. Each such image has a header containing its name
+@@ -142,7 +142,7 @@ config MTD_AR7_PARTS
+
+ config MTD_BCM63XX_PARTS
+ tristate "BCM63XX CFE partitioning support"
+- depends on BCM63XX
++ depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
+ select CRC32
+ help
+ This provides partions parsing for BCM63xx devices with CFE
+diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c
+index 08f6298..fbd5aff 100644
+--- a/drivers/mtd/cmdlinepart.c
++++ b/drivers/mtd/cmdlinepart.c
+@@ -304,7 +304,7 @@ static int mtdpart_setup_real(char *s)
+ * the first one in the chain if a NULL mtd_id is passed in.
+ */
+ static int parse_cmdline_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
++ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+ {
+ unsigned long long offset;
+@@ -382,7 +382,6 @@ static int __init mtdpart_setup(char *s)
+ __setup("mtdparts=", mtdpart_setup);
+
+ static struct mtd_part_parser cmdline_parser = {
+- .owner = THIS_MODULE,
+ .parse_fn = parse_cmdline_partitions,
+ .name = "cmdlinepart",
+ };
+diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
+index fe9ceb7..c9c3b7f 100644
+--- a/drivers/mtd/devices/m25p80.c
++++ b/drivers/mtd/devices/m25p80.c
+@@ -152,22 +152,6 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
+ return 0;
+ }
+
+-static int m25p80_erase(struct spi_nor *nor, loff_t offset)
+-{
+- struct m25p *flash = nor->priv;
+-
+- dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
+- flash->spi_nor.mtd.erasesize / 1024, (u32)offset);
+-
+- /* Set up command buffer. */
+- flash->command[0] = nor->erase_opcode;
+- m25p_addr2cmd(nor, offset, flash->command);
+-
+- spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
+-
+- return 0;
+-}
+-
+ /*
+ * board specific setup should have ensured the SPI clock used here
+ * matches what the READ command supports, at least until this driver
+@@ -175,12 +159,11 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset)
+ */
+ static int m25p_probe(struct spi_device *spi)
+ {
+- struct mtd_part_parser_data ppdata;
+ struct flash_platform_data *data;
+ struct m25p *flash;
+ struct spi_nor *nor;
+ enum read_mode mode = SPI_NOR_NORMAL;
+- char *flash_name = NULL;
++ char *flash_name;
+ int ret;
+
+ data = dev_get_platdata(&spi->dev);
+@@ -194,12 +177,11 @@ static int m25p_probe(struct spi_device *spi)
+ /* install the hooks */
+ nor->read = m25p80_read;
+ nor->write = m25p80_write;
+- nor->erase = m25p80_erase;
+ nor->write_reg = m25p80_write_reg;
+ nor->read_reg = m25p80_read_reg;
+
+ nor->dev = &spi->dev;
+- nor->flash_node = spi->dev.of_node;
++ spi_nor_set_flash_node(nor, spi->dev.of_node);
+ nor->priv = flash;
+
+ spi_set_drvdata(spi, flash);
+@@ -220,6 +202,8 @@ static int m25p_probe(struct spi_device *spi)
+ */
+ if (data && data->type)
+ flash_name = data->type;
++ else if (!strcmp(spi->modalias, "spi-nor"))
++ flash_name = NULL; /* auto-detect */
+ else
+ flash_name = spi->modalias;
+
+@@ -227,11 +211,8 @@ static int m25p_probe(struct spi_device *spi)
+ if (ret)
+ return ret;
+
+- ppdata.of_node = spi->dev.of_node;
+-
+- return mtd_device_parse_register(&nor->mtd, NULL, &ppdata,
+- data ? data->parts : NULL,
+- data ? data->nr_parts : 0);
++ return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
++ data ? data->nr_parts : 0);
+ }
+
+
+@@ -257,14 +238,21 @@ static int m25p_remove(struct spi_device *spi)
+ */
+ static const struct spi_device_id m25p_ids[] = {
+ /*
++ * Allow non-DT platform devices to bind to the "spi-nor" modalias, and
++ * hack around the fact that the SPI core does not provide uevent
++ * matching for .of_match_table
++ */
++ {"spi-nor"},
++
++ /*
+ * Entries not used in DTs that should be safe to drop after replacing
+- * them with "nor-jedec" in platform data.
++ * them with "spi-nor" in platform data.
+ */
+ {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
+
+ /*
+- * Entries that were used in DTs without "nor-jedec" fallback and should
+- * be kept for backward compatibility.
++ * Entries that were used in DTs without "jedec,spi-nor" fallback and
++ * should be kept for backward compatibility.
+ */
+ {"at25df321a"}, {"at25df641"}, {"at26df081a"},
+ {"mr25h256"},
+diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
+index e46b4e9..70c4531 100644
+--- a/drivers/mtd/maps/physmap_of.c
++++ b/drivers/mtd/maps/physmap_of.c
+@@ -166,7 +166,6 @@ static int of_flash_probe(struct platform_device *dev)
+ int reg_tuple_size;
+ struct mtd_info **mtd_list = NULL;
+ resource_size_t res_size;
+- struct mtd_part_parser_data ppdata;
+ bool map_indirect;
+ const char *mtd_name = NULL;
+
+@@ -310,13 +309,14 @@ static int of_flash_probe(struct platform_device *dev)
+ if (err)
+ goto err_out;
+
+- ppdata.of_node = dp;
++ info->cmtd->dev.parent = &dev->dev;
++ mtd_set_of_node(info->cmtd, dp);
+ part_probe_types = of_get_probes(dp);
+ if (!part_probe_types) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+- mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata,
++ mtd_device_parse_register(info->cmtd, part_probe_types, NULL,
+ NULL, 0);
+ of_free_probes(part_probe_types);
+
+diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
+index 6d19835..2a47a3f 100644
+--- a/drivers/mtd/mtdchar.c
++++ b/drivers/mtd/mtdchar.c
+@@ -465,35 +465,108 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,
+ }
+
+ /*
+- * Copies (and truncates, if necessary) data from the larger struct,
+- * nand_ecclayout, to the smaller, deprecated layout struct,
+- * nand_ecclayout_user. This is necessary only to support the deprecated
+- * API ioctl ECCGETLAYOUT while allowing all new functionality to use
+- * nand_ecclayout flexibly (i.e. the struct may change size in new
+- * releases without requiring major rewrites).
++ * Copies (and truncates, if necessary) OOB layout information to the
++ * deprecated layout struct, nand_ecclayout_user. This is necessary only to
++ * support the deprecated API ioctl ECCGETLAYOUT while allowing all new
++ * functionality to use mtd_ooblayout_ops flexibly (i.e. mtd_ooblayout_ops
++ * can describe any kind of OOB layout with almost zero overhead from a
++ * memory usage point of view).
+ */
+-static int shrink_ecclayout(const struct nand_ecclayout *from,
+- struct nand_ecclayout_user *to)
++static int shrink_ecclayout(struct mtd_info *mtd,
++ struct nand_ecclayout_user *to)
+ {
+- int i;
++ struct mtd_oob_region oobregion;
++ int i, section = 0, ret;
+
+- if (!from || !to)
++ if (!mtd || !to)
+ return -EINVAL;
+
+ memset(to, 0, sizeof(*to));
+
+- to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
+- for (i = 0; i < to->eccbytes; i++)
+- to->eccpos[i] = from->eccpos[i];
++ to->eccbytes = 0;
++ for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) {
++ u32 eccpos;
++
++ ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
++ if (ret < 0) {
++ if (ret != -ERANGE)
++ return ret;
++
++ break;
++ }
++
++ eccpos = oobregion.offset;
++ for (; i < MTD_MAX_ECCPOS_ENTRIES &&
++ eccpos < oobregion.offset + oobregion.length; i++) {
++ to->eccpos[i] = eccpos++;
++ to->eccbytes++;
++ }
++ }
+
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
+- if (from->oobfree[i].length == 0 &&
+- from->oobfree[i].offset == 0)
++ ret = mtd_ooblayout_free(mtd, i, &oobregion);
++ if (ret < 0) {
++ if (ret != -ERANGE)
++ return ret;
++
++ break;
++ }
++
++ to->oobfree[i].offset = oobregion.offset;
++ to->oobfree[i].length = oobregion.length;
++ to->oobavail += to->oobfree[i].length;
++ }
++
++ return 0;
++}
++
++static int get_oobinfo(struct mtd_info *mtd, struct nand_oobinfo *to)
++{
++ struct mtd_oob_region oobregion;
++ int i, section = 0, ret;
++
++ if (!mtd || !to)
++ return -EINVAL;
++
++ memset(to, 0, sizeof(*to));
++
++ to->eccbytes = 0;
++ for (i = 0; i < ARRAY_SIZE(to->eccpos);) {
++ u32 eccpos;
++
++ ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
++ if (ret < 0) {
++ if (ret != -ERANGE)
++ return ret;
++
+ break;
+- to->oobavail += from->oobfree[i].length;
+- to->oobfree[i] = from->oobfree[i];
++ }
++
++ if (oobregion.length + i > ARRAY_SIZE(to->eccpos))
++ return -EINVAL;
++
++ eccpos = oobregion.offset;
++ for (; eccpos < oobregion.offset + oobregion.length; i++) {
++ to->eccpos[i] = eccpos++;
++ to->eccbytes++;
++ }
+ }
+
++ for (i = 0; i < 8; i++) {
++ ret = mtd_ooblayout_free(mtd, i, &oobregion);
++ if (ret < 0) {
++ if (ret != -ERANGE)
++ return ret;
++
++ break;
++ }
++
++ to->oobfree[i][0] = oobregion.offset;
++ to->oobfree[i][1] = oobregion.length;
++ }
++
++ to->useecc = MTD_NANDECC_AUTOPLACE;
++
+ return 0;
+ }
+
+@@ -815,16 +888,12 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
+ {
+ struct nand_oobinfo oi;
+
+- if (!mtd->ecclayout)
++ if (!mtd->ooblayout)
+ return -EOPNOTSUPP;
+- if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
+- return -EINVAL;
+
+- oi.useecc = MTD_NANDECC_AUTOPLACE;
+- memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
+- memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
+- sizeof(oi.oobfree));
+- oi.eccbytes = mtd->ecclayout->eccbytes;
++ ret = get_oobinfo(mtd, &oi);
++ if (ret)
++ return ret;
+
+ if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
+ return -EFAULT;
+@@ -913,14 +982,14 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
+ {
+ struct nand_ecclayout_user *usrlay;
+
+- if (!mtd->ecclayout)
++ if (!mtd->ooblayout)
+ return -EOPNOTSUPP;
+
+ usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
+ if (!usrlay)
+ return -ENOMEM;
+
+- shrink_ecclayout(mtd->ecclayout, usrlay);
++ shrink_ecclayout(mtd, usrlay);
+
+ if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
+ ret = -EFAULT;
+diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
+index 239a8c8..d573606 100644
+--- a/drivers/mtd/mtdconcat.c
++++ b/drivers/mtd/mtdconcat.c
+@@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
+
+ }
+
+- concat->mtd.ecclayout = subdev[0]->ecclayout;
++ mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout);
+
+ concat->num_subdev = num_devs;
+ concat->mtd.name = name;
+diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
+index 95c13b2..cbfa5ad 100644
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -32,6 +32,7 @@
+ #include <linux/err.h>
+ #include <linux/ioctl.h>
+ #include <linux/init.h>
++#include <linux/of.h>
+ #include <linux/proc_fs.h>
+ #include <linux/idr.h>
+ #include <linux/backing-dev.h>
+@@ -426,15 +427,6 @@ int add_mtd_device(struct mtd_info *mtd)
+ mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
+ mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
+
+- if (mtd->dev.parent) {
+- if (!mtd->owner && mtd->dev.parent->driver)
+- mtd->owner = mtd->dev.parent->driver->owner;
+- if (!mtd->name)
+- mtd->name = dev_name(mtd->dev.parent);
+- } else {
+- pr_debug("mtd device won't show a device symlink in sysfs\n");
+- }
+-
+ /* Some chips always power up locked. Unlock them now */
+ if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
+ error = mtd_unlock(mtd, 0, mtd->size);
+@@ -454,6 +446,7 @@ int add_mtd_device(struct mtd_info *mtd)
+ mtd->dev.devt = MTD_DEVT(i);
+ dev_set_name(&mtd->dev, "mtd%d", i);
+ dev_set_drvdata(&mtd->dev, mtd);
++ of_node_get(mtd_get_of_node(mtd));
+ error = device_register(&mtd->dev);
+ if (error)
+ goto fail_added;
+@@ -476,6 +469,7 @@ int add_mtd_device(struct mtd_info *mtd)
+ return 0;
+
+ fail_added:
++ of_node_put(mtd_get_of_node(mtd));
+ idr_remove(&mtd_idr, i);
+ fail_locked:
+ mutex_unlock(&mtd_table_mutex);
+@@ -517,6 +511,7 @@ int del_mtd_device(struct mtd_info *mtd)
+ device_unregister(&mtd->dev);
+
+ idr_remove(&mtd_idr, mtd->index);
++ of_node_put(mtd_get_of_node(mtd));
+
+ module_put(THIS_MODULE);
+ ret = 0;
+@@ -528,9 +523,10 @@ out_error:
+ }
+
+ static int mtd_add_device_partitions(struct mtd_info *mtd,
+- struct mtd_partition *real_parts,
+- int nbparts)
++ struct mtd_partitions *parts)
+ {
++ const struct mtd_partition *real_parts = parts->parts;
++ int nbparts = parts->nr_parts;
+ int ret;
+
+ if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
+@@ -549,6 +545,21 @@ static int mtd_add_device_partitions(struct mtd_info *mtd,
+ return 0;
+ }
+
++/*
++ * Set a few defaults based on the parent devices, if not provided by the
++ * driver
++ */
++static void mtd_set_dev_defaults(struct mtd_info *mtd)
++{
++ if (mtd->dev.parent) {
++ if (!mtd->owner && mtd->dev.parent->driver)
++ mtd->owner = mtd->dev.parent->driver->owner;
++ if (!mtd->name)
++ mtd->name = dev_name(mtd->dev.parent);
++ } else {
++ pr_debug("mtd device won't show a device symlink in sysfs\n");
++ }
++}
+
+ /**
+ * mtd_device_parse_register - parse partitions and register an MTD device.
+@@ -584,27 +595,29 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
+ const struct mtd_partition *parts,
+ int nr_parts)
+ {
++ struct mtd_partitions parsed;
+ int ret;
+- struct mtd_partition *real_parts = NULL;
+-
+- ret = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
+- if (ret <= 0 && nr_parts && parts) {
+- real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
+- GFP_KERNEL);
+- if (!real_parts)
+- ret = -ENOMEM;
+- else
+- ret = nr_parts;
+- }
+- /* Didn't come up with either parsed OR fallback partitions */
+- if (ret < 0) {
++
++ mtd_set_dev_defaults(mtd);
++
++ memset(&parsed, 0, sizeof(parsed));
++
++ ret = parse_mtd_partitions(mtd, types, &parsed, parser_data);
++ if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) {
++ /* Fall back to driver-provided partitions */
++ parsed = (struct mtd_partitions){
++ .parts = parts,
++ .nr_parts = nr_parts,
++ };
++ } else if (ret < 0) {
++ /* Didn't come up with parsed OR fallback partitions */
+ pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n",
+ ret);
+ /* Don't abort on errors; we can still use unpartitioned MTD */
+- ret = 0;
++ memset(&parsed, 0, sizeof(parsed));
+ }
+
+- ret = mtd_add_device_partitions(mtd, real_parts, ret);
++ ret = mtd_add_device_partitions(mtd, &parsed);
+ if (ret)
+ goto out;
+
+@@ -624,7 +637,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
+ }
+
+ out:
+- kfree(real_parts);
++ /* Cleanup any parsed partitions */
++ mtd_part_parser_cleanup(&parsed);
+ return ret;
+ }
+ EXPORT_SYMBOL_GPL(mtd_device_parse_register);
+@@ -983,6 +997,366 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
+ }
+ EXPORT_SYMBOL_GPL(mtd_read_oob);
+
++/**
++ * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
++ * @mtd: MTD device structure
++ * @section: ECC section. Depending on the layout you may have all the ECC
++ * bytes stored in a single contiguous section, or one section
++ * per ECC chunk (and sometime several sections for a single ECC
++ * ECC chunk)
++ * @oobecc: OOB region struct filled with the appropriate ECC position
++ * information
++ *
++ * This functions return ECC section information in the OOB area. I you want
++ * to get all the ECC bytes information, then you should call
++ * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobecc)
++{
++ memset(oobecc, 0, sizeof(*oobecc));
++
++ if (!mtd || section < 0)
++ return -EINVAL;
++
++ if (!mtd->ooblayout || !mtd->ooblayout->ecc)
++ return -ENOTSUPP;
++
++ return mtd->ooblayout->ecc(mtd, section, oobecc);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
++
++/**
++ * mtd_ooblayout_free - Get the OOB region definition of a specific free
++ * section
++ * @mtd: MTD device structure
++ * @section: Free section you are interested in. Depending on the layout
++ * you may have all the free bytes stored in a single contiguous
++ * section, or one section per ECC chunk plus an extra section
++ * for the remaining bytes (or other funky layout).
++ * @oobfree: OOB region struct filled with the appropriate free position
++ * information
++ *
++ * This functions return free bytes position in the OOB area. I you want
++ * to get all the free bytes information, then you should call
++ * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobfree)
++{
++ memset(oobfree, 0, sizeof(*oobfree));
++
++ if (!mtd || section < 0)
++ return -EINVAL;
++
++ if (!mtd->ooblayout || !mtd->ooblayout->free)
++ return -ENOTSUPP;
++
++ return mtd->ooblayout->free(mtd, section, oobfree);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
++
++/**
++ * mtd_ooblayout_find_region - Find the region attached to a specific byte
++ * @mtd: mtd info structure
++ * @byte: the byte we are searching for
++ * @sectionp: pointer where the section id will be stored
++ * @oobregion: used to retrieve the ECC position
++ * @iter: iterator function. Should be either mtd_ooblayout_free or
++ * mtd_ooblayout_ecc depending on the region type you're searching for
++ *
++ * This functions returns the section id and oobregion information of a
++ * specific byte. For example, say you want to know where the 4th ECC byte is
++ * stored, you'll use:
++ *
++ * mtd_ooblayout_find_region(mtd, 3, §ion, &oobregion, mtd_ooblayout_ecc);
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
++ int *sectionp, struct mtd_oob_region *oobregion,
++ int (*iter)(struct mtd_info *,
++ int section,
++ struct mtd_oob_region *oobregion))
++{
++ int pos = 0, ret, section = 0;
++
++ memset(oobregion, 0, sizeof(*oobregion));
++
++ while (1) {
++ ret = iter(mtd, section, oobregion);
++ if (ret)
++ return ret;
++
++ if (pos + oobregion->length > byte)
++ break;
++
++ pos += oobregion->length;
++ section++;
++ }
++
++ /*
++ * Adjust region info to make it start at the beginning at the
++ * 'start' ECC byte.
++ */
++ oobregion->offset += byte - pos;
++ oobregion->length -= byte - pos;
++ *sectionp = section;
++
++ return 0;
++}
++
++/**
++ * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
++ * ECC byte
++ * @mtd: mtd info structure
++ * @eccbyte: the byte we are searching for
++ * @sectionp: pointer where the section id will be stored
++ * @oobregion: OOB region information
++ *
++ * Works like mtd_ooblayout_find_region() except it searches for a specific ECC
++ * byte.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
++ int *section,
++ struct mtd_oob_region *oobregion)
++{
++ return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
++ mtd_ooblayout_ecc);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
++
++/**
++ * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
++ * @mtd: mtd info structure
++ * @buf: destination buffer to store OOB bytes
++ * @oobbuf: OOB buffer
++ * @start: first byte to retrieve
++ * @nbytes: number of bytes to retrieve
++ * @iter: section iterator
++ *
++ * Extract bytes attached to a specific category (ECC or free)
++ * from the OOB buffer and copy them into buf.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
++ const u8 *oobbuf, int start, int nbytes,
++ int (*iter)(struct mtd_info *,
++ int section,
++ struct mtd_oob_region *oobregion))
++{
++ struct mtd_oob_region oobregion = { };
++ int section = 0, ret;
++
++ ret = mtd_ooblayout_find_region(mtd, start, §ion,
++ &oobregion, iter);
++
++ while (!ret) {
++ int cnt;
++
++ cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
++ memcpy(buf, oobbuf + oobregion.offset, cnt);
++ buf += cnt;
++ nbytes -= cnt;
++
++ if (!nbytes)
++ break;
++
++ ret = iter(mtd, ++section, &oobregion);
++ }
++
++ return ret;
++}
++
++/**
++ * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
++ * @mtd: mtd info structure
++ * @buf: source buffer to get OOB bytes from
++ * @oobbuf: OOB buffer
++ * @start: first OOB byte to set
++ * @nbytes: number of OOB bytes to set
++ * @iter: section iterator
++ *
++ * Fill the OOB buffer with data provided in buf. The category (ECC or free)
++ * is selected by passing the appropriate iterator.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
++ u8 *oobbuf, int start, int nbytes,
++ int (*iter)(struct mtd_info *,
++ int section,
++ struct mtd_oob_region *oobregion))
++{
++ struct mtd_oob_region oobregion = { };
++ int section = 0, ret;
++
++ ret = mtd_ooblayout_find_region(mtd, start, §ion,
++ &oobregion, iter);
++
++ while (!ret) {
++ int cnt;
++
++ cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
++ memcpy(oobbuf + oobregion.offset, buf, cnt);
++ buf += cnt;
++ nbytes -= cnt;
++
++ if (!nbytes)
++ break;
++
++ ret = iter(mtd, ++section, &oobregion);
++ }
++
++ return ret;
++}
++
++/**
++ * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
++ * @mtd: mtd info structure
++ * @iter: category iterator
++ *
++ * Count the number of bytes in a given category.
++ *
++ * Returns a positive value on success, a negative error code otherwise.
++ */
++static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
++ int (*iter)(struct mtd_info *,
++ int section,
++ struct mtd_oob_region *oobregion))
++{
++ struct mtd_oob_region oobregion = { };
++ int section = 0, ret, nbytes = 0;
++
++ while (1) {
++ ret = iter(mtd, section++, &oobregion);
++ if (ret) {
++ if (ret == -ERANGE)
++ ret = nbytes;
++ break;
++ }
++
++ nbytes += oobregion.length;
++ }
++
++ return ret;
++}
++
++/**
++ * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
++ * @mtd: mtd info structure
++ * @eccbuf: destination buffer to store ECC bytes
++ * @oobbuf: OOB buffer
++ * @start: first ECC byte to retrieve
++ * @nbytes: number of ECC bytes to retrieve
++ *
++ * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
++ const u8 *oobbuf, int start, int nbytes)
++{
++ return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
++ mtd_ooblayout_ecc);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
++
++/**
++ * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
++ * @mtd: mtd info structure
++ * @eccbuf: source buffer to get ECC bytes from
++ * @oobbuf: OOB buffer
++ * @start: first ECC byte to set
++ * @nbytes: number of ECC bytes to set
++ *
++ * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
++ u8 *oobbuf, int start, int nbytes)
++{
++ return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
++ mtd_ooblayout_ecc);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
++
++/**
++ * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
++ * @mtd: mtd info structure
++ * @databuf: destination buffer to store ECC bytes
++ * @oobbuf: OOB buffer
++ * @start: first ECC byte to retrieve
++ * @nbytes: number of ECC bytes to retrieve
++ *
++ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
++ const u8 *oobbuf, int start, int nbytes)
++{
++ return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
++ mtd_ooblayout_free);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
++
++/**
++ * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer
++ * @mtd: mtd info structure
++ * @eccbuf: source buffer to get data bytes from
++ * @oobbuf: OOB buffer
++ * @start: first ECC byte to set
++ * @nbytes: number of ECC bytes to set
++ *
++ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
++ u8 *oobbuf, int start, int nbytes)
++{
++ return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
++ mtd_ooblayout_free);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
++
++/**
++ * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
++ * @mtd: mtd info structure
++ *
++ * Works like mtd_ooblayout_count_bytes(), except it count free bytes.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
++{
++ return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
++
++/**
++ * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB
++ * @mtd: mtd info structure
++ *
++ * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
++ *
++ * Returns zero on success, a negative error code otherwise.
++ */
++int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
++{
++ return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
++}
++EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
++
+ /*
+ * Method to access the protection register area, present in some flash
+ * devices. The user data is one time programmable but the factory data is read
+diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
+index 7b03533..55fdb8e 100644
+--- a/drivers/mtd/mtdcore.h
++++ b/drivers/mtd/mtdcore.h
+@@ -10,10 +10,15 @@ int add_mtd_device(struct mtd_info *mtd);
+ int del_mtd_device(struct mtd_info *mtd);
+ int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
+ int del_mtd_partitions(struct mtd_info *);
++
++struct mtd_partitions;
++
+ int parse_mtd_partitions(struct mtd_info *master, const char * const *types,
+- struct mtd_partition **pparts,
++ struct mtd_partitions *pparts,
+ struct mtd_part_parser_data *data);
+
++void mtd_part_parser_cleanup(struct mtd_partitions *parts);
++
+ int __init init_mtdchar(void);
+ void __exit cleanup_mtdchar(void);
+
+diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
+index f8ba153..1f13e32 100644
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -48,9 +48,12 @@ struct mtd_part {
+
+ /*
+ * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
+- * the pointer to that structure with this macro.
++ * the pointer to that structure.
+ */
+-#define PART(x) ((struct mtd_part *)(x))
++static inline struct mtd_part *mtd_to_part(const struct mtd_info *mtd)
++{
++ return container_of(mtd, struct mtd_part, mtd);
++}
+
+
+ /*
+@@ -61,7 +64,7 @@ struct mtd_part {
+ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ struct mtd_ecc_stats stats;
+ int res;
+
+@@ -80,7 +83,7 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
+ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, void **virt, resource_size_t *phys)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+
+ return part->master->_point(part->master, from + part->offset, len,
+ retlen, virt, phys);
+@@ -88,7 +91,7 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
+
+ static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+
+ return part->master->_unpoint(part->master, from + part->offset, len);
+ }
+@@ -98,7 +101,7 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
+ unsigned long offset,
+ unsigned long flags)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+
+ offset += part->offset;
+ return part->master->_get_unmapped_area(part->master, len, offset,
+@@ -108,7 +111,7 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
+ static int part_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ int res;
+
+ if (from >= mtd->size)
+@@ -123,10 +126,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
+ if (ops->oobbuf) {
+ size_t len, pages;
+
+- if (ops->mode == MTD_OPS_AUTO_OOB)
+- len = mtd->oobavail;
+- else
+- len = mtd->oobsize;
++ len = mtd_oobavail(mtd, ops);
+ pages = mtd_div_by_ws(mtd->size, mtd);
+ pages -= mtd_div_by_ws(from, mtd);
+ if (ops->ooboffs + ops->ooblen > pages * len)
+@@ -146,7 +146,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
+ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_read_user_prot_reg(part->master, from, len,
+ retlen, buf);
+ }
+@@ -154,7 +154,7 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_get_user_prot_info(part->master, len, retlen,
+ buf);
+ }
+@@ -162,7 +162,7 @@ static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_read_fact_prot_reg(part->master, from, len,
+ retlen, buf);
+ }
+@@ -170,7 +170,7 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+ static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_get_fact_prot_info(part->master, len, retlen,
+ buf);
+ }
+@@ -178,7 +178,7 @@ static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_write(part->master, to + part->offset, len,
+ retlen, buf);
+ }
+@@ -186,7 +186,7 @@ static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
+ static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_panic_write(part->master, to + part->offset, len,
+ retlen, buf);
+ }
+@@ -194,7 +194,7 @@ static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ static int part_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+
+ if (to >= mtd->size)
+ return -EINVAL;
+@@ -206,7 +206,7 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
+ static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_write_user_prot_reg(part->master, from, len,
+ retlen, buf);
+ }
+@@ -214,21 +214,21 @@ static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_lock_user_prot_reg(part->master, from, len);
+ }
+
+ static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_writev(part->master, vecs, count,
+ to + part->offset, retlen);
+ }
+
+ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ int ret;
+
+ instr->addr += part->offset;
+@@ -244,7 +244,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
+ void mtd_erase_callback(struct erase_info *instr)
+ {
+ if (instr->mtd->_erase == part_erase) {
+- struct mtd_part *part = PART(instr->mtd);
++ struct mtd_part *part = mtd_to_part(instr->mtd);
+
+ if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
+ instr->fail_addr -= part->offset;
+@@ -257,57 +257,57 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
+
+ static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_lock(part->master, ofs + part->offset, len);
+ }
+
+ static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_unlock(part->master, ofs + part->offset, len);
+ }
+
+ static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_is_locked(part->master, ofs + part->offset, len);
+ }
+
+ static void part_sync(struct mtd_info *mtd)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ part->master->_sync(part->master);
+ }
+
+ static int part_suspend(struct mtd_info *mtd)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return part->master->_suspend(part->master);
+ }
+
+ static void part_resume(struct mtd_info *mtd)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ part->master->_resume(part->master);
+ }
+
+ static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ ofs += part->offset;
+ return part->master->_block_isreserved(part->master, ofs);
+ }
+
+ static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ ofs += part->offset;
+ return part->master->_block_isbad(part->master, ofs);
+ }
+
+ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
+ {
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ int res;
+
+ ofs += part->offset;
+@@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
+ return res;
+ }
+
++static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobregion)
++{
++ struct mtd_part *part = mtd_to_part(mtd);
++
++ return mtd_ooblayout_ecc(part->master, section, oobregion);
++}
++
++static int part_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobregion)
++{
++ struct mtd_part *part = mtd_to_part(mtd);
++
++ return mtd_ooblayout_free(part->master, section, oobregion);
++}
++
++static const struct mtd_ooblayout_ops part_ooblayout_ops = {
++ .ecc = part_ooblayout_ecc,
++ .free = part_ooblayout_free,
++};
++
+ static inline void free_partition(struct mtd_part *p)
+ {
+ kfree(p->mtd.name);
+@@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
+ part->name);
+ }
+
+- slave->mtd.ecclayout = master->ecclayout;
++ mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
+ slave->mtd.ecc_step_size = master->ecc_step_size;
+ slave->mtd.ecc_strength = master->ecc_strength;
+ slave->mtd.bitflip_threshold = master->bitflip_threshold;
+@@ -558,7 +579,7 @@ static ssize_t mtd_partition_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+ {
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+- struct mtd_part *part = PART(mtd);
++ struct mtd_part *part = mtd_to_part(mtd);
+ return snprintf(buf, PAGE_SIZE, "%lld\n", part->offset);
+ }
+
+@@ -596,11 +617,10 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
+ if (length <= 0)
+ return -EINVAL;
+
++ memset(&part, 0, sizeof(part));
+ part.name = name;
+ part.size = length;
+ part.offset = offset;
+- part.mask_flags = 0;
+- part.ecclayout = NULL;
+
+ new = allocate_partition(master, &part, -1, offset);
+ if (IS_ERR(new))
+@@ -685,7 +705,7 @@ int add_mtd_partitions(struct mtd_info *master,
+ static DEFINE_SPINLOCK(part_parser_lock);
+ static LIST_HEAD(part_parsers);
+
+-static struct mtd_part_parser *get_partition_parser(const char *name)
++static struct mtd_part_parser *mtd_part_parser_get(const char *name)
+ {
+ struct mtd_part_parser *p, *ret = NULL;
+
+@@ -702,15 +722,35 @@ static struct mtd_part_parser *get_partition_parser(const char *name)
+ return ret;
+ }
+
+-#define put_partition_parser(p) do { module_put((p)->owner); } while (0)
++static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
++{
++ module_put(p->owner);
++}
++
++/*
++ * Many partition parsers just expected the core to kfree() all their data in
++ * one chunk. Do that by default.
++ */
++static void mtd_part_parser_cleanup_default(const struct mtd_partition *pparts,
++ int nr_parts)
++{
++ kfree(pparts);
++}
+
+-void register_mtd_parser(struct mtd_part_parser *p)
++int __register_mtd_parser(struct mtd_part_parser *p, struct module *owner)
+ {
++ p->owner = owner;
++
++ if (!p->cleanup)
++ p->cleanup = &mtd_part_parser_cleanup_default;
++
+ spin_lock(&part_parser_lock);
+ list_add(&p->list, &part_parsers);
+ spin_unlock(&part_parser_lock);
++
++ return 0;
+ }
+-EXPORT_SYMBOL_GPL(register_mtd_parser);
++EXPORT_SYMBOL_GPL(__register_mtd_parser);
+
+ void deregister_mtd_parser(struct mtd_part_parser *p)
+ {
+@@ -734,7 +774,7 @@ static const char * const default_mtd_part_types[] = {
+ * parse_mtd_partitions - parse MTD partitions
+ * @master: the master partition (describes whole MTD device)
+ * @types: names of partition parsers to try or %NULL
+- * @pparts: array of partitions found is returned here
++ * @pparts: info about partitions found is returned here
+ * @data: MTD partition parser-specific data
+ *
+ * This function tries to find partition on MTD device @master. It uses MTD
+@@ -746,12 +786,13 @@ static const char * const default_mtd_part_types[] = {
+ *
+ * This function may return:
+ * o a negative error code in case of failure
+- * o zero if no partitions were found
+- * o a positive number of found partitions, in which case on exit @pparts will
+- * point to an array containing this number of &struct mtd_info objects.
++ * o zero otherwise, and @pparts will describe the partitions, number of
++ * partitions, and the parser which parsed them. Caller must release
++ * resources with mtd_part_parser_cleanup() when finished with the returned
++ * data.
+ */
+ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
+- struct mtd_partition **pparts,
++ struct mtd_partitions *pparts,
+ struct mtd_part_parser_data *data)
+ {
+ struct mtd_part_parser *parser;
+@@ -762,22 +803,24 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
+
+ for ( ; *types; types++) {
+ pr_debug("%s: parsing partitions %s\n", master->name, *types);
+- parser = get_partition_parser(*types);
++ parser = mtd_part_parser_get(*types);
+ if (!parser && !request_module("%s", *types))
+- parser = get_partition_parser(*types);
++ parser = mtd_part_parser_get(*types);
+ pr_debug("%s: got parser %s\n", master->name,
+ parser ? parser->name : NULL);
+ if (!parser)
+ continue;
+- ret = (*parser->parse_fn)(master, pparts, data);
++ ret = (*parser->parse_fn)(master, &pparts->parts, data);
+ pr_debug("%s: parser %s: %i\n",
+ master->name, parser->name, ret);
+- put_partition_parser(parser);
+ if (ret > 0) {
+ printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
+ ret, parser->name, master->name);
+- return ret;
++ pparts->nr_parts = ret;
++ pparts->parser = parser;
++ return 0;
+ }
++ mtd_part_parser_put(parser);
+ /*
+ * Stash the first error we see; only report it if no parser
+ * succeeds
+@@ -788,6 +831,22 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
+ return err;
+ }
+
++void mtd_part_parser_cleanup(struct mtd_partitions *parts)
++{
++ const struct mtd_part_parser *parser;
++
++ if (!parts)
++ return;
++
++ parser = parts->parser;
++ if (parser) {
++ if (parser->cleanup)
++ parser->cleanup(parts->parts, parts->nr_parts);
++
++ mtd_part_parser_put(parser);
++ }
++}
++
+ int mtd_is_partition(const struct mtd_info *mtd)
+ {
+ struct mtd_part *part;
+@@ -811,6 +870,6 @@ uint64_t mtd_get_device_size(const struct mtd_info *mtd)
+ if (!mtd_is_partition(mtd))
+ return mtd->size;
+
+- return PART(mtd)->master->size;
++ return mtd_to_part(mtd)->master->size;
+ }
+ EXPORT_SYMBOL_GPL(mtd_get_device_size);
+diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
+index fc8b3d1..cb06bdd 100644
+--- a/drivers/mtd/mtdswap.c
++++ b/drivers/mtd/mtdswap.c
+@@ -346,7 +346,7 @@ static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
+ if (mtd_can_have_bb(d->mtd) && mtd_block_isbad(d->mtd, offset))
+ return MTDSWAP_SCANNED_BAD;
+
+- ops.ooblen = 2 * d->mtd->ecclayout->oobavail;
++ ops.ooblen = 2 * d->mtd->oobavail;
+ ops.oobbuf = d->oob_buf;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+@@ -359,7 +359,7 @@ static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
+
+ data = (struct mtdswap_oobdata *)d->oob_buf;
+ data2 = (struct mtdswap_oobdata *)
+- (d->oob_buf + d->mtd->ecclayout->oobavail);
++ (d->oob_buf + d->mtd->oobavail);
+
+ if (le16_to_cpu(data->magic) == MTDSWAP_MAGIC_CLEAN) {
+ eb->erase_count = le32_to_cpu(data->count);
+@@ -933,7 +933,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
+
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.len = mtd->writesize;
+- ops.ooblen = mtd->ecclayout->oobavail;
++ ops.ooblen = mtd->oobavail;
+ ops.ooboffs = 0;
+ ops.datbuf = d->page_buf;
+ ops.oobbuf = d->oob_buf;
+@@ -945,7 +945,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
+ for (i = 0; i < mtd_pages; i++) {
+ patt = mtdswap_test_patt(test + i);
+ memset(d->page_buf, patt, mtd->writesize);
+- memset(d->oob_buf, patt, mtd->ecclayout->oobavail);
++ memset(d->oob_buf, patt, mtd->oobavail);
+ ret = mtd_write_oob(mtd, pos, &ops);
+ if (ret)
+ goto error;
+@@ -964,7 +964,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
+ if (p1[j] != patt)
+ goto error;
+
+- for (j = 0; j < mtd->ecclayout->oobavail; j++)
++ for (j = 0; j < mtd->oobavail; j++)
+ if (p2[j] != (unsigned char)patt)
+ goto error;
+
+@@ -1387,7 +1387,7 @@ static int mtdswap_init(struct mtdswap_dev *d, unsigned int eblocks,
+ if (!d->page_buf)
+ goto page_buf_fail;
+
+- d->oob_buf = kmalloc(2 * mtd->ecclayout->oobavail, GFP_KERNEL);
++ d->oob_buf = kmalloc(2 * mtd->oobavail, GFP_KERNEL);
+ if (!d->oob_buf)
+ goto oob_buf_fail;
+
+@@ -1417,7 +1417,6 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ unsigned long part;
+ unsigned int eblocks, eavailable, bad_blocks, spare_cnt;
+ uint64_t swap_size, use_size, size_limit;
+- struct nand_ecclayout *oinfo;
+ int ret;
+
+ parts = &partitions[0];
+@@ -1447,17 +1446,10 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ return;
+ }
+
+- oinfo = mtd->ecclayout;
+- if (!oinfo) {
+- printk(KERN_ERR "%s: mtd%d does not have OOB\n",
+- MTDSWAP_PREFIX, mtd->index);
+- return;
+- }
+-
+- if (!mtd->oobsize || oinfo->oobavail < MTDSWAP_OOBSIZE) {
++ if (!mtd->oobsize || mtd->oobavail < MTDSWAP_OOBSIZE) {
+ printk(KERN_ERR "%s: Not enough free bytes in OOB, "
+ "%d available, %zu needed.\n",
+- MTDSWAP_PREFIX, oinfo->oobavail, MTDSWAP_OOBSIZE);
++ MTDSWAP_PREFIX, mtd->oobavail, MTDSWAP_OOBSIZE);
+ return;
+ }
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index 2896640..f05e0e9 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -55,7 +55,7 @@ config MTD_NAND_DENALI_PCI
+ config MTD_NAND_DENALI_DT
+ tristate "Support Denali NAND controller as a DT device"
+ select MTD_NAND_DENALI
+- depends on HAS_DMA && HAVE_CLK
++ depends on HAS_DMA && HAVE_CLK && OF
+ help
+ Enable the driver for NAND flash on platforms using a Denali NAND
+ controller as a DT device.
+@@ -74,6 +74,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
+ config MTD_NAND_GPIO
+ tristate "GPIO assisted NAND Flash driver"
+ depends on GPIOLIB || COMPILE_TEST
++ depends on HAS_IOMEM
+ help
+ This enables a NAND flash driver where control signals are
+ connected to GPIO pins, and commands and data are communicated
+@@ -310,6 +311,7 @@ config MTD_NAND_CAFE
+ config MTD_NAND_CS553X
+ tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
+ depends on X86_32
++ depends on !UML && HAS_IOMEM
+ help
+ The CS553x companion chips for the AMD Geode processor
+ include NAND flash controllers with built-in hardware ECC
+@@ -463,6 +465,7 @@ config MTD_NAND_MPC5121_NFC
+ config MTD_NAND_VF610_NFC
+ tristate "Support for Freescale NFC for VF610/MPC5125"
+ depends on (SOC_VF610 || COMPILE_TEST)
++ depends on HAS_IOMEM
+ help
+ Enables support for NAND Flash Controller on some Freescale
+ processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
+@@ -480,7 +483,7 @@ config MTD_NAND_MXC
+
+ config MTD_NAND_SH_FLCTL
+ tristate "Support for NAND on Renesas SuperH FLCTL"
+- depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
++ depends on SUPERH || COMPILE_TEST
+ depends on HAS_IOMEM
+ depends on HAS_DMA
+ help
+@@ -519,6 +522,13 @@ config MTD_NAND_JZ4740
+ help
+ Enables support for NAND Flash on JZ4740 SoC based boards.
+
++config MTD_NAND_JZ4780
++ tristate "Support for NAND on JZ4780 SoC"
++ depends on MACH_JZ4780 && JZ4780_NEMC
++ help
++ Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
++ based boards, using the BCH controller for hardware error correction.
++
+ config MTD_NAND_FSMC
+ tristate "Support for NAND on ST Micros FSMC"
+ depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
+@@ -546,4 +556,11 @@ config MTD_NAND_HISI504
+ help
+ Enables support for NAND controller on Hisilicon SoC Hip04.
+
++config MTD_NAND_QCOM
++ tristate "Support for NAND on QCOM SoCs"
++ depends on ARCH_QCOM
++ help
++ Enables support for NAND flash chips on SoCs containing the EBI2 NAND
++ controller. This controller is found on IPQ806x SoC.
++
+ endif # MTD_NAND
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index 2c7f014..f553353 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -49,11 +49,13 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
+ obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
+ obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
+ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
++obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
+ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
+ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
+ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
+ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
++obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
+
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
+diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
+index ece544e..cf0c3180 100644
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -48,50 +48,6 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/of_mtd.h>
+
+-/* Define default oob placement schemes for large and small page devices */
+-static struct nand_ecclayout nand_oob_8 = {
+- .eccbytes = 3,
+- .eccpos = {0, 1, 2},
+- .oobfree = {
+- {.offset = 3,
+- .length = 2},
+- {.offset = 6,
+- .length = 2} }
+-};
+-
+-static struct nand_ecclayout nand_oob_16 = {
+- .eccbytes = 6,
+- .eccpos = {0, 1, 2, 3, 6, 7},
+- .oobfree = {
+- {.offset = 8,
+- . length = 8} }
+-};
+-
+-static struct nand_ecclayout nand_oob_64 = {
+- .eccbytes = 24,
+- .eccpos = {
+- 40, 41, 42, 43, 44, 45, 46, 47,
+- 48, 49, 50, 51, 52, 53, 54, 55,
+- 56, 57, 58, 59, 60, 61, 62, 63},
+- .oobfree = {
+- {.offset = 2,
+- .length = 38} }
+-};
+-
+-static struct nand_ecclayout nand_oob_128 = {
+- .eccbytes = 48,
+- .eccpos = {
+- 80, 81, 82, 83, 84, 85, 86, 87,
+- 88, 89, 90, 91, 92, 93, 94, 95,
+- 96, 97, 98, 99, 100, 101, 102, 103,
+- 104, 105, 106, 107, 108, 109, 110, 111,
+- 112, 113, 114, 115, 116, 117, 118, 119,
+- 120, 121, 122, 123, 124, 125, 126, 127},
+- .oobfree = {
+- {.offset = 2,
+- .length = 78} }
+-};
+-
+ static int nand_get_device(struct mtd_info *mtd, int new_state);
+
+ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+@@ -103,10 +59,96 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ */
+ DEFINE_LED_TRIGGER(nand_led_trigger);
+
++/* Define default oob placement schemes for large and small page devices */
++static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobregion)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++
++ if (section > 1)
++ return -ERANGE;
++
++ if (!section) {
++ oobregion->offset = 0;
++ oobregion->length = 4;
++ } else {
++ oobregion->offset = 6;
++ oobregion->length = ecc->total - 4;
++ }
++
++ return 0;
++}
++
++static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobregion)
++{
++ if (section > 1)
++ return -ERANGE;
++
++ if (mtd->oobsize == 16) {
++ if (section)
++ return -ERANGE;
++
++ oobregion->length = 8;
++ oobregion->offset = 8;
++ } else {
++ oobregion->length = 2;
++ if (!section)
++ oobregion->offset = 3;
++ else
++ oobregion->offset = 6;
++ }
++
++ return 0;
++}
++
++const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
++ .ecc = nand_ooblayout_ecc_sp,
++ .free = nand_ooblayout_free_sp,
++};
++EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
++
++static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobregion)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++
++ if (section)
++ return -ERANGE;
++
++ oobregion->length = ecc->total;
++ oobregion->offset = mtd->oobsize - oobregion->length;
++
++ return 0;
++}
++
++static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobregion)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++
++ if (section)
++ return -ERANGE;
++
++ oobregion->length = mtd->oobsize - ecc->total - 2;
++ oobregion->offset = 2;
++
++ return 0;
++}
++
++const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
++ .ecc = nand_ooblayout_ecc_lp,
++ .free = nand_ooblayout_free_lp,
++};
++EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
++
+ static int check_offs_len(struct mtd_info *mtd,
+ loff_t ofs, uint64_t len)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+
+ /* Start address must align on block boundary */
+@@ -132,7 +174,7 @@ static int check_offs_len(struct mtd_info *mtd,
+ */
+ static void nand_release_device(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Release the controller and the chip */
+ spin_lock(&chip->controller->lock);
+@@ -150,7 +192,7 @@ static void nand_release_device(struct mtd_info *mtd)
+ */
+ static uint8_t nand_read_byte(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ return readb(chip->IO_ADDR_R);
+ }
+
+@@ -163,7 +205,7 @@ static uint8_t nand_read_byte(struct mtd_info *mtd)
+ */
+ static uint8_t nand_read_byte16(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
+ }
+
+@@ -175,7 +217,7 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd)
+ */
+ static u16 nand_read_word(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ return readw(chip->IO_ADDR_R);
+ }
+
+@@ -188,7 +230,7 @@ static u16 nand_read_word(struct mtd_info *mtd)
+ */
+ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ switch (chipnr) {
+ case -1:
+@@ -211,7 +253,7 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+ */
+ static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ chip->write_buf(mtd, &byte, 1);
+ }
+@@ -225,7 +267,7 @@ static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
+ */
+ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint16_t word = byte;
+
+ /*
+@@ -257,7 +299,7 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
+ */
+ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ iowrite8_rep(chip->IO_ADDR_W, buf, len);
+ }
+@@ -272,7 +314,7 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+ */
+ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ ioread8_rep(chip->IO_ADDR_R, buf, len);
+ }
+@@ -287,7 +329,7 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+ */
+ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+
+ iowrite16_rep(chip->IO_ADDR_W, p, len >> 1);
+@@ -303,7 +345,7 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+ */
+ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+
+ ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
+@@ -313,14 +355,13 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+- * @getchip: 0, if the chip is already selected
+ *
+ * Check, if the block is bad.
+ */
+-static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
++static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+ {
+- int page, chipnr, res = 0, i = 0;
+- struct nand_chip *chip = mtd->priv;
++ int page, res = 0, i = 0;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 bad;
+
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+@@ -328,15 +369,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+- if (getchip) {
+- chipnr = (int)(ofs >> chip->chip_shift);
+-
+- nand_get_device(mtd, FL_READING);
+-
+- /* Select the NAND device */
+- chip->select_chip(mtd, chipnr);
+- }
+-
+ do {
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB,
+@@ -361,11 +393,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+ i++;
+ } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
+
+- if (getchip) {
+- chip->select_chip(mtd, -1);
+- nand_release_device(mtd);
+- }
+-
+ return res;
+ }
+
+@@ -380,7 +407,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+ */
+ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_oob_ops ops;
+ uint8_t buf[2] = { 0, 0 };
+ int ret = 0, res, i = 0;
+@@ -430,7 +457,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+ */
+ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ int res, ret = 0;
+
+ if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
+@@ -471,7 +498,7 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+ */
+ static int nand_check_wp(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Broken xD cards report WP despite being writable */
+ if (chip->options & NAND_BROKEN_XD)
+@@ -491,7 +518,7 @@ static int nand_check_wp(struct mtd_info *mtd)
+ */
+ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!chip->bbt)
+ return 0;
+@@ -503,19 +530,17 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+- * @getchip: 0, if the chip is already selected
+ * @allowbbt: 1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+- int allowbbt)
++static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!chip->bbt)
+- return chip->block_bad(mtd, ofs, getchip);
++ return chip->block_bad(mtd, ofs);
+
+ /* Return info from the table */
+ return nand_isbad_bbt(mtd, ofs, allowbbt);
+@@ -531,7 +556,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+ */
+ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ int i;
+
+ /* Wait for the device to get ready */
+@@ -551,7 +576,7 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
+ */
+ void nand_wait_ready(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ unsigned long timeo = 400;
+
+ if (in_interrupt() || oops_in_progress)
+@@ -566,8 +591,8 @@ void nand_wait_ready(struct mtd_info *mtd)
+ cond_resched();
+ } while (time_before(jiffies, timeo));
+
+- pr_warn_ratelimited(
+- "timeout while waiting for chip to become ready\n");
++ if (!chip->dev_ready(mtd))
++ pr_warn_ratelimited("timeout while waiting for chip to become ready\n");
+ out:
+ led_trigger_event(nand_led_trigger, LED_OFF);
+ }
+@@ -582,7 +607,7 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);
+ */
+ static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
+ {
+- register struct nand_chip *chip = mtd->priv;
++ register struct nand_chip *chip = mtd_to_nand(mtd);
+
+ timeo = jiffies + msecs_to_jiffies(timeo);
+ do {
+@@ -605,7 +630,7 @@ static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
+ static void nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+ {
+- register struct nand_chip *chip = mtd->priv;
++ register struct nand_chip *chip = mtd_to_nand(mtd);
+ int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
+
+ /* Write out the command to the device */
+@@ -708,7 +733,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
+ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+ {
+- register struct nand_chip *chip = mtd->priv;
++ register struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+@@ -832,7 +857,7 @@ static void panic_nand_get_device(struct nand_chip *chip,
+ static int
+ nand_get_device(struct mtd_info *mtd, int new_state)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ spinlock_t *lock = &chip->controller->lock;
+ wait_queue_head_t *wq = &chip->controller->wq;
+ DECLARE_WAITQUEUE(wait, current);
+@@ -952,7 +977,7 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
+ {
+ int ret = 0;
+ int status, page;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Submit address of first page to unlock */
+ page = ofs >> chip->page_shift;
+@@ -987,7 +1012,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+ int ret = 0;
+ int chipnr;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
+ __func__, (unsigned long long)ofs, len);
+@@ -1050,7 +1075,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+ int ret = 0;
+ int chipnr, status, page;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
+ __func__, (unsigned long long)ofs, len);
+@@ -1309,13 +1334,12 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
+ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+ {
+- int i, eccsize = chip->ecc.size;
++ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ unsigned int max_bitflips = 0;
+
+ chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
+@@ -1323,8 +1347,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+- for (i = 0; i < chip->ecc.total; i++)
+- ecc_code[i] = chip->oob_poi[eccpos[i]];
++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
++ chip->ecc.total);
++ if (ret)
++ return ret;
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+@@ -1356,14 +1382,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+ int page)
+ {
+- int start_step, end_step, num_steps;
+- uint32_t *eccpos = chip->ecc.layout->eccpos;
++ int start_step, end_step, num_steps, ret;
+ uint8_t *p;
+ int data_col_addr, i, gaps = 0;
+ int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+ int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+- int index;
++ int index, section = 0;
+ unsigned int max_bitflips = 0;
++ struct mtd_oob_region oobregion = { };
+
+ /* Column address within the page aligned to ECC size (256bytes) */
+ start_step = data_offs / chip->ecc.size;
+@@ -1391,12 +1417,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ * The performance is faster if we position offsets according to
+ * ecc.pos. Let's make sure that there are no gaps in ECC positions.
+ */
+- for (i = 0; i < eccfrag_len - 1; i++) {
+- if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
+- gaps = 1;
+- break;
+- }
+- }
++ ret = mtd_ooblayout_find_eccregion(mtd, index, §ion, &oobregion);
++ if (ret)
++ return ret;
++
++ if (oobregion.length < eccfrag_len)
++ gaps = 1;
++
+ if (gaps) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+@@ -1405,20 +1432,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ * Send the command to read the particular ECC bytes take care
+ * about buswidth alignment in read_buf.
+ */
+- aligned_pos = eccpos[index] & ~(busw - 1);
++ aligned_pos = oobregion.offset & ~(busw - 1);
+ aligned_len = eccfrag_len;
+- if (eccpos[index] & (busw - 1))
++ if (oobregion.offset & (busw - 1))
+ aligned_len++;
+- if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
++ if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
++ (busw - 1))
+ aligned_len++;
+
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+- mtd->writesize + aligned_pos, -1);
++ mtd->writesize + aligned_pos, -1);
+ chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+ }
+
+- for (i = 0; i < eccfrag_len; i++)
+- chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
++ ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode,
++ chip->oob_poi, index, eccfrag_len);
++ if (ret)
++ return ret;
+
+ p = bufpoi + data_col_addr;
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+@@ -1426,6 +1456,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+
+ stat = chip->ecc.correct(mtd, p,
+ &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
++ if (stat == -EBADMSG &&
++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
++ /* check for empty pages with bitflips */
++ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
++ &chip->buffers->ecccode[i],
++ chip->ecc.bytes,
++ NULL, 0,
++ chip->ecc.strength);
++ }
++
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+@@ -1449,13 +1489,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+ {
+- int i, eccsize = chip->ecc.size;
++ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ unsigned int max_bitflips = 0;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+@@ -1465,8 +1504,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ }
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+- for (i = 0; i < chip->ecc.total; i++)
+- ecc_code[i] = chip->oob_poi[eccpos[i]];
++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
++ chip->ecc.total);
++ if (ret)
++ return ret;
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+@@ -1475,6 +1516,15 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
++ if (stat == -EBADMSG &&
++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
++ /* check for empty pages with bitflips */
++ stat = nand_check_erased_ecc_chunk(p, eccsize,
++ &ecc_code[i], eccbytes,
++ NULL, 0,
++ chip->ecc.strength);
++ }
++
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+@@ -1502,12 +1552,11 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+ {
+- int i, eccsize = chip->ecc.size;
++ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ unsigned int max_bitflips = 0;
+
+@@ -1516,8 +1565,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+- for (i = 0; i < chip->ecc.total; i++)
+- ecc_code[i] = chip->oob_poi[eccpos[i]];
++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
++ chip->ecc.total);
++ if (ret)
++ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+@@ -1527,6 +1578,15 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
++ if (stat == -EBADMSG &&
++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
++ /* check for empty pages with bitflips */
++ stat = nand_check_erased_ecc_chunk(p, eccsize,
++ &ecc_code[i], eccbytes,
++ NULL, 0,
++ chip->ecc.strength);
++ }
++
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+@@ -1554,6 +1614,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
++ int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ unsigned int max_bitflips = 0;
+@@ -1573,19 +1634,29 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ chip->read_buf(mtd, oob, eccbytes);
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+- if (stat < 0) {
+- mtd->ecc_stats.failed++;
+- } else {
+- mtd->ecc_stats.corrected += stat;
+- max_bitflips = max_t(unsigned int, max_bitflips, stat);
+- }
+-
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->read_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
++
++ if (stat == -EBADMSG &&
++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
++ /* check for empty pages with bitflips */
++ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
++ oob - eccpadbytes,
++ eccpadbytes,
++ NULL, 0,
++ chip->ecc.strength);
++ }
++
++ if (stat < 0) {
++ mtd->ecc_stats.failed++;
++ } else {
++ mtd->ecc_stats.corrected += stat;
++ max_bitflips = max_t(unsigned int, max_bitflips, stat);
++ }
+ }
+
+ /* Calculate remaining oob bytes */
+@@ -1598,14 +1669,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+
+ /**
+ * nand_transfer_oob - [INTERN] Transfer oob to client buffer
+- * @chip: nand chip structure
++ * @mtd: mtd info structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
+ */
+-static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
++static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
+ struct mtd_oob_ops *ops, size_t len)
+ {
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ int ret;
++
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+@@ -1613,31 +1687,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+ memcpy(oob, chip->oob_poi + ops->ooboffs, len);
+ return oob + len;
+
+- case MTD_OPS_AUTO_OOB: {
+- struct nand_oobfree *free = chip->ecc.layout->oobfree;
+- uint32_t boffs = 0, roffs = ops->ooboffs;
+- size_t bytes = 0;
+-
+- for (; free->length && len; free++, len -= bytes) {
+- /* Read request not from offset 0? */
+- if (unlikely(roffs)) {
+- if (roffs >= free->length) {
+- roffs -= free->length;
+- continue;
+- }
+- boffs = free->offset + roffs;
+- bytes = min_t(size_t, len,
+- (free->length - roffs));
+- roffs = 0;
+- } else {
+- bytes = min_t(size_t, len, free->length);
+- boffs = free->offset;
+- }
+- memcpy(oob, chip->oob_poi + boffs, bytes);
+- oob += bytes;
+- }
+- return oob;
+- }
++ case MTD_OPS_AUTO_OOB:
++ ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
++ ops->ooboffs, len);
++ BUG_ON(ret);
++ return oob + len;
++
+ default:
+ BUG();
+ }
+@@ -1655,7 +1710,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+ */
+ static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("setting READ RETRY mode %d\n", retry_mode);
+
+@@ -1680,12 +1735,11 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+ {
+ int chipnr, page, realpage, col, bytes, aligned, oob_required;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+ uint32_t readlen = ops->len;
+ uint32_t oobreadlen = ops->ooblen;
+- uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
+- mtd->oobavail : mtd->oobsize;
++ uint32_t max_oobsize = mtd_oobavail(mtd, ops);
+
+ uint8_t *bufpoi, *oob, *buf;
+ int use_bufpoi;
+@@ -1772,7 +1826,7 @@ read_retry:
+ int toread = min(oobreadlen, max_oobsize);
+
+ if (toread) {
+- oob = nand_transfer_oob(chip,
++ oob = nand_transfer_oob(mtd,
+ oob, ops, toread);
+ oobreadlen -= toread;
+ }
+@@ -2024,7 +2078,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+ {
+ int page, realpage, chipnr;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_ecc_stats stats;
+ int readlen = ops->ooblen;
+ int len;
+@@ -2036,10 +2090,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+
+ stats = mtd->ecc_stats;
+
+- if (ops->mode == MTD_OPS_AUTO_OOB)
+- len = chip->ecc.layout->oobavail;
+- else
+- len = mtd->oobsize;
++ len = mtd_oobavail(mtd, ops);
+
+ if (unlikely(ops->ooboffs >= len)) {
+ pr_debug("%s: attempt to start read outside oob\n",
+@@ -2073,7 +2124,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+ break;
+
+ len = min(len, readlen);
+- buf = nand_transfer_oob(chip, buf, ops, len);
++ buf = nand_transfer_oob(mtd, buf, ops, len);
+
+ if (chip->options & NAND_NEED_READRDY) {
+ /* Apply delay or wait for ready/busy pin */
+@@ -2232,19 +2283,20 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+ {
+- int i, eccsize = chip->ecc.size;
++ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+- uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ /* Software ECC calculation */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+- for (i = 0; i < chip->ecc.total; i++)
+- chip->oob_poi[eccpos[i]] = ecc_calc[i];
++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
++ chip->ecc.total);
++ if (ret)
++ return ret;
+
+ return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
+ }
+@@ -2261,12 +2313,11 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+ {
+- int i, eccsize = chip->ecc.size;
++ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+- uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+@@ -2274,8 +2325,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+
+- for (i = 0; i < chip->ecc.total; i++)
+- chip->oob_poi[eccpos[i]] = ecc_calc[i];
++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
++ chip->ecc.total);
++ if (ret)
++ return ret;
+
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+@@ -2303,11 +2356,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int ecc_steps = chip->ecc.steps;
+- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t start_step = offset / ecc_size;
+ uint32_t end_step = (offset + data_len - 1) / ecc_size;
+ int oob_bytes = mtd->oobsize / ecc_steps;
+- int step, i;
++ int step, ret;
+
+ for (step = 0; step < ecc_steps; step++) {
+ /* configure controller for WRITE access */
+@@ -2335,8 +2387,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
+ /* copy calculated ECC for whole page to chip->buffer->oob */
+ /* this include masked-value(0xFF) for unwritten subpages */
+ ecc_calc = chip->buffers->ecccalc;
+- for (i = 0; i < chip->ecc.total; i++)
+- chip->oob_poi[eccpos[i]] = ecc_calc[i];
++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
++ chip->ecc.total);
++ if (ret)
++ return ret;
+
+ /* write OOB buffer to NAND device */
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+@@ -2472,7 +2526,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
+ struct mtd_oob_ops *ops)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ int ret;
+
+ /*
+ * Initialise to all 0xFF, to avoid the possibility of left over OOB
+@@ -2487,31 +2542,12 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
+ memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+ return oob + len;
+
+- case MTD_OPS_AUTO_OOB: {
+- struct nand_oobfree *free = chip->ecc.layout->oobfree;
+- uint32_t boffs = 0, woffs = ops->ooboffs;
+- size_t bytes = 0;
+-
+- for (; free->length && len; free++, len -= bytes) {
+- /* Write request not from offset 0? */
+- if (unlikely(woffs)) {
+- if (woffs >= free->length) {
+- woffs -= free->length;
+- continue;
+- }
+- boffs = free->offset + woffs;
+- bytes = min_t(size_t, len,
+- (free->length - woffs));
+- woffs = 0;
+- } else {
+- bytes = min_t(size_t, len, free->length);
+- boffs = free->offset;
+- }
+- memcpy(chip->oob_poi + boffs, oob, bytes);
+- oob += bytes;
+- }
+- return oob;
+- }
++ case MTD_OPS_AUTO_OOB:
++ ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
++ ops->ooboffs, len);
++ BUG_ON(ret);
++ return oob + len;
++
+ default:
+ BUG();
+ }
+@@ -2532,12 +2568,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+ {
+ int chipnr, realpage, page, blockmask, column;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint32_t writelen = ops->len;
+
+ uint32_t oobwritelen = ops->ooblen;
+- uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
+- mtd->oobavail : mtd->oobsize;
++ uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
+
+ uint8_t *oob = ops->oobbuf;
+ uint8_t *buf = ops->datbuf;
+@@ -2662,7 +2697,7 @@ err_out:
+ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const uint8_t *buf)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_oob_ops ops;
+ int ret;
+
+@@ -2722,15 +2757,12 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+ {
+ int chipnr, page, status, len;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("%s: to = 0x%08x, len = %i\n",
+ __func__, (unsigned int)to, (int)ops->ooblen);
+
+- if (ops->mode == MTD_OPS_AUTO_OOB)
+- len = chip->ecc.layout->oobavail;
+- else
+- len = mtd->oobsize;
++ len = mtd_oobavail(mtd, ops);
+
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + ops->ooblen) > len) {
+@@ -2847,7 +2879,7 @@ out:
+ */
+ static int single_erase(struct mtd_info *mtd, int page)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ /* Send commands to erase a block */
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+@@ -2879,7 +2911,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+ int allowbbt)
+ {
+ int page, status, pages_per_block, ret, chipnr;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ loff_t len;
+
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
+@@ -2918,7 +2950,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+ while (len) {
+ /* Check if we have a bad block, we do not erase bad blocks! */
+ if (nand_block_checkbad(mtd, ((loff_t) page) <<
+- chip->page_shift, 0, allowbbt)) {
++ chip->page_shift, allowbbt)) {
+ pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
+ __func__, page);
+ instr->state = MTD_ERASE_FAILED;
+@@ -3005,7 +3037,20 @@ static void nand_sync(struct mtd_info *mtd)
+ */
+ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+ {
+- return nand_block_checkbad(mtd, offs, 1, 0);
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ int chipnr = (int)(offs >> chip->chip_shift);
++ int ret;
++
++ /* Select the NAND device */
++ nand_get_device(mtd, FL_READING);
++ chip->select_chip(mtd, chipnr);
++
++ ret = nand_block_checkbad(mtd, offs, 0);
++
++ chip->select_chip(mtd, -1);
++ nand_release_device(mtd);
++
++ return ret;
+ }
+
+ /**
+@@ -3094,7 +3139,7 @@ static int nand_suspend(struct mtd_info *mtd)
+ */
+ static void nand_resume(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (chip->state == FL_PM_SUSPENDED)
+ nand_release_device(mtd);
+@@ -3266,7 +3311,7 @@ ext_out:
+
+ static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
+
+ return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
+@@ -3937,10 +3982,13 @@ ident_done:
+ return type;
+ }
+
+-static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
+- struct device_node *dn)
++static int nand_dt_init(struct nand_chip *chip)
+ {
+- int ecc_mode, ecc_strength, ecc_step;
++ struct device_node *dn = nand_get_flash_node(chip);
++ int ecc_mode, ecc_algo, ecc_strength, ecc_step;
++
++ if (!dn)
++ return 0;
+
+ if (of_get_nand_bus_width(dn) == 16)
+ chip->options |= NAND_BUSWIDTH_16;
+@@ -3949,6 +3997,7 @@ static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+ ecc_mode = of_get_nand_ecc_mode(dn);
++ ecc_algo = of_get_nand_ecc_algo(dn);
+ ecc_strength = of_get_nand_ecc_strength(dn);
+ ecc_step = of_get_nand_ecc_step_size(dn);
+
+@@ -3961,6 +4010,9 @@ static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
+ if (ecc_mode >= 0)
+ chip->ecc.mode = ecc_mode;
+
++ if (ecc_algo >= 0)
++ chip->ecc.algo = ecc_algo;
++
+ if (ecc_strength >= 0)
+ chip->ecc.strength = ecc_strength;
+
+@@ -3985,15 +4037,16 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
+ struct nand_flash_dev *table)
+ {
+ int i, nand_maf_id, nand_dev_id;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_flash_dev *type;
+ int ret;
+
+- if (chip->flash_node) {
+- ret = nand_dt_init(mtd, chip, chip->flash_node);
+- if (ret)
+- return ret;
+- }
++ ret = nand_dt_init(chip);
++ if (ret)
++ return ret;
++
++ if (!mtd->name && mtd->dev.parent)
++ mtd->name = dev_name(mtd->dev.parent);
+
+ /* Set the default functions */
+ nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
+@@ -4053,7 +4106,7 @@ EXPORT_SYMBOL(nand_scan_ident);
+ */
+ static bool nand_ecc_strength_good(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int corr, ds_corr;
+
+@@ -4081,10 +4134,10 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
+ */
+ int nand_scan_tail(struct mtd_info *mtd)
+ {
+- int i;
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_buffers *nbuf;
++ int ret;
+
+ /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+@@ -4111,19 +4164,15 @@ int nand_scan_tail(struct mtd_info *mtd)
+ /*
+ * If no default placement scheme is given, select an appropriate one.
+ */
+- if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
++ if (!mtd->ooblayout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
+ switch (mtd->oobsize) {
+ case 8:
+- ecc->layout = &nand_oob_8;
+- break;
+ case 16:
+- ecc->layout = &nand_oob_16;
++ mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
+ break;
+ case 64:
+- ecc->layout = &nand_oob_64;
+- break;
+ case 128:
+- ecc->layout = &nand_oob_128;
++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+ break;
+ default:
+ pr_warn("No oob scheme defined for oobsize %d\n",
+@@ -4166,7 +4215,7 @@ int nand_scan_tail(struct mtd_info *mtd)
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->read_subpage)
+ ecc->read_subpage = nand_read_subpage;
+- if (!ecc->write_subpage)
++ if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
+ ecc->write_subpage = nand_write_subpage_hwecc;
+
+ case NAND_ECC_HW_SYNDROME:
+@@ -4244,10 +4293,8 @@ int nand_scan_tail(struct mtd_info *mtd)
+ }
+
+ /* See nand_bch_init() for details. */
+- ecc->bytes = DIV_ROUND_UP(
+- ecc->strength * fls(8 * ecc->size), 8);
+- ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
+- &ecc->layout);
++ ecc->bytes = 0;
++ ecc->priv = nand_bch_init(mtd);
+ if (!ecc->priv) {
+ pr_warn("BCH ECC initialization failed!\n");
+ BUG();
+@@ -4278,20 +4325,9 @@ int nand_scan_tail(struct mtd_info *mtd)
+ if (!ecc->write_oob_raw)
+ ecc->write_oob_raw = ecc->write_oob;
+
+- /*
+- * The number of bytes available for a client to place data into
+- * the out of band area.
+- */
+- ecc->layout->oobavail = 0;
+- for (i = 0; ecc->layout->oobfree[i].length
+- && i < ARRAY_SIZE(ecc->layout->oobfree); i++)
+- ecc->layout->oobavail += ecc->layout->oobfree[i].length;
+- mtd->oobavail = ecc->layout->oobavail;
+-
+- /* ECC sanity check: warn if it's too weak */
+- if (!nand_ecc_strength_good(mtd))
+- pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
+- mtd->name);
++ /* propagate ecc info to mtd_info */
++ mtd->ecc_strength = ecc->strength;
++ mtd->ecc_step_size = ecc->size;
+
+ /*
+ * Set the number of read / write steps for one page depending on ECC
+@@ -4304,6 +4340,21 @@ int nand_scan_tail(struct mtd_info *mtd)
+ }
+ ecc->total = ecc->steps * ecc->bytes;
+
++ /*
++ * The number of bytes available for a client to place data into
++ * the out of band area.
++ */
++ ret = mtd_ooblayout_count_freebytes(mtd);
++ if (ret < 0)
++ ret = 0;
++
++ mtd->oobavail = ret;
++
++ /* ECC sanity check: warn if it's too weak */
++ if (!nand_ecc_strength_good(mtd))
++ pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
++ mtd->name);
++
+ /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
+ switch (ecc->steps) {
+@@ -4360,10 +4411,6 @@ int nand_scan_tail(struct mtd_info *mtd)
+ mtd->_block_markbad = nand_block_markbad;
+ mtd->writebufsize = mtd->writesize;
+
+- /* propagate ecc info to mtd_info */
+- mtd->ecclayout = ecc->layout;
+- mtd->ecc_strength = ecc->strength;
+- mtd->ecc_step_size = ecc->size;
+ /*
+ * Initialize bitflip_threshold to its default prior scan_bbt() call.
+ * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
+@@ -4426,7 +4473,7 @@ EXPORT_SYMBOL(nand_scan);
+ */
+ void nand_release(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
+ nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
+diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
+index b1d4f81..2fbb523 100644
+--- a/drivers/mtd/nand/nand_bbt.c
++++ b/drivers/mtd/nand/nand_bbt.c
+@@ -172,7 +172,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+ struct nand_bbt_descr *td, int offs)
+ {
+ int res, ret = 0, i, j, act = 0;
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ size_t retlen, len, totlen;
+ loff_t from;
+ int bits = td->options & NAND_BBT_NRBITS_MSK;
+@@ -263,7 +263,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+ */
+ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int res = 0, i;
+
+ if (td->options & NAND_BBT_PERCHIP) {
+@@ -388,7 +388,7 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+ static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+
+ /* Read the primary version, if available */
+ if (td->options & NAND_BBT_VERSION) {
+@@ -454,7 +454,7 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *bd, int chip)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, numblocks, numpages;
+ int startblock;
+ loff_t from;
+@@ -523,7 +523,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+ */
+ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, chips;
+ int startblock, block, dir;
+ int scanlen = mtd->writesize + mtd->oobsize;
+@@ -618,7 +618,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md,
+ int chipsel)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ struct erase_info einfo;
+ int i, res, chip = 0;
+ int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+@@ -819,7 +819,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+ */
+ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+
+ return create_bbt(mtd, this->buffers->databuf, bd, -1);
+ }
+@@ -838,7 +838,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
+ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+ {
+ int i, chips, writeops, create, chipsel, res, res2;
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+ struct nand_bbt_descr *rd, *rd2;
+@@ -962,7 +962,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
+ */
+ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, j, chips, block, nrblocks, update;
+ uint8_t oldval;
+
+@@ -1022,7 +1022,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+ */
+ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ u32 pattern_len;
+ u32 bits;
+ u32 table_size;
+@@ -1074,7 +1074,7 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+ */
+ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int len, res;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+@@ -1147,7 +1147,7 @@ err:
+ */
+ static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int len, res = 0;
+ int chip, chipsel;
+ uint8_t *buf;
+@@ -1281,7 +1281,7 @@ static int nand_create_badblock_pattern(struct nand_chip *this)
+ */
+ int nand_default_bbt(struct mtd_info *mtd)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int ret;
+
+ /* Is a flash based bad block table requested? */
+@@ -1317,7 +1317,7 @@ int nand_default_bbt(struct mtd_info *mtd)
+ */
+ int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int block;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+@@ -1332,7 +1332,7 @@ int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
+ */
+ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int block, res;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+@@ -1359,7 +1359,7 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+ */
+ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
+ {
+- struct nand_chip *this = mtd->priv;
++ struct nand_chip *this = mtd_to_nand(mtd);
+ int block, ret = 0;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+@@ -1373,5 +1373,3 @@ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
+
+ return ret;
+ }
+-
+-EXPORT_SYMBOL(nand_scan_bbt);
+diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
+index 3803e0b..ca9b2a4 100644
+--- a/drivers/mtd/nand/nand_bch.c
++++ b/drivers/mtd/nand/nand_bch.c
+@@ -32,13 +32,11 @@
+ /**
+ * struct nand_bch_control - private NAND BCH control structure
+ * @bch: BCH control structure
+- * @ecclayout: private ecc layout for this BCH configuration
+ * @errloc: error location array
+ * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
+ */
+ struct nand_bch_control {
+ struct bch_control *bch;
+- struct nand_ecclayout ecclayout;
+ unsigned int *errloc;
+ unsigned char *eccmask;
+ };
+@@ -52,7 +50,7 @@ struct nand_bch_control {
+ int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+ unsigned char *code)
+ {
+- const struct nand_chip *chip = mtd->priv;
++ const struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int i;
+
+@@ -79,7 +77,7 @@ EXPORT_SYMBOL(nand_bch_calculate_ecc);
+ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+ {
+- const struct nand_chip *chip = mtd->priv;
++ const struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int *errloc = nbc->errloc;
+ int i, count;
+@@ -98,7 +96,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ }
+ } else if (count < 0) {
+ printk(KERN_ERR "ecc unrecoverable error\n");
+- count = -1;
++ count = -EBADMSG;
+ }
+ return count;
+ }
+@@ -107,9 +105,6 @@ EXPORT_SYMBOL(nand_bch_correct_data);
+ /**
+ * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
+ * @mtd: MTD block structure
+- * @eccsize: ecc block size in bytes
+- * @eccbytes: ecc length in bytes
+- * @ecclayout: output default layout
+ *
+ * Returns:
+ * a pointer to a new NAND BCH control structure, or NULL upon failure
+@@ -123,14 +118,20 @@ EXPORT_SYMBOL(nand_bch_correct_data);
+ * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
+ * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
+ */
+-struct nand_bch_control *
+-nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
+- struct nand_ecclayout **ecclayout)
++struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
+ {
++ struct nand_chip *nand = mtd_to_nand(mtd);
+ unsigned int m, t, eccsteps, i;
+- struct nand_ecclayout *layout;
+ struct nand_bch_control *nbc = NULL;
+ unsigned char *erased_page;
++ unsigned int eccsize = nand->ecc.size;
++ unsigned int eccbytes = nand->ecc.bytes;
++ unsigned int eccstrength = nand->ecc.strength;
++
++ if (!eccbytes && eccstrength) {
++ eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
++ nand->ecc.bytes = eccbytes;
++ }
+
+ if (!eccsize || !eccbytes) {
+ printk(KERN_WARNING "ecc parameters not supplied\n");
+@@ -158,7 +159,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
+ eccsteps = mtd->writesize/eccsize;
+
+ /* if no ecc placement scheme was provided, build one */
+- if (!*ecclayout) {
++ if (!mtd->ooblayout) {
+
+ /* handle large page devices only */
+ if (mtd->oobsize < 64) {
+@@ -167,24 +168,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
+ goto fail;
+ }
+
+- layout = &nbc->ecclayout;
+- layout->eccbytes = eccsteps*eccbytes;
+-
+- /* reserve 2 bytes for bad block marker */
+- if (layout->eccbytes+2 > mtd->oobsize) {
+- printk(KERN_WARNING "no suitable oob scheme available "
+- "for oobsize %d eccbytes %u\n", mtd->oobsize,
+- eccbytes);
+- goto fail;
+- }
+- /* put ecc bytes at oob tail */
+- for (i = 0; i < layout->eccbytes; i++)
+- layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
+-
+- layout->oobfree[0].offset = 2;
+- layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
+-
+- *ecclayout = layout;
++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+ }
+
+ /* sanity checks */
+@@ -192,7 +176,8 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
+ printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
+ goto fail;
+ }
+- if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) {
++
++ if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
+ printk(KERN_WARNING "invalid ecc layout\n");
+ goto fail;
+ }
+@@ -216,6 +201,9 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
+ for (i = 0; i < eccbytes; i++)
+ nbc->eccmask[i] ^= 0xff;
+
++ if (!eccstrength)
++ nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
++
+ return nbc;
+ fail:
+ nand_bch_free(nbc);
+diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
+index 97c4c02..d1770b0 100644
+--- a/drivers/mtd/nand/nand_ecc.c
++++ b/drivers/mtd/nand/nand_ecc.c
+@@ -424,7 +424,7 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+ unsigned char *code)
+ {
+ __nand_calculate_ecc(buf,
+- ((struct nand_chip *)mtd->priv)->ecc.size, code);
++ mtd_to_nand(mtd)->ecc.size, code);
+
+ return 0;
+ }
+@@ -507,7 +507,7 @@ int __nand_correct_data(unsigned char *buf,
+ return 1; /* error in ECC data; no action needed */
+
+ pr_err("%s: uncorrectable ECC error\n", __func__);
+- return -1;
++ return -EBADMSG;
+ }
+ EXPORT_SYMBOL(__nand_correct_data);
+
+@@ -524,7 +524,7 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+ {
+ return __nand_correct_data(buf, read_ecc, calc_ecc,
+- ((struct nand_chip *)mtd->priv)->ecc.size);
++ mtd_to_nand(mtd)->ecc.size);
+ }
+ EXPORT_SYMBOL(nand_correct_data);
+
+diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
+index a8804a3..ccc05f5 100644
+--- a/drivers/mtd/nand/nand_ids.c
++++ b/drivers/mtd/nand/nand_ids.c
+@@ -50,8 +50,8 @@ struct nand_flash_dev nand_flash_ids[] = {
+ SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
+ {"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
+ { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
+- SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K),
+- 4 },
++ SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
++ NAND_ECC_INFO(40, SZ_1K), 4 },
+
+ LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
+diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
+index b16d70a..6ff1d8d 100644
+--- a/drivers/mtd/nand/nandsim.c
++++ b/drivers/mtd/nand/nandsim.c
+@@ -666,8 +666,8 @@ static char *get_partition_name(int i)
+ */
+ static int init_nandsim(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = mtd->priv;
+- struct nandsim *ns = chip->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nandsim *ns = nand_get_controller_data(chip);
+ int i, ret = 0;
+ uint64_t remains;
+ uint64_t next_offset;
+@@ -1908,7 +1908,8 @@ static void switch_state(struct nandsim *ns)
+
+ static u_char ns_nand_read_byte(struct mtd_info *mtd)
+ {
+- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nandsim *ns = nand_get_controller_data(chip);
+ u_char outb = 0x00;
+
+ /* Sanity and correctness checks */
+@@ -1969,7 +1970,8 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd)
+
+ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+ {
+- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nandsim *ns = nand_get_controller_data(chip);
+
+ /* Sanity and correctness checks */
+ if (!ns->lines.ce) {
+@@ -2123,7 +2125,8 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+
+ static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
+ {
+- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nandsim *ns = nand_get_controller_data(chip);
+
+ ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
+ ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
+@@ -2141,7 +2144,7 @@ static int ns_device_ready(struct mtd_info *mtd)
+
+ static uint16_t ns_nand_read_word(struct mtd_info *mtd)
+ {
+- struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ NS_DBG("read_word\n");
+
+@@ -2150,7 +2153,8 @@ static uint16_t ns_nand_read_word(struct mtd_info *mtd)
+
+ static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+ {
+- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nandsim *ns = nand_get_controller_data(chip);
+
+ /* Check that chip is expecting data input */
+ if (!(ns->state & STATE_DATAIN_MASK)) {
+@@ -2177,7 +2181,8 @@ static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+
+ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+ {
+- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct nandsim *ns = nand_get_controller_data(chip);
+
+ /* Sanity and correctness checks */
+ if (!ns->lines.ce) {
+@@ -2198,7 +2203,7 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+ int i;
+
+ for (i = 0; i < len; i++)
+- buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd);
++ buf[i] = mtd_to_nand(mtd)->read_byte(mtd);
+
+ return;
+ }
+@@ -2236,16 +2241,15 @@ static int __init ns_init_module(void)
+ }
+
+ /* Allocate and initialize mtd_info, nand_chip and nandsim structures */
+- nsmtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
+- + sizeof(struct nandsim), GFP_KERNEL);
+- if (!nsmtd) {
++ chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim),
++ GFP_KERNEL);
++ if (!chip) {
+ NS_ERR("unable to allocate core structures.\n");
+ return -ENOMEM;
+ }
+- chip = (struct nand_chip *)(nsmtd + 1);
+- nsmtd->priv = (void *)chip;
++ nsmtd = nand_to_mtd(chip);
+ nand = (struct nandsim *)(chip + 1);
+- chip->priv = (void *)nand;
++ nand_set_controller_data(chip, (void *)nand);
+
+ /*
+ * Register simulator's callbacks.
+@@ -2257,6 +2261,7 @@ static int __init ns_init_module(void)
+ chip->read_buf = ns_nand_read_buf;
+ chip->read_word = ns_nand_read_word;
+ chip->ecc.mode = NAND_ECC_SOFT;
++ chip->ecc.algo = NAND_ECC_HAMMING;
+ /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
+ /* and 'badblocks' parameters to work */
+ chip->options |= NAND_SKIP_BBTSCAN;
+@@ -2335,6 +2340,7 @@ static int __init ns_init_module(void)
+ goto error;
+ }
+ chip->ecc.mode = NAND_ECC_SOFT_BCH;
++ chip->ecc.algo = NAND_ECC_BCH;
+ chip->ecc.size = 512;
+ chip->ecc.strength = bch;
+ chip->ecc.bytes = eccbytes;
+@@ -2392,7 +2398,7 @@ err_exit:
+ for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
+ kfree(nand->partitions[i].name);
+ error:
+- kfree(nsmtd);
++ kfree(chip);
+ free_lists();
+
+ return retval;
+@@ -2405,7 +2411,8 @@ module_init(ns_init_module);
+ */
+ static void __exit ns_cleanup_module(void)
+ {
+- struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv;
++ struct nand_chip *chip = mtd_to_nand(nsmtd);
++ struct nandsim *ns = nand_get_controller_data(chip);
+ int i;
+
+ nandsim_debugfs_remove(ns);
+@@ -2413,7 +2420,7 @@ static void __exit ns_cleanup_module(void)
+ nand_release(nsmtd); /* Unregister driver */
+ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
+ kfree(ns->partitions[i].name);
+- kfree(nsmtd); /* Free other structures */
++ kfree(mtd_to_nand(nsmtd)); /* Free other structures */
+ free_lists();
+ }
+
+diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
+index 9ed6038..ede407d 100644
+--- a/drivers/mtd/ofpart.c
++++ b/drivers/mtd/ofpart.c
+@@ -26,9 +26,10 @@ static bool node_has_compatible(struct device_node *pp)
+ }
+
+ static int parse_ofpart_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
++ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+ {
++ struct mtd_partition *parts;
+ struct device_node *mtd_node;
+ struct device_node *ofpart_node;
+ const char *partname;
+@@ -37,10 +38,8 @@ static int parse_ofpart_partitions(struct mtd_info *master,
+ bool dedicated = true;
+
+
+- if (!data)
+- return 0;
+-
+- mtd_node = data->of_node;
++ /* Pull of_node from the master device node */
++ mtd_node = mtd_get_of_node(master);
+ if (!mtd_node)
+ return 0;
+
+@@ -72,8 +71,8 @@ static int parse_ofpart_partitions(struct mtd_info *master,
+ if (nr_parts == 0)
+ return 0;
+
+- *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL);
+- if (!*pparts)
++ parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
++ if (!parts)
+ return -ENOMEM;
+
+ i = 0;
+@@ -107,19 +106,19 @@ static int parse_ofpart_partitions(struct mtd_info *master,
+ goto ofpart_fail;
+ }
+
+- (*pparts)[i].offset = of_read_number(reg, a_cells);
+- (*pparts)[i].size = of_read_number(reg + a_cells, s_cells);
++ parts[i].offset = of_read_number(reg, a_cells);
++ parts[i].size = of_read_number(reg + a_cells, s_cells);
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+- (*pparts)[i].name = partname;
++ parts[i].name = partname;
+
+ if (of_get_property(pp, "read-only", &len))
+- (*pparts)[i].mask_flags |= MTD_WRITEABLE;
++ parts[i].mask_flags |= MTD_WRITEABLE;
+
+ if (of_get_property(pp, "lock", &len))
+- (*pparts)[i].mask_flags |= MTD_POWERUP_LOCK;
++ parts[i].mask_flags |= MTD_POWERUP_LOCK;
+
+ i++;
+ }
+@@ -127,6 +126,7 @@ static int parse_ofpart_partitions(struct mtd_info *master,
+ if (!nr_parts)
+ goto ofpart_none;
+
++ *pparts = parts;
+ return nr_parts;
+
+ ofpart_fail:
+@@ -135,21 +135,20 @@ ofpart_fail:
+ ret = -EINVAL;
+ ofpart_none:
+ of_node_put(pp);
+- kfree(*pparts);
+- *pparts = NULL;
++ kfree(parts);
+ return ret;
+ }
+
+ static struct mtd_part_parser ofpart_parser = {
+- .owner = THIS_MODULE,
+ .parse_fn = parse_ofpart_partitions,
+ .name = "ofpart",
+ };
+
+ static int parse_ofoldpart_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
++ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+ {
++ struct mtd_partition *parts;
+ struct device_node *dp;
+ int i, plen, nr_parts;
+ const struct {
+@@ -157,10 +156,8 @@ static int parse_ofoldpart_partitions(struct mtd_info *master,
+ } *part;
+ const char *names;
+
+- if (!data)
+- return 0;
+-
+- dp = data->of_node;
++ /* Pull of_node from the master device node */
++ dp = mtd_get_of_node(master);
+ if (!dp)
+ return 0;
+
+@@ -173,37 +170,37 @@ static int parse_ofoldpart_partitions(struct mtd_info *master,
+
+ nr_parts = plen / sizeof(part[0]);
+
+- *pparts = kzalloc(nr_parts * sizeof(*(*pparts)), GFP_KERNEL);
+- if (!*pparts)
++ parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
++ if (!parts)
+ return -ENOMEM;
+
+ names = of_get_property(dp, "partition-names", &plen);
+
+ for (i = 0; i < nr_parts; i++) {
+- (*pparts)[i].offset = be32_to_cpu(part->offset);
+- (*pparts)[i].size = be32_to_cpu(part->len) & ~1;
++ parts[i].offset = be32_to_cpu(part->offset);
++ parts[i].size = be32_to_cpu(part->len) & ~1;
+ /* bit 0 set signifies read only partition */
+ if (be32_to_cpu(part->len) & 1)
+- (*pparts)[i].mask_flags = MTD_WRITEABLE;
++ parts[i].mask_flags = MTD_WRITEABLE;
+
+ if (names && (plen > 0)) {
+ int len = strlen(names) + 1;
+
+- (*pparts)[i].name = names;
++ parts[i].name = names;
+ plen -= len;
+ names += len;
+ } else {
+- (*pparts)[i].name = "unnamed";
++ parts[i].name = "unnamed";
+ }
+
+ part++;
+ }
+
++ *pparts = parts;
+ return nr_parts;
+ }
+
+ static struct mtd_part_parser ofoldpart_parser = {
+- .owner = THIS_MODULE,
+ .parse_fn = parse_ofoldpart_partitions,
+ .name = "ofoldpart",
+ };
+diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
+index 2fe2a7e..d42c98e 100644
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -7,6 +7,14 @@ menuconfig MTD_SPI_NOR
+
+ if MTD_SPI_NOR
+
++config MTD_MT81xx_NOR
++ tristate "Mediatek MT81xx SPI NOR flash controller"
++ depends on HAS_IOMEM
++ help
++ This enables access to SPI NOR flash, using MT81xx SPI NOR flash
++ controller. This controller does not support generic SPI BUS, it only
++ supports SPI NOR Flash.
++
+ config MTD_SPI_NOR_USE_4K_SECTORS
+ bool "Use small 4096 B erase sectors"
+ default y
+@@ -23,7 +31,7 @@ config MTD_SPI_NOR_USE_4K_SECTORS
+
+ config SPI_FSL_QUADSPI
+ tristate "Freescale Quad SPI controller"
+- depends on ARCH_MXC || COMPILE_TEST
++ depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ This enables support for the Quad SPI controller in master mode.
+diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
+index e53333e..0bf3a7f8 100644
+--- a/drivers/mtd/spi-nor/Makefile
++++ b/drivers/mtd/spi-nor/Makefile
+@@ -1,3 +1,4 @@
+ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
+ obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
++obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
+ obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
+diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
+new file mode 100644
+index 0000000..8bed1a4c
+--- /dev/null
++++ b/drivers/mtd/spi-nor/mtk-quadspi.c
+@@ -0,0 +1,485 @@
++/*
++ * Copyright (c) 2015 MediaTek Inc.
++ * Author: Bayi Cheng <bayi.cheng@mediatek.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/ioport.h>
++#include <linux/math64.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/spi-nor.h>
++
++#define MTK_NOR_CMD_REG 0x00
++#define MTK_NOR_CNT_REG 0x04
++#define MTK_NOR_RDSR_REG 0x08
++#define MTK_NOR_RDATA_REG 0x0c
++#define MTK_NOR_RADR0_REG 0x10
++#define MTK_NOR_RADR1_REG 0x14
++#define MTK_NOR_RADR2_REG 0x18
++#define MTK_NOR_WDATA_REG 0x1c
++#define MTK_NOR_PRGDATA0_REG 0x20
++#define MTK_NOR_PRGDATA1_REG 0x24
++#define MTK_NOR_PRGDATA2_REG 0x28
++#define MTK_NOR_PRGDATA3_REG 0x2c
++#define MTK_NOR_PRGDATA4_REG 0x30
++#define MTK_NOR_PRGDATA5_REG 0x34
++#define MTK_NOR_SHREG0_REG 0x38
++#define MTK_NOR_SHREG1_REG 0x3c
++#define MTK_NOR_SHREG2_REG 0x40
++#define MTK_NOR_SHREG3_REG 0x44
++#define MTK_NOR_SHREG4_REG 0x48
++#define MTK_NOR_SHREG5_REG 0x4c
++#define MTK_NOR_SHREG6_REG 0x50
++#define MTK_NOR_SHREG7_REG 0x54
++#define MTK_NOR_SHREG8_REG 0x58
++#define MTK_NOR_SHREG9_REG 0x5c
++#define MTK_NOR_CFG1_REG 0x60
++#define MTK_NOR_CFG2_REG 0x64
++#define MTK_NOR_CFG3_REG 0x68
++#define MTK_NOR_STATUS0_REG 0x70
++#define MTK_NOR_STATUS1_REG 0x74
++#define MTK_NOR_STATUS2_REG 0x78
++#define MTK_NOR_STATUS3_REG 0x7c
++#define MTK_NOR_FLHCFG_REG 0x84
++#define MTK_NOR_TIME_REG 0x94
++#define MTK_NOR_PP_DATA_REG 0x98
++#define MTK_NOR_PREBUF_STUS_REG 0x9c
++#define MTK_NOR_DELSEL0_REG 0xa0
++#define MTK_NOR_DELSEL1_REG 0xa4
++#define MTK_NOR_INTRSTUS_REG 0xa8
++#define MTK_NOR_INTREN_REG 0xac
++#define MTK_NOR_CHKSUM_CTL_REG 0xb8
++#define MTK_NOR_CHKSUM_REG 0xbc
++#define MTK_NOR_CMD2_REG 0xc0
++#define MTK_NOR_WRPROT_REG 0xc4
++#define MTK_NOR_RADR3_REG 0xc8
++#define MTK_NOR_DUAL_REG 0xcc
++#define MTK_NOR_DELSEL2_REG 0xd0
++#define MTK_NOR_DELSEL3_REG 0xd4
++#define MTK_NOR_DELSEL4_REG 0xd8
++
++/* commands for mtk nor controller */
++#define MTK_NOR_READ_CMD 0x0
++#define MTK_NOR_RDSR_CMD 0x2
++#define MTK_NOR_PRG_CMD 0x4
++#define MTK_NOR_WR_CMD 0x10
++#define MTK_NOR_PIO_WR_CMD 0x90
++#define MTK_NOR_WRSR_CMD 0x20
++#define MTK_NOR_PIO_READ_CMD 0x81
++#define MTK_NOR_WR_BUF_ENABLE 0x1
++#define MTK_NOR_WR_BUF_DISABLE 0x0
++#define MTK_NOR_ENABLE_SF_CMD 0x30
++#define MTK_NOR_DUAD_ADDR_EN 0x8
++#define MTK_NOR_QUAD_READ_EN 0x4
++#define MTK_NOR_DUAL_ADDR_EN 0x2
++#define MTK_NOR_DUAL_READ_EN 0x1
++#define MTK_NOR_DUAL_DISABLE 0x0
++#define MTK_NOR_FAST_READ 0x1
++
++#define SFLASH_WRBUF_SIZE 128
++
++/* Can shift up to 48 bits (6 bytes) of TX/RX */
++#define MTK_NOR_MAX_RX_TX_SHIFT 6
++/* can shift up to 56 bits (7 bytes) transfer by MTK_NOR_PRG_CMD */
++#define MTK_NOR_MAX_SHIFT 7
++
++/* Helpers for accessing the program data / shift data registers */
++#define MTK_NOR_PRG_REG(n) (MTK_NOR_PRGDATA0_REG + 4 * (n))
++#define MTK_NOR_SHREG(n) (MTK_NOR_SHREG0_REG + 4 * (n))
++
++struct mt8173_nor {
++ struct spi_nor nor;
++ struct device *dev;
++ void __iomem *base; /* nor flash base address */
++ struct clk *spi_clk;
++ struct clk *nor_clk;
++};
++
++static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
++{
++ struct spi_nor *nor = &mt8173_nor->nor;
++
++ switch (nor->flash_read) {
++ case SPI_NOR_FAST:
++ writeb(nor->read_opcode, mt8173_nor->base +
++ MTK_NOR_PRGDATA3_REG);
++ writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
++ MTK_NOR_CFG1_REG);
++ break;
++ case SPI_NOR_DUAL:
++ writeb(nor->read_opcode, mt8173_nor->base +
++ MTK_NOR_PRGDATA3_REG);
++ writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
++ MTK_NOR_DUAL_REG);
++ break;
++ case SPI_NOR_QUAD:
++ writeb(nor->read_opcode, mt8173_nor->base +
++ MTK_NOR_PRGDATA4_REG);
++ writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
++ MTK_NOR_DUAL_REG);
++ break;
++ default:
++ writeb(MTK_NOR_DUAL_DISABLE, mt8173_nor->base +
++ MTK_NOR_DUAL_REG);
++ break;
++ }
++}
++
++static int mt8173_nor_execute_cmd(struct mt8173_nor *mt8173_nor, u8 cmdval)
++{
++ int reg;
++ u8 val = cmdval & 0x1f;
++
++ writeb(cmdval, mt8173_nor->base + MTK_NOR_CMD_REG);
++ return readl_poll_timeout(mt8173_nor->base + MTK_NOR_CMD_REG, reg,
++ !(reg & val), 100, 10000);
++}
++
++static int mt8173_nor_do_tx_rx(struct mt8173_nor *mt8173_nor, u8 op,
++ u8 *tx, int txlen, u8 *rx, int rxlen)
++{
++ int len = 1 + txlen + rxlen;
++ int i, ret, idx;
++
++ if (len > MTK_NOR_MAX_SHIFT)
++ return -EINVAL;
++
++ writeb(len * 8, mt8173_nor->base + MTK_NOR_CNT_REG);
++
++ /* start at PRGDATA5, go down to PRGDATA0 */
++ idx = MTK_NOR_MAX_RX_TX_SHIFT - 1;
++
++ /* opcode */
++ writeb(op, mt8173_nor->base + MTK_NOR_PRG_REG(idx));
++ idx--;
++
++ /* program TX data */
++ for (i = 0; i < txlen; i++, idx--)
++ writeb(tx[i], mt8173_nor->base + MTK_NOR_PRG_REG(idx));
++
++ /* clear out rest of TX registers */
++ while (idx >= 0) {
++ writeb(0, mt8173_nor->base + MTK_NOR_PRG_REG(idx));
++ idx--;
++ }
++
++ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_PRG_CMD);
++ if (ret)
++ return ret;
++
++ /* restart at first RX byte */
++ idx = rxlen - 1;
++
++ /* read out RX data */
++ for (i = 0; i < rxlen; i++, idx--)
++ rx[i] = readb(mt8173_nor->base + MTK_NOR_SHREG(idx));
++
++ return 0;
++}
++
++/* Do a WRSR (Write Status Register) command */
++static int mt8173_nor_wr_sr(struct mt8173_nor *mt8173_nor, u8 sr)
++{
++ writeb(sr, mt8173_nor->base + MTK_NOR_PRGDATA5_REG);
++ writeb(8, mt8173_nor->base + MTK_NOR_CNT_REG);
++ return mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_WRSR_CMD);
++}
++
++static int mt8173_nor_write_buffer_enable(struct mt8173_nor *mt8173_nor)
++{
++ u8 reg;
++
++ /* the bit0 of MTK_NOR_CFG2_REG is pre-fetch buffer
++ * 0: pre-fetch buffer use for read
++ * 1: pre-fetch buffer use for page program
++ */
++ writel(MTK_NOR_WR_BUF_ENABLE, mt8173_nor->base + MTK_NOR_CFG2_REG);
++ return readb_poll_timeout(mt8173_nor->base + MTK_NOR_CFG2_REG, reg,
++ 0x01 == (reg & 0x01), 100, 10000);
++}
++
++static int mt8173_nor_write_buffer_disable(struct mt8173_nor *mt8173_nor)
++{
++ u8 reg;
++
++ writel(MTK_NOR_WR_BUF_DISABLE, mt8173_nor->base + MTK_NOR_CFG2_REG);
++ return readb_poll_timeout(mt8173_nor->base + MTK_NOR_CFG2_REG, reg,
++ MTK_NOR_WR_BUF_DISABLE == (reg & 0x1), 100,
++ 10000);
++}
++
++static void mt8173_nor_set_addr(struct mt8173_nor *mt8173_nor, u32 addr)
++{
++ int i;
++
++ for (i = 0; i < 3; i++) {
++ writeb(addr & 0xff, mt8173_nor->base + MTK_NOR_RADR0_REG + i * 4);
++ addr >>= 8;
++ }
++ /* Last register is non-contiguous */
++ writeb(addr & 0xff, mt8173_nor->base + MTK_NOR_RADR3_REG);
++}
++
++static int mt8173_nor_read(struct spi_nor *nor, loff_t from, size_t length,
++ size_t *retlen, u_char *buffer)
++{
++ int i, ret;
++ int addr = (int)from;
++ u8 *buf = (u8 *)buffer;
++ struct mt8173_nor *mt8173_nor = nor->priv;
++
++ /* set mode for fast read mode ,dual mode or quad mode */
++ mt8173_nor_set_read_mode(mt8173_nor);
++ mt8173_nor_set_addr(mt8173_nor, addr);
++
++ for (i = 0; i < length; i++, (*retlen)++) {
++ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_PIO_READ_CMD);
++ if (ret < 0)
++ return ret;
++ buf[i] = readb(mt8173_nor->base + MTK_NOR_RDATA_REG);
++ }
++ return 0;
++}
++
++static int mt8173_nor_write_single_byte(struct mt8173_nor *mt8173_nor,
++ int addr, int length, u8 *data)
++{
++ int i, ret;
++
++ mt8173_nor_set_addr(mt8173_nor, addr);
++
++ for (i = 0; i < length; i++) {
++ writeb(*data++, mt8173_nor->base + MTK_NOR_WDATA_REG);
++ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_PIO_WR_CMD);
++ if (ret < 0)
++ return ret;
++ }
++ return 0;
++}
++
++static int mt8173_nor_write_buffer(struct mt8173_nor *mt8173_nor, int addr,
++ const u8 *buf)
++{
++ int i, bufidx, data;
++
++ mt8173_nor_set_addr(mt8173_nor, addr);
++
++ bufidx = 0;
++ for (i = 0; i < SFLASH_WRBUF_SIZE; i += 4) {
++ data = buf[bufidx + 3]<<24 | buf[bufidx + 2]<<16 |
++ buf[bufidx + 1]<<8 | buf[bufidx];
++ bufidx += 4;
++ writel(data, mt8173_nor->base + MTK_NOR_PP_DATA_REG);
++ }
++ return mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_WR_CMD);
++}
++
++static void mt8173_nor_write(struct spi_nor *nor, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ int ret;
++ struct mt8173_nor *mt8173_nor = nor->priv;
++
++ ret = mt8173_nor_write_buffer_enable(mt8173_nor);
++ if (ret < 0)
++ dev_warn(mt8173_nor->dev, "write buffer enable failed!\n");
++
++ while (len >= SFLASH_WRBUF_SIZE) {
++ ret = mt8173_nor_write_buffer(mt8173_nor, to, buf);
++ if (ret < 0)
++ dev_err(mt8173_nor->dev, "write buffer failed!\n");
++ len -= SFLASH_WRBUF_SIZE;
++ to += SFLASH_WRBUF_SIZE;
++ buf += SFLASH_WRBUF_SIZE;
++ (*retlen) += SFLASH_WRBUF_SIZE;
++ }
++ ret = mt8173_nor_write_buffer_disable(mt8173_nor);
++ if (ret < 0)
++ dev_warn(mt8173_nor->dev, "write buffer disable failed!\n");
++
++ if (len) {
++ ret = mt8173_nor_write_single_byte(mt8173_nor, to, (int)len,
++ (u8 *)buf);
++ if (ret < 0)
++ dev_err(mt8173_nor->dev, "write single byte failed!\n");
++ (*retlen) += len;
++ }
++}
++
++static int mt8173_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
++{
++ int ret;
++ struct mt8173_nor *mt8173_nor = nor->priv;
++
++ switch (opcode) {
++ case SPINOR_OP_RDSR:
++ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_RDSR_CMD);
++ if (ret < 0)
++ return ret;
++ if (len == 1)
++ *buf = readb(mt8173_nor->base + MTK_NOR_RDSR_REG);
++ else
++ dev_err(mt8173_nor->dev, "len should be 1 for read status!\n");
++ break;
++ default:
++ ret = mt8173_nor_do_tx_rx(mt8173_nor, opcode, NULL, 0, buf, len);
++ break;
++ }
++ return ret;
++}
++
++static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
++ int len)
++{
++ int ret;
++ struct mt8173_nor *mt8173_nor = nor->priv;
++
++ switch (opcode) {
++ case SPINOR_OP_WRSR:
++ /* We only handle 1 byte */
++ ret = mt8173_nor_wr_sr(mt8173_nor, *buf);
++ break;
++ default:
++ ret = mt8173_nor_do_tx_rx(mt8173_nor, opcode, buf, len, NULL, 0);
++ if (ret)
++ dev_warn(mt8173_nor->dev, "write reg failure!\n");
++ break;
++ }
++ return ret;
++}
++
++static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
++ struct device_node *flash_node)
++{
++ int ret;
++ struct spi_nor *nor;
++
++ /* initialize controller to accept commands */
++ writel(MTK_NOR_ENABLE_SF_CMD, mt8173_nor->base + MTK_NOR_WRPROT_REG);
++
++ nor = &mt8173_nor->nor;
++ nor->dev = mt8173_nor->dev;
++ nor->priv = mt8173_nor;
++ spi_nor_set_flash_node(nor, flash_node);
++
++ /* fill the hooks to spi nor */
++ nor->read = mt8173_nor_read;
++ nor->read_reg = mt8173_nor_read_reg;
++ nor->write = mt8173_nor_write;
++ nor->write_reg = mt8173_nor_write_reg;
++ nor->mtd.name = "mtk_nor";
++ /* initialized with NULL */
++ ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
++ if (ret)
++ return ret;
++
++ return mtd_device_register(&nor->mtd, NULL, 0);
++}
++
++static int mtk_nor_drv_probe(struct platform_device *pdev)
++{
++ struct device_node *flash_np;
++ struct resource *res;
++ int ret;
++ struct mt8173_nor *mt8173_nor;
++
++ if (!pdev->dev.of_node) {
++ dev_err(&pdev->dev, "No DT found\n");
++ return -EINVAL;
++ }
++
++ mt8173_nor = devm_kzalloc(&pdev->dev, sizeof(*mt8173_nor), GFP_KERNEL);
++ if (!mt8173_nor)
++ return -ENOMEM;
++ platform_set_drvdata(pdev, mt8173_nor);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ mt8173_nor->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(mt8173_nor->base))
++ return PTR_ERR(mt8173_nor->base);
++
++ mt8173_nor->spi_clk = devm_clk_get(&pdev->dev, "spi");
++ if (IS_ERR(mt8173_nor->spi_clk))
++ return PTR_ERR(mt8173_nor->spi_clk);
++
++ mt8173_nor->nor_clk = devm_clk_get(&pdev->dev, "sf");
++ if (IS_ERR(mt8173_nor->nor_clk))
++ return PTR_ERR(mt8173_nor->nor_clk);
++
++ mt8173_nor->dev = &pdev->dev;
++ ret = clk_prepare_enable(mt8173_nor->spi_clk);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(mt8173_nor->nor_clk);
++ if (ret) {
++ clk_disable_unprepare(mt8173_nor->spi_clk);
++ return ret;
++ }
++ /* only support one attached flash */
++ flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
++ if (!flash_np) {
++ dev_err(&pdev->dev, "no SPI flash device to configure\n");
++ ret = -ENODEV;
++ goto nor_free;
++ }
++ ret = mtk_nor_init(mt8173_nor, flash_np);
++
++nor_free:
++ if (ret) {
++ clk_disable_unprepare(mt8173_nor->spi_clk);
++ clk_disable_unprepare(mt8173_nor->nor_clk);
++ }
++ return ret;
++}
++
++static int mtk_nor_drv_remove(struct platform_device *pdev)
++{
++ struct mt8173_nor *mt8173_nor = platform_get_drvdata(pdev);
++
++ clk_disable_unprepare(mt8173_nor->spi_clk);
++ clk_disable_unprepare(mt8173_nor->nor_clk);
++ return 0;
++}
++
++static const struct of_device_id mtk_nor_of_ids[] = {
++ { .compatible = "mediatek,mt8173-nor"},
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, mtk_nor_of_ids);
++
++static struct platform_driver mtk_nor_driver = {
++ .probe = mtk_nor_drv_probe,
++ .remove = mtk_nor_drv_remove,
++ .driver = {
++ .name = "mtk-nor",
++ .of_match_table = mtk_nor_of_ids,
++ },
++};
++
++module_platform_driver(mtk_nor_driver);
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("MediaTek SPI NOR Flash Driver");
+diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
+index 4988390..157841d 100644
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -38,6 +38,7 @@
+ #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ)
+
+ #define SPI_NOR_MAX_ID_LEN 6
++#define SPI_NOR_MAX_ADDR_WIDTH 4
+
+ struct flash_info {
+ char *name;
+@@ -60,14 +61,20 @@ struct flash_info {
+ u16 addr_width;
+
+ u16 flags;
+-#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
+-#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
+-#define SST_WRITE 0x04 /* use SST byte programming */
+-#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
+-#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
+-#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
+-#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
+-#define USE_FSR 0x80 /* use flag status register */
++#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
++#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
++#define SST_WRITE BIT(2) /* use SST byte programming */
++#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */
++#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */
++#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */
++#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
++#define USE_FSR BIT(7) /* use flag status register */
++#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */
++#define SPI_NOR_HAS_TB BIT(9) /*
++ * Flash SR has Top/Bottom (TB) protect
++ * bit. Must be used with
++ * SPI_NOR_HAS_LOCK.
++ */
+ };
+
+ #define JEDEC_MFR(info) ((info)->id[0])
+@@ -313,6 +320,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
+ }
+
+ /*
++ * Initiate the erasure of a single sector
++ */
++static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
++{
++ u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
++ int i;
++
++ if (nor->erase)
++ return nor->erase(nor, addr);
++
++ /*
++ * Default implementation, if driver doesn't have a specialized HW
++ * control
++ */
++ for (i = nor->addr_width - 1; i >= 0; i--) {
++ buf[i] = addr & 0xff;
++ addr >>= 8;
++ }
++
++ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
++}
++
++/*
+ * Erase an address range on the nor chip. The address range may extend
+ * one or more erase sectors. Return an error is there is a problem erasing.
+ */
+@@ -371,10 +401,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
+ while (len) {
+ write_enable(nor);
+
+- if (nor->erase(nor, addr)) {
+- ret = -EIO;
++ ret = spi_nor_erase_sector(nor, addr);
++ if (ret)
+ goto erase_err;
+- }
+
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
+@@ -387,17 +416,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
+
+ write_disable(nor);
+
++erase_err:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
+
+- instr->state = MTD_ERASE_DONE;
++ instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return ret;
+-
+-erase_err:
+- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
+- instr->state = MTD_ERASE_FAILED;
+- return ret;
+ }
+
+ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+@@ -415,32 +440,58 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+ } else {
+ pow = ((sr & mask) ^ mask) >> shift;
+ *len = mtd->size >> pow;
+- *ofs = mtd->size - *len;
++ if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)
++ *ofs = 0;
++ else
++ *ofs = mtd->size - *len;
+ }
+ }
+
+ /*
+- * Return 1 if the entire region is locked, 0 otherwise
++ * Return 1 if the entire region is locked (if @locked is true) or unlocked (if
++ * @locked is false); 0 otherwise
+ */
+-static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+- u8 sr)
++static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
++ u8 sr, bool locked)
+ {
+ loff_t lock_offs;
+ uint64_t lock_len;
+
++ if (!len)
++ return 1;
++
+ stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
+
+- return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
++ if (locked)
++ /* Requested range is a sub-range of locked range */
++ return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
++ else
++ /* Requested range does not overlap with locked range */
++ return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
++}
++
++static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
++ u8 sr)
++{
++ return stm_check_lock_status_sr(nor, ofs, len, sr, true);
++}
++
++static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
++ u8 sr)
++{
++ return stm_check_lock_status_sr(nor, ofs, len, sr, false);
+ }
+
+ /*
+ * Lock a region of the flash. Compatible with ST Micro and similar flash.
+- * Supports only the block protection bits BP{0,1,2} in the status register
++ * Supports the block protection bits BP{0,1,2} in the status register
+ * (SR). Does not support these features found in newer SR bitfields:
+- * - TB: top/bottom protect - only handle TB=0 (top protect)
+ * - SEC: sector/block protect - only handle SEC=0 (block protect)
+ * - CMP: complement protect - only support CMP=0 (range is not complemented)
+ *
++ * Support for the following is provided conditionally for some flash:
++ * - TB: top/bottom protect
++ *
+ * Sample table portion for 8MB flash (Winbond w25q64fw):
+ *
+ * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion
+@@ -453,26 +504,55 @@ static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+ * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4
+ * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2
+ * X | X | 1 | 1 | 1 | 8 MB | ALL
++ * ------|-------|-------|-------|-------|---------------|-------------------
++ * 0 | 1 | 0 | 0 | 1 | 128 KB | Lower 1/64
++ * 0 | 1 | 0 | 1 | 0 | 256 KB | Lower 1/32
++ * 0 | 1 | 0 | 1 | 1 | 512 KB | Lower 1/16
++ * 0 | 1 | 1 | 0 | 0 | 1 MB | Lower 1/8
++ * 0 | 1 | 1 | 0 | 1 | 2 MB | Lower 1/4
++ * 0 | 1 | 1 | 1 | 0 | 4 MB | Lower 1/2
+ *
+ * Returns negative on errors, 0 on success.
+ */
+ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+ {
+ struct mtd_info *mtd = &nor->mtd;
+- u8 status_old, status_new;
++ int status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
++ loff_t lock_len;
++ bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
++ bool use_top;
++ int ret;
+
+ status_old = read_sr(nor);
++ if (status_old < 0)
++ return status_old;
+
+- /* SPI NOR always locks to the end */
+- if (ofs + len != mtd->size) {
+- /* Does combined region extend to end? */
+- if (!stm_is_locked_sr(nor, ofs + len, mtd->size - ofs - len,
+- status_old))
+- return -EINVAL;
+- len = mtd->size - ofs;
+- }
++ /* If nothing in our range is unlocked, we don't need to do anything */
++ if (stm_is_locked_sr(nor, ofs, len, status_old))
++ return 0;
++
++ /* If anything below us is unlocked, we can't use 'bottom' protection */
++ if (!stm_is_locked_sr(nor, 0, ofs, status_old))
++ can_be_bottom = false;
++
++ /* If anything above us is unlocked, we can't use 'top' protection */
++ if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),
++ status_old))
++ can_be_top = false;
++
++ if (!can_be_bottom && !can_be_top)
++ return -EINVAL;
++
++ /* Prefer top, if both are valid */
++ use_top = can_be_top;
++
++ /* lock_len: length of region that should end up locked */
++ if (use_top)
++ lock_len = mtd->size - ofs;
++ else
++ lock_len = ofs + len;
+
+ /*
+ * Need smallest pow such that:
+@@ -483,7 +563,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+ *
+ * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
+ */
+- pow = ilog2(mtd->size) - ilog2(len);
++ pow = ilog2(mtd->size) - ilog2(lock_len);
+ val = mask - (pow << shift);
+ if (val & ~mask)
+ return -EINVAL;
+@@ -491,14 +571,27 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+ if (!(val & mask))
+ return -EINVAL;
+
+- status_new = (status_old & ~mask) | val;
++ status_new = (status_old & ~mask & ~SR_TB) | val;
++
++ /* Disallow further writes if WP pin is asserted */
++ status_new |= SR_SRWD;
++
++ if (!use_top)
++ status_new |= SR_TB;
++
++ /* Don't bother if they're the same */
++ if (status_new == status_old)
++ return 0;
+
+ /* Only modify protection if it will not unlock other areas */
+- if ((status_new & mask) <= (status_old & mask))
++ if ((status_new & mask) < (status_old & mask))
+ return -EINVAL;
+
+ write_enable(nor);
+- return write_sr(nor, status_new);
++ ret = write_sr(nor, status_new);
++ if (ret)
++ return ret;
++ return spi_nor_wait_till_ready(nor);
+ }
+
+ /*
+@@ -509,17 +602,43 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+ {
+ struct mtd_info *mtd = &nor->mtd;
+- uint8_t status_old, status_new;
++ int status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
++ loff_t lock_len;
++ bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
++ bool use_top;
++ int ret;
+
+ status_old = read_sr(nor);
++ if (status_old < 0)
++ return status_old;
+
+- /* Cannot unlock; would unlock larger region than requested */
+- if (stm_is_locked_sr(nor, status_old, ofs - mtd->erasesize,
+- mtd->erasesize))
++ /* If nothing in our range is locked, we don't need to do anything */
++ if (stm_is_unlocked_sr(nor, ofs, len, status_old))
++ return 0;
++
++ /* If anything below us is locked, we can't use 'top' protection */
++ if (!stm_is_unlocked_sr(nor, 0, ofs, status_old))
++ can_be_top = false;
++
++ /* If anything above us is locked, we can't use 'bottom' protection */
++ if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),
++ status_old))
++ can_be_bottom = false;
++
++ if (!can_be_bottom && !can_be_top)
+ return -EINVAL;
+
++ /* Prefer top, if both are valid */
++ use_top = can_be_top;
++
++ /* lock_len: length of region that should remain locked */
++ if (use_top)
++ lock_len = mtd->size - (ofs + len);
++ else
++ lock_len = ofs;
++
+ /*
+ * Need largest pow such that:
+ *
+@@ -529,8 +648,8 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+ *
+ * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
+ */
+- pow = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len));
+- if (ofs + len == mtd->size) {
++ pow = ilog2(mtd->size) - order_base_2(lock_len);
++ if (lock_len == 0) {
+ val = 0; /* fully unlocked */
+ } else {
+ val = mask - (pow << shift);
+@@ -539,14 +658,28 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+ return -EINVAL;
+ }
+
+- status_new = (status_old & ~mask) | val;
++ status_new = (status_old & ~mask & ~SR_TB) | val;
++
++ /* Don't protect status register if we're fully unlocked */
++ if (lock_len == mtd->size)
++ status_new &= ~SR_SRWD;
++
++ if (!use_top)
++ status_new |= SR_TB;
++
++ /* Don't bother if they're the same */
++ if (status_new == status_old)
++ return 0;
+
+ /* Only modify protection if it will not lock other areas */
+- if ((status_new & mask) >= (status_old & mask))
++ if ((status_new & mask) > (status_old & mask))
+ return -EINVAL;
+
+ write_enable(nor);
+- return write_sr(nor, status_new);
++ ret = write_sr(nor, status_new);
++ if (ret)
++ return ret;
++ return spi_nor_wait_till_ready(nor);
+ }
+
+ /*
+@@ -715,9 +848,9 @@ static const struct flash_info spi_nor_ids[] = {
+ { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
+ { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
+ { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
+- { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) },
++ { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
+ { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
+- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) },
++ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
+ { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
+ { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
+ { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
+@@ -731,8 +864,8 @@ static const struct flash_info spi_nor_ids[] = {
+ { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
+ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
+- { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
+- { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
++ { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
++ { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+ { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+@@ -766,6 +899,7 @@ static const struct flash_info spi_nor_ids[] = {
+ { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
++ { "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
+ { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
+ { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) },
+@@ -829,11 +963,23 @@ static const struct flash_info spi_nor_ids[] = {
+ { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
+ { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
+ { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
+- { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++ {
++ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++ },
+ { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
+ { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
+- { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+- { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++ {
++ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++ },
++ {
++ "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++ },
+ { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
+ { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
+ { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
+@@ -856,7 +1002,7 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+
+ tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
+ if (tmp < 0) {
+- dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
++ dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp);
+ return ERR_PTR(tmp);
+ }
+
+@@ -867,7 +1013,7 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+ return &spi_nor_ids[tmp];
+ }
+ }
+- dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
++ dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n",
+ id[0], id[1], id[2]);
+ return ERR_PTR(-ENODEV);
+ }
+@@ -1013,6 +1159,8 @@ static int macronix_quad_enable(struct spi_nor *nor)
+ int ret, val;
+
+ val = read_sr(nor);
++ if (val < 0)
++ return val;
+ write_enable(nor);
+
+ write_sr(nor, val | SR_QUAD_EN_MX);
+@@ -1067,45 +1215,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
+ return 0;
+ }
+
+-static int micron_quad_enable(struct spi_nor *nor)
+-{
+- int ret;
+- u8 val;
+-
+- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
+- if (ret < 0) {
+- dev_err(nor->dev, "error %d reading EVCR\n", ret);
+- return ret;
+- }
+-
+- write_enable(nor);
+-
+- /* set EVCR, enable quad I/O */
+- nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
+- ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1);
+- if (ret < 0) {
+- dev_err(nor->dev, "error while writing EVCR register\n");
+- return ret;
+- }
+-
+- ret = spi_nor_wait_till_ready(nor);
+- if (ret)
+- return ret;
+-
+- /* read EVCR and check it */
+- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
+- if (ret < 0) {
+- dev_err(nor->dev, "error %d reading EVCR\n", ret);
+- return ret;
+- }
+- if (val & EVCR_QUAD_EN_MICRON) {
+- dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
+- return -EINVAL;
+- }
+-
+- return 0;
+-}
+-
+ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
+ {
+ int status;
+@@ -1119,12 +1228,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
+ }
+ return status;
+ case SNOR_MFR_MICRON:
+- status = micron_quad_enable(nor);
+- if (status) {
+- dev_err(nor->dev, "Micron quad-read not enabled\n");
+- return -EINVAL;
+- }
+- return status;
++ return 0;
+ default:
+ status = spansion_quad_enable(nor);
+ if (status) {
+@@ -1138,7 +1242,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
+ static int spi_nor_check(struct spi_nor *nor)
+ {
+ if (!nor->dev || !nor->read || !nor->write ||
+- !nor->read_reg || !nor->write_reg || !nor->erase) {
++ !nor->read_reg || !nor->write_reg) {
+ pr_err("spi-nor: please fill all the necessary fields!\n");
+ return -EINVAL;
+ }
+@@ -1151,7 +1255,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+ const struct flash_info *info = NULL;
+ struct device *dev = nor->dev;
+ struct mtd_info *mtd = &nor->mtd;
+- struct device_node *np = nor->flash_node;
++ struct device_node *np = spi_nor_get_flash_node(nor);
+ int ret;
+ int i;
+
+@@ -1201,9 +1305,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+ if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
+ JEDEC_MFR(info) == SNOR_MFR_INTEL ||
+ JEDEC_MFR(info) == SNOR_MFR_SST ||
+- JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
++ info->flags & SPI_NOR_HAS_LOCK) {
+ write_enable(nor);
+ write_sr(nor, 0);
++ spi_nor_wait_till_ready(nor);
+ }
+
+ if (!mtd->name)
+@@ -1218,7 +1323,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+
+ /* NOR protection support for STmicro/Micron chips and similar */
+ if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
+- JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
++ info->flags & SPI_NOR_HAS_LOCK) {
+ nor->flash_lock = stm_lock;
+ nor->flash_unlock = stm_unlock;
+ nor->flash_is_locked = stm_is_locked;
+@@ -1238,6 +1343,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+
+ if (info->flags & USE_FSR)
+ nor->flags |= SNOR_F_USE_FSR;
++ if (info->flags & SPI_NOR_HAS_TB)
++ nor->flags |= SNOR_F_HAS_SR_TB;
+
+ #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ /* prefer "small sector" erase if possible */
+@@ -1340,6 +1447,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+ nor->addr_width = 3;
+ }
+
++ if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
++ dev_err(dev, "address width is too large: %u\n",
++ nor->addr_width);
++ return -EINVAL;
++ }
++
+ nor->read_dummy = spi_nor_read_dummy_cycles(nor);
+
+ dev_info(dev, "%s (%lld Kbytes)\n", info->name,
+diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c
+index 7931615..88b6c81 100644
+--- a/drivers/mtd/tests/mtd_nandecctest.c
++++ b/drivers/mtd/tests/mtd_nandecctest.c
+@@ -187,7 +187,7 @@ static int double_bit_error_detect(void *error_data, void *error_ecc,
+ __nand_calculate_ecc(error_data, size, calc_ecc);
+ ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size);
+
+- return (ret == -1) ? 0 : -EINVAL;
++ return (ret == -EBADMSG) ? 0 : -EINVAL;
+ }
+
+ static const struct nand_ecc_test nand_ecc_test[] = {
+diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c
+index 3176212..1cb3f77 100644
+--- a/drivers/mtd/tests/oobtest.c
++++ b/drivers/mtd/tests/oobtest.c
+@@ -215,19 +215,19 @@ static int verify_eraseblock(int ebnum)
+ pr_info("ignoring error as within bitflip_limit\n");
+ }
+
+- if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
++ if (use_offset != 0 || use_len < mtd->oobavail) {
+ int k;
+
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.len = 0;
+ ops.retlen = 0;
+- ops.ooblen = mtd->ecclayout->oobavail;
++ ops.ooblen = mtd->oobavail;
+ ops.oobretlen = 0;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+ ops.oobbuf = readbuf;
+ err = mtd_read_oob(mtd, addr, &ops);
+- if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
++ if (err || ops.oobretlen != mtd->oobavail) {
+ pr_err("error: readoob failed at %#llx\n",
+ (long long)addr);
+ errcnt += 1;
+@@ -244,7 +244,7 @@ static int verify_eraseblock(int ebnum)
+ /* verify post-(use_offset + use_len) area for 0xff */
+ k = use_offset + use_len;
+ bitflips += memffshow(addr, k, readbuf + k,
+- mtd->ecclayout->oobavail - k);
++ mtd->oobavail - k);
+
+ if (bitflips > bitflip_limit) {
+ pr_err("error: verify failed at %#llx\n",
+@@ -269,8 +269,8 @@ static int verify_eraseblock_in_one_go(int ebnum)
+ struct mtd_oob_ops ops;
+ int err = 0;
+ loff_t addr = (loff_t)ebnum * mtd->erasesize;
+- size_t len = mtd->ecclayout->oobavail * pgcnt;
+- size_t oobavail = mtd->ecclayout->oobavail;
++ size_t len = mtd->oobavail * pgcnt;
++ size_t oobavail = mtd->oobavail;
+ size_t bitflips;
+ int i;
+
+@@ -394,8 +394,8 @@ static int __init mtd_oobtest_init(void)
+ goto out;
+
+ use_offset = 0;
+- use_len = mtd->ecclayout->oobavail;
+- use_len_max = mtd->ecclayout->oobavail;
++ use_len = mtd->oobavail;
++ use_len_max = mtd->oobavail;
+ vary_offset = 0;
+
+ /* First test: write all OOB, read it back and verify */
+@@ -460,8 +460,8 @@ static int __init mtd_oobtest_init(void)
+
+ /* Write all eraseblocks */
+ use_offset = 0;
+- use_len = mtd->ecclayout->oobavail;
+- use_len_max = mtd->ecclayout->oobavail;
++ use_len = mtd->oobavail;
++ use_len_max = mtd->oobavail;
+ vary_offset = 1;
+ prandom_seed_state(&rnd_state, 5);
+
+@@ -471,8 +471,8 @@ static int __init mtd_oobtest_init(void)
+
+ /* Check all eraseblocks */
+ use_offset = 0;
+- use_len = mtd->ecclayout->oobavail;
+- use_len_max = mtd->ecclayout->oobavail;
++ use_len = mtd->oobavail;
++ use_len_max = mtd->oobavail;
+ vary_offset = 1;
+ prandom_seed_state(&rnd_state, 5);
+ err = verify_all_eraseblocks();
+@@ -480,8 +480,8 @@ static int __init mtd_oobtest_init(void)
+ goto out;
+
+ use_offset = 0;
+- use_len = mtd->ecclayout->oobavail;
+- use_len_max = mtd->ecclayout->oobavail;
++ use_len = mtd->oobavail;
++ use_len_max = mtd->oobavail;
+ vary_offset = 0;
+
+ /* Fourth test: try to write off end of device */
+@@ -501,7 +501,7 @@ static int __init mtd_oobtest_init(void)
+ ops.retlen = 0;
+ ops.ooblen = 1;
+ ops.oobretlen = 0;
+- ops.ooboffs = mtd->ecclayout->oobavail;
++ ops.ooboffs = mtd->oobavail;
+ ops.datbuf = NULL;
+ ops.oobbuf = writebuf;
+ pr_info("attempting to start write past end of OOB\n");
+@@ -521,7 +521,7 @@ static int __init mtd_oobtest_init(void)
+ ops.retlen = 0;
+ ops.ooblen = 1;
+ ops.oobretlen = 0;
+- ops.ooboffs = mtd->ecclayout->oobavail;
++ ops.ooboffs = mtd->oobavail;
+ ops.datbuf = NULL;
+ ops.oobbuf = readbuf;
+ pr_info("attempting to start read past end of OOB\n");
+@@ -543,7 +543,7 @@ static int __init mtd_oobtest_init(void)
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.len = 0;
+ ops.retlen = 0;
+- ops.ooblen = mtd->ecclayout->oobavail + 1;
++ ops.ooblen = mtd->oobavail + 1;
+ ops.oobretlen = 0;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+@@ -563,7 +563,7 @@ static int __init mtd_oobtest_init(void)
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.len = 0;
+ ops.retlen = 0;
+- ops.ooblen = mtd->ecclayout->oobavail + 1;
++ ops.ooblen = mtd->oobavail + 1;
+ ops.oobretlen = 0;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+@@ -587,7 +587,7 @@ static int __init mtd_oobtest_init(void)
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.len = 0;
+ ops.retlen = 0;
+- ops.ooblen = mtd->ecclayout->oobavail;
++ ops.ooblen = mtd->oobavail;
+ ops.oobretlen = 0;
+ ops.ooboffs = 1;
+ ops.datbuf = NULL;
+@@ -607,7 +607,7 @@ static int __init mtd_oobtest_init(void)
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.len = 0;
+ ops.retlen = 0;
+- ops.ooblen = mtd->ecclayout->oobavail;
++ ops.ooblen = mtd->oobavail;
+ ops.oobretlen = 0;
+ ops.ooboffs = 1;
+ ops.datbuf = NULL;
+@@ -638,7 +638,7 @@ static int __init mtd_oobtest_init(void)
+ for (i = 0; i < ebcnt - 1; ++i) {
+ int cnt = 2;
+ int pg;
+- size_t sz = mtd->ecclayout->oobavail;
++ size_t sz = mtd->oobavail;
+ if (bbt[i] || bbt[i + 1])
+ continue;
+ addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
+@@ -673,13 +673,12 @@ static int __init mtd_oobtest_init(void)
+ for (i = 0; i < ebcnt - 1; ++i) {
+ if (bbt[i] || bbt[i + 1])
+ continue;
+- prandom_bytes_state(&rnd_state, writebuf,
+- mtd->ecclayout->oobavail * 2);
++ prandom_bytes_state(&rnd_state, writebuf, mtd->oobavail * 2);
+ addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.len = 0;
+ ops.retlen = 0;
+- ops.ooblen = mtd->ecclayout->oobavail * 2;
++ ops.ooblen = mtd->oobavail * 2;
+ ops.oobretlen = 0;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+@@ -688,7 +687,7 @@ static int __init mtd_oobtest_init(void)
+ if (err)
+ goto out;
+ if (memcmpshow(addr, readbuf, writebuf,
+- mtd->ecclayout->oobavail * 2)) {
++ mtd->oobavail * 2)) {
+ pr_err("error: verify failed at %#llx\n",
+ (long long)addr);
+ errcnt += 1;
+diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c
+index ba1890d..ff1e056 100644
+--- a/drivers/mtd/tests/pagetest.c
++++ b/drivers/mtd/tests/pagetest.c
+@@ -127,13 +127,12 @@ static int crosstest(void)
+ unsigned char *pp1, *pp2, *pp3, *pp4;
+
+ pr_info("crosstest\n");
+- pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
++ pp1 = kzalloc(pgsize * 4, GFP_KERNEL);
+ if (!pp1)
+ return -ENOMEM;
+ pp2 = pp1 + pgsize;
+ pp3 = pp2 + pgsize;
+ pp4 = pp3 + pgsize;
+- memset(pp1, 0, pgsize * 4);
+
+ addr0 = 0;
+ for (i = 0; i < ebcnt && bbt[i]; ++i)
+diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
+index 54e056d..ee2b74d 100644
+--- a/drivers/mtd/ubi/cdev.c
++++ b/drivers/mtd/ubi/cdev.c
+@@ -174,9 +174,9 @@ static int vol_cdev_fsync(struct file *file, loff_t start, loff_t end,
+ struct ubi_device *ubi = desc->vol->ubi;
+ struct inode *inode = file_inode(file);
+ int err;
+- mutex_lock(&inode->i_mutex);
++ inode_lock(inode);
+ err = ubi_sync(ubi->ubi_num);
+- mutex_unlock(&inode->i_mutex);
++ inode_unlock(inode);
+ return err;
+ }
+
+diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
+index 2a45ac2..989036c 100644
+--- a/drivers/mtd/ubi/misc.c
++++ b/drivers/mtd/ubi/misc.c
+@@ -153,3 +153,52 @@ int ubi_check_pattern(const void *buf, uint8_t patt, int size)
+ return 0;
+ return 1;
+ }
++
++/* Normal UBI messages */
++void ubi_msg(const struct ubi_device *ubi, const char *fmt, ...)
++{
++ struct va_format vaf;
++ va_list args;
++
++ va_start(args, fmt);
++
++ vaf.fmt = fmt;
++ vaf.va = &args;
++
++ pr_notice(UBI_NAME_STR "%d: %pV\n", ubi->ubi_num, &vaf);
++
++ va_end(args);
++}
++
++/* UBI warning messages */
++void ubi_warn(const struct ubi_device *ubi, const char *fmt, ...)
++{
++ struct va_format vaf;
++ va_list args;
++
++ va_start(args, fmt);
++
++ vaf.fmt = fmt;
++ vaf.va = &args;
++
++ pr_warn(UBI_NAME_STR "%d warning: %ps: %pV\n",
++ ubi->ubi_num, __builtin_return_address(0), &vaf);
++
++ va_end(args);
++}
++
++/* UBI error messages */
++void ubi_err(const struct ubi_device *ubi, const char *fmt, ...)
++{
++ struct va_format vaf;
++ va_list args;
++
++ va_start(args, fmt);
++
++ vaf.fmt = fmt;
++ vaf.va = &args;
++
++ pr_err(UBI_NAME_STR "%d error: %ps: %pV\n",
++ ubi->ubi_num, __builtin_return_address(0), &vaf);
++ va_end(args);
++}
+diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
+index 2974b67..dadc6a9 100644
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -49,15 +49,19 @@
+ /* UBI name used for character devices, sysfs, etc */
+ #define UBI_NAME_STR "ubi"
+
++struct ubi_device;
++
+ /* Normal UBI messages */
+-#define ubi_msg(ubi, fmt, ...) pr_notice(UBI_NAME_STR "%d: " fmt "\n", \
+- ubi->ubi_num, ##__VA_ARGS__)
++__printf(2, 3)
++void ubi_msg(const struct ubi_device *ubi, const char *fmt, ...);
++
+ /* UBI warning messages */
+-#define ubi_warn(ubi, fmt, ...) pr_warn(UBI_NAME_STR "%d warning: %s: " fmt "\n", \
+- ubi->ubi_num, __func__, ##__VA_ARGS__)
++__printf(2, 3)
++void ubi_warn(const struct ubi_device *ubi, const char *fmt, ...);
++
+ /* UBI error messages */
+-#define ubi_err(ubi, fmt, ...) pr_err(UBI_NAME_STR "%d error: %s: " fmt "\n", \
+- ubi->ubi_num, __func__, ##__VA_ARGS__)
++__printf(2, 3)
++void ubi_err(const struct ubi_device *ubi, const char *fmt, ...);
+
+ /* Background thread name pattern */
+ #define UBI_BGT_NAME_PATTERN "ubi_bgt%dd"
+diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
+index 2a1b6e0..0134ba3 100644
+--- a/drivers/mtd/ubi/upd.c
++++ b/drivers/mtd/ubi/upd.c
+@@ -193,7 +193,7 @@ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
+ vol->changing_leb = 1;
+ vol->ch_lnum = req->lnum;
+
+- vol->upd_buf = vmalloc(req->bytes);
++ vol->upd_buf = vmalloc(ALIGN((int)req->bytes, ubi->min_io_size));
+ if (!vol->upd_buf)
+ return -ENOMEM;
+
+diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
+index 5606563..17ec948 100644
+--- a/drivers/mtd/ubi/wl.c
++++ b/drivers/mtd/ubi/wl.c
+@@ -628,6 +628,7 @@ static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
+ return __erase_worker(ubi, &wl_wrk);
+ }
+
++static int ensure_wear_leveling(struct ubi_device *ubi, int nested);
+ /**
+ * wear_leveling_worker - wear-leveling worker function.
+ * @ubi: UBI device description object
+@@ -649,6 +650,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
+ #endif
+ struct ubi_wl_entry *e1, *e2;
+ struct ubi_vid_hdr *vid_hdr;
++ int dst_leb_clean = 0;
+
+ kfree(wrk);
+ if (shutdown)
+@@ -753,6 +755,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
+
+ err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0);
+ if (err && err != UBI_IO_BITFLIPS) {
++ dst_leb_clean = 1;
+ if (err == UBI_IO_FF) {
+ /*
+ * We are trying to move PEB without a VID header. UBI
+@@ -798,10 +801,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
+ * protection queue.
+ */
+ protect = 1;
++ dst_leb_clean = 1;
+ goto out_not_moved;
+ }
+ if (err == MOVE_RETRY) {
+ scrubbing = 1;
++ dst_leb_clean = 1;
+ goto out_not_moved;
+ }
+ if (err == MOVE_TARGET_BITFLIPS || err == MOVE_TARGET_WR_ERR ||
+@@ -827,6 +832,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
+ ubi->erroneous_peb_count);
+ goto out_error;
+ }
++ dst_leb_clean = 1;
+ erroneous = 1;
+ goto out_not_moved;
+ }
+@@ -897,15 +903,24 @@ out_not_moved:
+ wl_tree_add(e1, &ubi->scrub);
+ else
+ wl_tree_add(e1, &ubi->used);
++ if (dst_leb_clean) {
++ wl_tree_add(e2, &ubi->free);
++ ubi->free_count++;
++ }
++
+ ubi_assert(!ubi->move_to_put);
+ ubi->move_from = ubi->move_to = NULL;
+ ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+
+ ubi_free_vid_hdr(ubi, vid_hdr);
+- err = do_sync_erase(ubi, e2, vol_id, lnum, torture);
+- if (err)
+- goto out_ro;
++ if (dst_leb_clean) {
++ ensure_wear_leveling(ubi, 1);
++ } else {
++ err = do_sync_erase(ubi, e2, vol_id, lnum, torture);
++ if (err)
++ goto out_ro;
++ }
+
+ mutex_unlock(&ubi->move_mutex);
+ return 0;
+diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h
+index 36bb6a5..3bf8f95 100644
+--- a/include/linux/mtd/bbm.h
++++ b/include/linux/mtd/bbm.h
+@@ -166,7 +166,6 @@ struct bbm_info {
+ };
+
+ /* OneNAND BBT interface */
+-extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
+ extern int onenand_default_bbt(struct mtd_info *mtd);
+
+ #endif /* __LINUX_MTD_BBM_H */
+diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h
+index c8be32e..ad3c348 100644
+--- a/include/linux/mtd/fsmc.h
++++ b/include/linux/mtd/fsmc.h
+@@ -103,24 +103,6 @@
+
+ #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
+
+-/*
+- * There are 13 bytes of ecc for every 512 byte block in FSMC version 8
+- * and it has to be read consecutively and immediately after the 512
+- * byte data block for hardware to generate the error bit offsets
+- * Managing the ecc bytes in the following way is easier. This way is
+- * similar to oobfree structure maintained already in u-boot nand driver
+- */
+-#define MAX_ECCPLACE_ENTRIES 32
+-
+-struct fsmc_nand_eccplace {
+- uint8_t offset;
+- uint8_t length;
+-};
+-
+-struct fsmc_eccplace {
+- struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES];
+-};
+-
+ struct fsmc_nand_timings {
+ uint8_t tclr;
+ uint8_t tar;
+diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h
+index 02cd5f9..8255118 100644
+--- a/include/linux/mtd/inftl.h
++++ b/include/linux/mtd/inftl.h
+@@ -44,7 +44,6 @@ struct INFTLrecord {
+ unsigned int nb_blocks; /* number of physical blocks */
+ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
+ struct erase_info instr;
+- struct nand_ecclayout oobinfo;
+ };
+
+ int INFTL_mount(struct INFTLrecord *s);
+diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h
+index 366cf77..5e0eb7c 100644
+--- a/include/linux/mtd/map.h
++++ b/include/linux/mtd/map.h
+@@ -142,7 +142,9 @@
+ #endif
+
+ #ifndef map_bankwidth
++#ifdef CONFIG_MTD
+ #warning "No CONFIG_MTD_MAP_BANK_WIDTH_xx selected. No NOR chip support can work"
++#endif
+ static inline int map_bankwidth(void *map)
+ {
+ BUG();
+@@ -238,8 +240,11 @@ struct map_info {
+ If there is no cache to care about this can be set to NULL. */
+ void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
+
+- /* set_vpp() must handle being reentered -- enable, enable, disable
+- must leave it enabled. */
++ /* This will be called with 1 as parameter when the first map user
++ * needs VPP, and called with 0 when the last user exits. The map
++ * core maintains a reference counter, and assumes that VPP is a
++ * global resource applying to all mapped flash chips on the system.
++ */
+ void (*set_vpp)(struct map_info *, int);
+
+ unsigned long pfow_base;
+diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
+index f17fa75..df8c116 100644
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -96,17 +96,35 @@ struct mtd_oob_ops {
+
+ #define MTD_MAX_OOBFREE_ENTRIES_LARGE 32
+ #define MTD_MAX_ECCPOS_ENTRIES_LARGE 640
++/**
++ * struct mtd_oob_region - oob region definition
++ * @offset: region offset
++ * @length: region length
++ *
++ * This structure describes a region of the OOB area, and is used
++ * to retrieve ECC or free bytes sections.
++ * Each section is defined by an offset within the OOB area and a
++ * length.
++ */
++struct mtd_oob_region {
++ u32 offset;
++ u32 length;
++};
++
+ /*
+- * Internal ECC layout control structure. For historical reasons, there is a
+- * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
+- * for export to user-space via the ECCGETLAYOUT ioctl.
+- * nand_ecclayout should be expandable in the future simply by the above macros.
++ * struct mtd_ooblayout_ops - NAND OOB layout operations
++ * @ecc: function returning an ECC region in the OOB area.
++ * Should return -ERANGE if %section exceeds the total number of
++ * ECC sections.
++ * @free: function returning a free region in the OOB area.
++ * Should return -ERANGE if %section exceeds the total number of
++ * free sections.
+ */
+-struct nand_ecclayout {
+- __u32 eccbytes;
+- __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
+- __u32 oobavail;
+- struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
++struct mtd_ooblayout_ops {
++ int (*ecc)(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobecc);
++ int (*free)(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobfree);
+ };
+
+ struct module; /* only needed for owner field in mtd_info */
+@@ -167,8 +185,8 @@ struct mtd_info {
+ const char *name;
+ int index;
+
+- /* ECC layout structure pointer - read only! */
+- struct nand_ecclayout *ecclayout;
++ /* OOB layout description */
++ const struct mtd_ooblayout_ops *ooblayout;
+
+ /* the ecc step size. */
+ unsigned int ecc_step_size;
+@@ -254,6 +272,46 @@ struct mtd_info {
+ int usecount;
+ };
+
++int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobecc);
++int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
++ int *section,
++ struct mtd_oob_region *oobregion);
++int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
++ const u8 *oobbuf, int start, int nbytes);
++int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
++ u8 *oobbuf, int start, int nbytes);
++int mtd_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oobfree);
++int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
++ const u8 *oobbuf, int start, int nbytes);
++int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
++ u8 *oobbuf, int start, int nbytes);
++int mtd_ooblayout_count_freebytes(struct mtd_info *mtd);
++int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd);
++
++static inline void mtd_set_ooblayout(struct mtd_info *mtd,
++ const struct mtd_ooblayout_ops *ooblayout)
++{
++ mtd->ooblayout = ooblayout;
++}
++
++static inline void mtd_set_of_node(struct mtd_info *mtd,
++ struct device_node *np)
++{
++ mtd->dev.of_node = np;
++}
++
++static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
++{
++ return mtd->dev.of_node;
++}
++
++static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
++{
++ return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
++}
++
+ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
+ int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+ void **virt, resource_size_t *phys);
+diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
+index 5a9d1d4..d766c12 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -119,6 +119,12 @@ typedef enum {
+ NAND_ECC_SOFT_BCH,
+ } nand_ecc_modes_t;
+
++enum nand_ecc_algo {
++ NAND_ECC_UNKNOWN,
++ NAND_ECC_HAMMING,
++ NAND_ECC_BCH,
++};
++
+ /*
+ * Constants for Hardware ECC
+ */
+@@ -129,6 +135,14 @@ typedef enum {
+ /* Enable Hardware ECC before syndrome is read back from flash */
+ #define NAND_ECC_READSYN 2
+
++/*
++ * Enable generic NAND 'page erased' check. This check is only done when
++ * ecc.correct() returns -EBADMSG.
++ * Set this flag if your implementation does not fix bitflips in erased
++ * pages and you want to rely on the default implementation.
++ */
++#define NAND_ECC_GENERIC_ERASED_CHECK BIT(0)
++
+ /* Bit mask for flags passed to do_nand_read_ecc */
+ #define NAND_GET_DEVICE 0x80
+
+@@ -160,6 +174,12 @@ typedef enum {
+ /* Device supports subpage reads */
+ #define NAND_SUBPAGE_READ 0x00001000
+
++/*
++ * Some MLC NANDs need data scrambling to limit bitflips caused by repeated
++ * patterns.
++ */
++#define NAND_NEED_SCRAMBLING 0x00002000
++
+ /* Options valid for Samsung large page devices */
+ #define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG
+
+@@ -276,15 +296,15 @@ struct nand_onfi_params {
+ __le16 t_r;
+ __le16 t_ccs;
+ __le16 src_sync_timing_mode;
+- __le16 src_ssync_features;
++ u8 src_ssync_features;
+ __le16 clk_pin_capacitance_typ;
+ __le16 io_pin_capacitance_typ;
+ __le16 input_pin_capacitance_typ;
+ u8 input_pin_capacitance_max;
+ u8 driver_strength_support;
+ __le16 t_int_r;
+- __le16 t_ald;
+- u8 reserved4[7];
++ __le16 t_adl;
++ u8 reserved4[8];
+
+ /* vendor */
+ __le16 vendor_revision;
+@@ -407,7 +427,7 @@ struct nand_jedec_params {
+ __le16 input_pin_capacitance_typ;
+ __le16 clk_pin_capacitance_typ;
+ u8 driver_strength_support;
+- __le16 t_ald;
++ __le16 t_adl;
+ u8 reserved4[36];
+
+ /* ECC and endurance block */
+@@ -444,6 +464,7 @@ struct nand_hw_control {
+ /**
+ * struct nand_ecc_ctrl - Control structure for ECC
+ * @mode: ECC mode
++ * @algo: ECC algorithm
+ * @steps: number of ECC steps per page
+ * @size: data bytes per ECC step
+ * @bytes: ECC bytes per step
+@@ -451,12 +472,18 @@ struct nand_hw_control {
+ * @total: total number of ECC bytes per page
+ * @prepad: padding information for syndrome based ECC generators
+ * @postpad: padding information for syndrome based ECC generators
+- * @layout: ECC layout control struct pointer
++ * @options: ECC specific options (see NAND_ECC_XXX flags defined above)
+ * @priv: pointer to private ECC control data
+ * @hwctl: function to control hardware ECC generator. Must only
+ * be provided if an hardware ECC is available
+ * @calculate: function for ECC calculation or readback from ECC hardware
+- * @correct: function for ECC correction, matching to ECC generator (sw/hw)
++ * @correct: function for ECC correction, matching to ECC generator (sw/hw).
++ * Should return a positive number representing the number of
++ * corrected bitflips, -EBADMSG if the number of bitflips exceed
++ * ECC strength, or any other error code if the error is not
++ * directly related to correction.
++ * If -EBADMSG is returned the input buffers should be left
++ * untouched.
+ * @read_page_raw: function to read a raw page without ECC. This function
+ * should hide the specific layout used by the ECC
+ * controller and always return contiguous in-band and
+@@ -487,6 +514,7 @@ struct nand_hw_control {
+ */
+ struct nand_ecc_ctrl {
+ nand_ecc_modes_t mode;
++ enum nand_ecc_algo algo;
+ int steps;
+ int size;
+ int bytes;
+@@ -494,7 +522,7 @@ struct nand_ecc_ctrl {
+ int strength;
+ int prepad;
+ int postpad;
+- struct nand_ecclayout *layout;
++ unsigned int options;
+ void *priv;
+ void (*hwctl)(struct mtd_info *mtd, int mode);
+ int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
+@@ -540,11 +568,11 @@ struct nand_buffers {
+
+ /**
+ * struct nand_chip - NAND Private Flash Chip Data
++ * @mtd: MTD device registered to the MTD framework
+ * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
+ * flash device
+ * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
+ * flash device.
+- * @flash_node: [BOARDSPECIFIC] device node describing this instance
+ * @read_byte: [REPLACEABLE] read one byte from the chip
+ * @read_word: [REPLACEABLE] read one word from the chip
+ * @write_byte: [REPLACEABLE] write a single byte to the chip on the
+@@ -640,18 +668,17 @@ struct nand_buffers {
+ */
+
+ struct nand_chip {
++ struct mtd_info mtd;
+ void __iomem *IO_ADDR_R;
+ void __iomem *IO_ADDR_W;
+
+- struct device_node *flash_node;
+-
+ uint8_t (*read_byte)(struct mtd_info *mtd);
+ u16 (*read_word)(struct mtd_info *mtd);
+ void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
+ void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
+ void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
+ void (*select_chip)(struct mtd_info *mtd, int chip);
+- int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
++ int (*block_bad)(struct mtd_info *mtd, loff_t ofs);
+ int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
+ void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
+ int (*dev_ready)(struct mtd_info *mtd);
+@@ -719,6 +746,40 @@ struct nand_chip {
+ void *priv;
+ };
+
++extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
++extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops;
++
++static inline void nand_set_flash_node(struct nand_chip *chip,
++ struct device_node *np)
++{
++ mtd_set_of_node(&chip->mtd, np);
++}
++
++static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
++{
++ return mtd_get_of_node(&chip->mtd);
++}
++
++static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
++{
++ return container_of(mtd, struct nand_chip, mtd);
++}
++
++static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
++{
++ return &chip->mtd;
++}
++
++static inline void *nand_get_controller_data(struct nand_chip *chip)
++{
++ return chip->priv;
++}
++
++static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
++{
++ chip->priv = priv;
++}
++
+ /*
+ * NAND Flash Manufacturer ID Codes
+ */
+@@ -850,7 +911,6 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
+ * @chip_delay: R/B delay value in us
+ * @options: Option flags, e.g. 16bit buswidth
+ * @bbt_options: BBT option flags, e.g. NAND_BBT_USE_FLASH
+- * @ecclayout: ECC layout info structure
+ * @part_probe_types: NULL-terminated array of probe types
+ */
+ struct platform_nand_chip {
+@@ -858,7 +918,6 @@ struct platform_nand_chip {
+ int chip_offset;
+ int nr_partitions;
+ struct mtd_partition *partitions;
+- struct nand_ecclayout *ecclayout;
+ int chip_delay;
+ unsigned int options;
+ unsigned int bbt_options;
+@@ -907,15 +966,6 @@ struct platform_nand_data {
+ struct platform_nand_ctrl ctrl;
+ };
+
+-/* Some helpers to access the data structures */
+-static inline
+-struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)
+-{
+- struct nand_chip *chip = mtd->priv;
+-
+- return chip->priv;
+-}
+-
+ /* return the supported features. */
+ static inline int onfi_feature(struct nand_chip *chip)
+ {
+diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h
+index 74acf53..98f20ef 100644
+--- a/include/linux/mtd/nand_bch.h
++++ b/include/linux/mtd/nand_bch.h
+@@ -32,9 +32,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc,
+ /*
+ * Initialize BCH encoder/decoder
+ */
+-struct nand_bch_control *
+-nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
+- unsigned int eccbytes, struct nand_ecclayout **ecclayout);
++struct nand_bch_control *nand_bch_init(struct mtd_info *mtd);
+ /*
+ * Release BCH encoder/decoder resources
+ */
+@@ -55,12 +53,10 @@ static inline int
+ nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+ {
+- return -1;
++ return -ENOTSUPP;
+ }
+
+-static inline struct nand_bch_control *
+-nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
+- unsigned int eccbytes, struct nand_ecclayout **ecclayout)
++static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
+ {
+ return NULL;
+ }
+diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h
+index b059629..044daa0 100644
+--- a/include/linux/mtd/nftl.h
++++ b/include/linux/mtd/nftl.h
+@@ -50,7 +50,6 @@ struct NFTLrecord {
+ unsigned int nb_blocks; /* number of physical blocks */
+ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
+ struct erase_info instr;
+- struct nand_ecclayout oobinfo;
+ };
+
+ int NFTL_mount(struct NFTLrecord *s);
+diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
+index 4596503..0aaa98b 100644
+--- a/include/linux/mtd/onenand.h
++++ b/include/linux/mtd/onenand.h
+@@ -80,7 +80,6 @@ struct onenand_bufferram {
+ * @page_buf: [INTERN] page main data buffer
+ * @oob_buf: [INTERN] page oob data buffer
+ * @subpagesize: [INTERN] holds the subpagesize
+- * @ecclayout: [REPLACEABLE] the default ecc placement scheme
+ * @bbm: [REPLACEABLE] pointer to Bad Block Management
+ * @priv: [OPTIONAL] pointer to private chip date
+ */
+@@ -134,7 +133,6 @@ struct onenand_chip {
+ #endif
+
+ int subpagesize;
+- struct nand_ecclayout *ecclayout;
+
+ void *bbm;
+
+diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
+index 6a35e6d..70736e1 100644
+--- a/include/linux/mtd/partitions.h
++++ b/include/linux/mtd/partitions.h
+@@ -41,7 +41,6 @@ struct mtd_partition {
+ uint64_t size; /* partition size */
+ uint64_t offset; /* offset within the master MTD space */
+ uint32_t mask_flags; /* master MTD flags to mask out for this partition */
+- struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */
+ };
+
+ #define MTDPART_OFS_RETAIN (-3)
+@@ -56,11 +55,9 @@ struct device_node;
+ /**
+ * struct mtd_part_parser_data - used to pass data to MTD partition parsers.
+ * @origin: for RedBoot, start address of MTD device
+- * @of_node: for OF parsers, device node containing partitioning information
+ */
+ struct mtd_part_parser_data {
+ unsigned long origin;
+- struct device_node *of_node;
+ };
+
+
+@@ -72,13 +69,33 @@ struct mtd_part_parser {
+ struct list_head list;
+ struct module *owner;
+ const char *name;
+- int (*parse_fn)(struct mtd_info *, struct mtd_partition **,
++ int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
+ struct mtd_part_parser_data *);
++ void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
+ };
+
+-extern void register_mtd_parser(struct mtd_part_parser *parser);
++/* Container for passing around a set of parsed partitions */
++struct mtd_partitions {
++ const struct mtd_partition *parts;
++ int nr_parts;
++ const struct mtd_part_parser *parser;
++};
++
++extern int __register_mtd_parser(struct mtd_part_parser *parser,
++ struct module *owner);
++#define register_mtd_parser(parser) __register_mtd_parser(parser, THIS_MODULE)
++
+ extern void deregister_mtd_parser(struct mtd_part_parser *parser);
+
++/*
++ * module_mtd_part_parser() - Helper macro for MTD partition parsers that don't
++ * do anything special in module init/exit. Each driver may only use this macro
++ * once, and calling it replaces module_init() and module_exit().
++ */
++#define module_mtd_part_parser(__mtd_part_parser) \
++ module_driver(__mtd_part_parser, register_mtd_parser, \
++ deregister_mtd_parser)
++
+ int mtd_is_partition(const struct mtd_info *mtd);
+ int mtd_add_partition(struct mtd_info *master, const char *name,
+ long long offset, long long length);
+diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
+index 1c28f88..2251add 100644
+--- a/include/linux/mtd/sh_flctl.h
++++ b/include/linux/mtd/sh_flctl.h
+@@ -143,11 +143,11 @@ enum flctl_ecc_res_t {
+ struct dma_chan;
+
+ struct sh_flctl {
+- struct mtd_info mtd;
+ struct nand_chip chip;
+ struct platform_device *pdev;
+ struct dev_pm_qos_request pm_qos;
+ void __iomem *reg;
++ resource_size_t fifo;
+
+ uint8_t done_buff[2048 + 64]; /* max size 2048 + 64 */
+ int read_bytes;
+@@ -186,7 +186,7 @@ struct sh_flctl_platform_data {
+
+ static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo)
+ {
+- return container_of(mtdinfo, struct sh_flctl, mtd);
++ return container_of(mtd_to_nand(mtdinfo), struct sh_flctl, chip);
+ }
+
+ #endif /* __SH_FLCTL_H__ */
+diff --git a/include/linux/mtd/sharpsl.h b/include/linux/mtd/sharpsl.h
+index 25f4d2a..65e91d0 100644
+--- a/include/linux/mtd/sharpsl.h
++++ b/include/linux/mtd/sharpsl.h
+@@ -14,7 +14,7 @@
+
+ struct sharpsl_nand_platform_data {
+ struct nand_bbt_descr *badblock_pattern;
+- struct nand_ecclayout *ecc_layout;
++ const struct mtd_ooblayout_ops *ecc_layout;
+ struct mtd_partition *partitions;
+ unsigned int nr_partitions;
+ };
+diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
+index c8723b6..3c36113 100644
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -12,6 +12,7 @@
+
+ #include <linux/bitops.h>
+ #include <linux/mtd/cfi.h>
++#include <linux/mtd/mtd.h>
+
+ /*
+ * Manufacturer IDs
+@@ -25,7 +26,7 @@
+ #define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX
+ #define SNOR_MFR_SPANSION CFI_MFR_AMD
+ #define SNOR_MFR_SST CFI_MFR_SST
+-#define SNOR_MFR_WINBOND 0xef
++#define SNOR_MFR_WINBOND 0xef /* Also used by some Spansion */
+
+ /*
+ * Note on opcode nomenclature: some opcodes have a format like
+@@ -84,6 +85,7 @@
+ #define SR_BP0 BIT(2) /* Block protect 0 */
+ #define SR_BP1 BIT(3) /* Block protect 1 */
+ #define SR_BP2 BIT(4) /* Block protect 2 */
++#define SR_TB BIT(5) /* Top/Bottom protect */
+ #define SR_SRWD BIT(7) /* SR write protect */
+
+ #define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
+@@ -115,16 +117,14 @@ enum spi_nor_ops {
+
+ enum spi_nor_option_flags {
+ SNOR_F_USE_FSR = BIT(0),
++ SNOR_F_HAS_SR_TB = BIT(1),
+ };
+
+-struct mtd_info;
+-
+ /**
+ * struct spi_nor - Structure for defining a the SPI NOR layer
+ * @mtd: point to a mtd_info structure
+ * @lock: the lock for the read/write/erase/lock/unlock operations
+ * @dev: point to a spi device, or a spi nor controller device.
+- * @flash_node: point to a device node describing this flash instance.
+ * @page_size: the page size of the SPI NOR
+ * @addr_width: number of address bytes
+ * @erase_opcode: the opcode for erasing a sector
+@@ -144,7 +144,8 @@ struct mtd_info;
+ * @read: [DRIVER-SPECIFIC] read data from the SPI NOR
+ * @write: [DRIVER-SPECIFIC] write data to the SPI NOR
+ * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
+- * at the offset @offs
++ * at the offset @offs; if not provided by the driver,
++ * spi-nor will send the erase opcode via write_reg()
+ * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR
+ * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
+ * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
+@@ -155,7 +156,6 @@ struct spi_nor {
+ struct mtd_info mtd;
+ struct mutex lock;
+ struct device *dev;
+- struct device_node *flash_node;
+ u32 page_size;
+ u8 addr_width;
+ u8 erase_opcode;
+@@ -185,6 +185,17 @@ struct spi_nor {
+ void *priv;
+ };
+
++static inline void spi_nor_set_flash_node(struct spi_nor *nor,
++ struct device_node *np)
++{
++ mtd_set_of_node(&nor->mtd, np);
++}
++
++static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
++{
++ return mtd_get_of_node(&nor->mtd);
++}
++
+ /**
+ * spi_nor_scan() - scan the SPI NOR
+ * @nor: the spi_nor structure
+diff --git a/include/uapi/mtd/mtd-abi.h b/include/uapi/mtd/mtd-abi.h
+index 763bb69..0ec1da2 100644
+--- a/include/uapi/mtd/mtd-abi.h
++++ b/include/uapi/mtd/mtd-abi.h
+@@ -228,7 +228,7 @@ struct nand_oobfree {
+ * complete set of ECC information. The ioctl truncates the larger internal
+ * structure to retain binary compatibility with the static declaration of the
+ * ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of
+- * the user struct, not the MAX size of the internal struct nand_ecclayout.
++ * the user struct, not the MAX size of the internal OOB layout representation.
+ */
+ struct nand_ecclayout_user {
+ __u32 eccbytes;
+--
+1.7.10.4
+
+++ /dev/null
-From 51dc0a2114c3d6e51bf2acde415fccdec031e480 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Fri, 25 Mar 2016 04:24:27 +0100
-Subject: [PATCH 72/91] net: mediatek: remove superflous reset call
-
-HW reset is triggered int he mtk_hw_init() function. There is no need to
-reset the core during probe.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ----
- 1 file changed, 4 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1679,10 +1679,6 @@ static int mtk_probe(struct platform_dev
- struct mtk_eth *eth;
- int err;
-
-- err = device_reset(&pdev->dev);
-- if (err)
-- return err;
--
- match = of_match_device(of_mtk_match, &pdev->dev);
- soc = (struct mtk_soc_data *)match->data;
-
+++ /dev/null
-From 868eb5a3d0217e1ecdc2f628c6dc4fcd18562a71 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 29 Mar 2016 16:41:07 +0200
-Subject: [PATCH 73/91] net: mediatek: fix stop and wakeup of queue
-
-The driver supports 2 MACs. Both run on the same DMA ring. If we go
-above/below the TX rings thershold value, we always need to wake/stop
-the queu of both devices. Not doing to can cause TX stalls and packet
-drops on one of the devices.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 37 +++++++++++++++++++--------
- 1 file changed, 27 insertions(+), 10 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -684,6 +684,28 @@ static inline int mtk_cal_txd_req(struct
- return nfrags;
- }
-
-+static void mtk_wake_queue(struct mtk_eth *eth)
-+{
-+ int i;
-+
-+ for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ if (!eth->netdev[i])
-+ continue;
-+ netif_wake_queue(eth->netdev[i]);
-+ }
-+}
-+
-+static void mtk_stop_queue(struct mtk_eth *eth)
-+{
-+ int i;
-+
-+ for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ if (!eth->netdev[i])
-+ continue;
-+ netif_stop_queue(eth->netdev[i]);
-+ }
-+}
-+
- static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
- {
- struct mtk_mac *mac = netdev_priv(dev);
-@@ -695,7 +717,7 @@ static int mtk_start_xmit(struct sk_buff
-
- tx_num = mtk_cal_txd_req(skb);
- if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
-- netif_stop_queue(dev);
-+ mtk_stop_queue(eth);
- netif_err(eth, tx_queued, dev,
- "Tx Ring full when queue awake!\n");
- return NETDEV_TX_BUSY;
-@@ -720,10 +742,10 @@ static int mtk_start_xmit(struct sk_buff
- goto drop;
-
- if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
-- netif_stop_queue(dev);
-+ mtk_stop_queue(eth);
- if (unlikely(atomic_read(&ring->free_count) >
- ring->thresh))
-- netif_wake_queue(dev);
-+ mtk_wake_queue(eth);
- }
-
- return NETDEV_TX_OK;
-@@ -897,13 +919,8 @@ static int mtk_poll_tx(struct mtk_eth *e
- if (!total)
- return 0;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- if (!eth->netdev[i] ||
-- unlikely(!netif_queue_stopped(eth->netdev[i])))
-- continue;
-- if (atomic_read(&ring->free_count) > ring->thresh)
-- netif_wake_queue(eth->netdev[i]);
-- }
-+ if (atomic_read(&ring->free_count) > ring->thresh)
-+ mtk_wake_queue(eth);
-
- return total;
- }
--- /dev/null
+From 410a91f6efa1c4c3c4369d1dd2c31286749dff33 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Wed, 23 Mar 2016 11:19:01 +0100
+Subject: [PATCH 073/102] of: mtd: prepare helper reading NAND ECC algo from
+ DT
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+NAND subsystem is being slightly reworked to store ECC details in
+separated fields. In future we'll want to add support for more DT
+properties as specifying every possible setup with a single
+"nand-ecc-mode" is a pretty bad idea.
+To allow this let's add a helper that will support something like
+"nand-ecc-algo" in future. Right now we use it for keeping backward
+compatibility.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+---
+ drivers/of/of_mtd.c | 36 ++++++++++++++++++++++++++++++++++++
+ include/linux/of_mtd.h | 6 ++++++
+ 2 files changed, 42 insertions(+)
+
+diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
+index b7361ed..15d056e 100644
+--- a/drivers/of/of_mtd.c
++++ b/drivers/of/of_mtd.c
+@@ -50,6 +50,42 @@ int of_get_nand_ecc_mode(struct device_node *np)
+ EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
+
+ /**
++ * of_get_nand_ecc_algo - Get nand ecc algorithm for given device_node
++ * @np: Pointer to the given device_node
++ *
++ * The function gets ecc algorithm and returns its enum value, or errno in error
++ * case.
++ */
++int of_get_nand_ecc_algo(struct device_node *np)
++{
++ const char *pm;
++ int err;
++
++ /*
++ * TODO: Read ECC algo OF property and map it to enum nand_ecc_algo.
++ * It's not implemented yet as currently NAND subsystem ignores
++ * algorithm explicitly set this way. Once it's handled we should
++ * document & support new property.
++ */
++
++ /*
++ * For backward compatibility we also read "nand-ecc-mode" checking
++ * for some obsoleted values that were specifying ECC algorithm.
++ */
++ err = of_property_read_string(np, "nand-ecc-mode", &pm);
++ if (err < 0)
++ return err;
++
++ if (!strcasecmp(pm, "soft"))
++ return NAND_ECC_HAMMING;
++ else if (!strcasecmp(pm, "soft_bch"))
++ return NAND_ECC_BCH;
++
++ return -ENODEV;
++}
++EXPORT_SYMBOL_GPL(of_get_nand_ecc_algo);
++
++/**
+ * of_get_nand_ecc_step_size - Get ECC step size associated to
+ * the required ECC strength (see below).
+ * @np: Pointer to the given device_node
+diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h
+index e266caa..0f6aca5 100644
+--- a/include/linux/of_mtd.h
++++ b/include/linux/of_mtd.h
+@@ -13,6 +13,7 @@
+
+ #include <linux/of.h>
+ int of_get_nand_ecc_mode(struct device_node *np);
++int of_get_nand_ecc_algo(struct device_node *np);
+ int of_get_nand_ecc_step_size(struct device_node *np);
+ int of_get_nand_ecc_strength(struct device_node *np);
+ int of_get_nand_bus_width(struct device_node *np);
+@@ -25,6 +26,11 @@ static inline int of_get_nand_ecc_mode(struct device_node *np)
+ return -ENOSYS;
+ }
+
++static inline int of_get_nand_ecc_algo(struct device_node *np)
++{
++ return -ENOSYS;
++}
++
+ static inline int of_get_nand_ecc_step_size(struct device_node *np)
+ {
+ return -ENOSYS;
+--
+1.7.10.4
+
--- /dev/null
+From 5e1c00983efeca4522ac2e8574e3e3997d26a203 Mon Sep 17 00:00:00 2001
+From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+Date: Fri, 29 Apr 2016 12:17:21 -0400
+Subject: [PATCH 074/102] mtd: mediatek: device tree docs for MTK Smart Device
+ Gen1 NAND
+
+This patch adds documentation support for Smart Device Gen1 type of
+NAND controllers.
+
+Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+---
+ Documentation/devicetree/bindings/mtd/mtk-nand.txt | 161 ++++++++++++++++++++
+ 1 file changed, 161 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/mtk-nand.txt
+
+diff --git a/Documentation/devicetree/bindings/mtd/mtk-nand.txt b/Documentation/devicetree/bindings/mtd/mtk-nand.txt
+new file mode 100644
+index 0000000..175767d
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/mtk-nand.txt
+@@ -0,0 +1,161 @@
++MTK SoCs NAND FLASH controller (NFC) DT binding
++
++This file documents the device tree bindings for MTK SoCs NAND controllers.
++The functional split of the controller requires two drivers to operate:
++the nand controller interface driver and the ECC engine driver.
++
++The hardware description for both devices must be captured as device
++tree nodes.
++
++1) NFC NAND Controller Interface (NFI):
++=======================================
++
++The first part of NFC is NAND Controller Interface (NFI) HW.
++Required NFI properties:
++- compatible: Should be "mediatek,mtxxxx-nfc".
++- reg: Base physical address and size of NFI.
++- interrupts: Interrupts of NFI.
++- clocks: NFI required clocks.
++- clock-names: NFI clocks internal name.
++- status: Disabled default. Then set "okay" by platform.
++- ecc-engine: Required ECC Engine node.
++- #address-cells: NAND chip index, should be 1.
++- #size-cells: Should be 0.
++
++Example:
++
++ nandc: nfi@1100d000 {
++ compatible = "mediatek,mt2701-nfc";
++ reg = <0 0x1100d000 0 0x1000>;
++ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>;
++ clocks = <&pericfg CLK_PERI_NFI>,
++ <&pericfg CLK_PERI_NFI_PAD>;
++ clock-names = "nfi_clk", "pad_clk";
++ status = "disabled";
++ ecc-engine = <&bch>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++Platform related properties, should be set in {platform_name}.dts:
++- children nodes: NAND chips.
++
++Children nodes properties:
++- reg: Chip Select Signal, default 0.
++ Set as reg = <0>, <1> when need 2 CS.
++Optional:
++- nand-on-flash-bbt: Store BBT on NAND Flash.
++- nand-ecc-mode: the NAND ecc mode (check driver for supported modes)
++- nand-ecc-step-size: Number of data bytes covered by a single ECC step.
++ The controller only supports 512 and 1024.
++ For large page NANDs ther recommended value is 1024.
++- nand-ecc-strength: Number of bits to correct per ECC step.
++ The valid values that the controller supports are: 4, 6,
++ 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44,
++ 48, 52, 56, 60.
++ The strength should be calculated as follows:
++ E = (S - F) * 8 / 14
++ S = O / (P / Q)
++ E :nand-ecc-strength;
++ S :spare size per sector;
++ F : FDM size, should be in the range [1,8].
++ It is used to store free oob data.
++ O : oob size;
++ P : page size;
++ Q :nand-ecc-step-size
++ If the result does not match any one of the listed
++ choices above, please select the smaller valid value from
++ the list.
++ (otherwise the driver will do the clamping at runtime).
++- vmch-supply: NAND power supply.
++- pinctrl-names: Default NAND pin GPIO setting name.
++- pinctrl-0: GPIO setting node.
++
++Example:
++ &pio {
++ nand_pins_default: nanddefault {
++ pins_dat {
++ pinmux = <MT2701_PIN_111_MSDC0_DAT7__FUNC_NLD7>,
++ <MT2701_PIN_112_MSDC0_DAT6__FUNC_NLD6>,
++ <MT2701_PIN_114_MSDC0_DAT4__FUNC_NLD4>,
++ <MT2701_PIN_118_MSDC0_DAT3__FUNC_NLD3>,
++ <MT2701_PIN_121_MSDC0_DAT0__FUNC_NLD0>,
++ <MT2701_PIN_120_MSDC0_DAT1__FUNC_NLD1>,
++ <MT2701_PIN_113_MSDC0_DAT5__FUNC_NLD5>,
++ <MT2701_PIN_115_MSDC0_RSTB__FUNC_NLD8>,
++ <MT2701_PIN_119_MSDC0_DAT2__FUNC_NLD2>;
++ input-enable;
++ drive-strength = <MTK_DRIVE_8mA>;
++ bias-pull-up;
++ };
++
++ pins_we {
++ pinmux = <MT2701_PIN_117_MSDC0_CLK__FUNC_NWEB>;
++ drive-strength = <MTK_DRIVE_8mA>;
++ bias-pull-up = <MTK_PUPD_SET_R1R0_10>;
++ };
++
++ pins_ale {
++ pinmux = <MT2701_PIN_116_MSDC0_CMD__FUNC_NALE>;
++ drive-strength = <MTK_DRIVE_8mA>;
++ bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
++ };
++ };
++ };
++
++ &nandc {
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <&nand_pins_default>;
++ nand@0 {
++ reg = <0>;
++ nand-on-flash-bbt;
++ nand-ecc-mode = "hw";
++ nand-ecc-strength = <24>;
++ nand-ecc-step-size = <1024>;
++ };
++ };
++
++NAND chip optional subnodes:
++- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
++
++Example:
++ nand@0 {
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ preloader@0 {
++ label = "pl";
++ read-only;
++ reg = <0x00000000 0x00400000>;
++ };
++ android@0x00400000 {
++ label = "android";
++ reg = <0x00400000 0x12c00000>;
++ };
++ };
++ };
++
++2) ECC Engine:
++==============
++
++Required BCH properties:
++- compatible: Should be "mediatek,mtxxxx-ecc".
++- reg: Base physical address and size of ECC.
++- interrupts: Interrupts of ECC.
++- clocks: ECC required clocks.
++- clock-names: ECC clocks internal name.
++- status: Disabled default. Then set "okay" by platform.
++
++Example:
++
++ bch: ecc@1100e000 {
++ compatible = "mediatek,mt2701-ecc";
++ reg = <0 0x1100e000 0 0x1000>;
++ interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
++ clocks = <&pericfg CLK_PERI_NFI_ECC>;
++ clock-names = "nfiecc_clk";
++ status = "disabled";
++ };
+--
+1.7.10.4
+
+++ /dev/null
-From 300ca8c6b5dcee2593f22d5bf8f13bb4da8c19c5 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 29 Mar 2016 17:00:47 +0200
-Subject: [PATCH 74/91] net: mediatek: fix mtk_pending_work
-
-The driver supports 2 MACs. Both run on the same DMA ring. If we hit a TX
-timeout we need to stop both netdevs before retarting them again. If we
-dont do thsi, mtk_stop() wont shutdown DMA and the consecutive call to
-mtk_open() wont restart DMA and enable IRQs.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 30 +++++++++++++++++++--------
- 1 file changed, 21 insertions(+), 9 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1430,19 +1430,31 @@ static int mtk_do_ioctl(struct net_devic
-
- static void mtk_pending_work(struct work_struct *work)
- {
-- struct mtk_mac *mac = container_of(work, struct mtk_mac, pending_work);
-- struct mtk_eth *eth = mac->hw;
-- struct net_device *dev = eth->netdev[mac->id];
-- int err;
-+ struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work);
-+ int err, i;
-+ unsigned long restart = 0;
-
- rtnl_lock();
-- mtk_stop(dev);
-
-- err = mtk_open(dev);
-- if (err) {
-- netif_alert(eth, ifup, dev,
-+ /* stop all devices to make sure that dma is properly shut down */
-+ for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ if (!netif_oper_up(eth->netdev[i]))
-+ continue;
-+ mtk_stop(eth->netdev[i]);
-+ __set_bit(i, &restart);
-+ }
-+
-+
-+ /* restart DMA and enable IRQs */
-+ for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ if (!test_bit(i, &restart))
-+ continue;
-+ err = mtk_open(eth->netdev[i]);
-+ if (err) {
-+ netif_alert(eth, ifup, eth->netdev[i],
- "Driver up/down cycle failed, closing device.\n");
-- dev_close(dev);
-+ dev_close(eth->netdev[i]);
-+ }
- }
- rtnl_unlock();
- }
--- /dev/null
+From de18239fc971cfc17c53320c66ae64dd5ade032d Mon Sep 17 00:00:00 2001
+From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+Date: Fri, 29 Apr 2016 12:17:22 -0400
+Subject: [PATCH 075/102] mtd: mediatek: driver for MTK Smart Device Gen1 NAND
+
+This patch adds support for mediatek's SDG1 NFC nand controller
+embedded in SoC 2701
+
+Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+---
+ drivers/mtd/nand/Kconfig | 7 +
+ drivers/mtd/nand/Makefile | 1 +
+ drivers/mtd/nand/mtk_ecc.c | 527 ++++++++++++++++
+ drivers/mtd/nand/mtk_ecc.h | 53 ++
+ drivers/mtd/nand/mtk_nand.c | 1432 +++++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 2020 insertions(+)
+ create mode 100644 drivers/mtd/nand/mtk_ecc.c
+ create mode 100644 drivers/mtd/nand/mtk_ecc.h
+ create mode 100644 drivers/mtd/nand/mtk_nand.c
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index f05e0e9..3c26e89 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -563,4 +563,11 @@ config MTD_NAND_QCOM
+ Enables support for NAND flash chips on SoCs containing the EBI2 NAND
+ controller. This controller is found on IPQ806x SoC.
+
++config MTD_NAND_MTK
++ tristate "Support for NAND controller on MTK SoCs"
++ depends on HAS_DMA
++ help
++ Enables support for NAND controller on MTK SoCs.
++ This controller is found on mt27xx, mt81xx, mt65xx SoCs.
++
+ endif # MTD_NAND
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index f553353..cafde6f 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -57,5 +57,6 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
+ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
+ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
++obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o mtk_ecc.o
+
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
+diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c
+new file mode 100644
+index 0000000..28769f1
+--- /dev/null
++++ b/drivers/mtd/nand/mtk_ecc.c
+@@ -0,0 +1,527 @@
++/*
++ * MTK ECC controller driver.
++ * Copyright (C) 2016 MediaTek Inc.
++ * Authors: Xiaolei Li <xiaolei.li@mediatek.com>
++ * Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/clk.h>
++#include <linux/module.h>
++#include <linux/iopoll.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/semaphore.h>
++
++#include "mtk_ecc.h"
++
++#define ECC_ENCCON (0x00)
++#define ENC_EN (1)
++#define ENC_DE (0)
++#define ECC_ENCCNFG (0x04)
++#define ECC_CNFG_4BIT (0)
++#define ECC_CNFG_6BIT (1)
++#define ECC_CNFG_8BIT (2)
++#define ECC_CNFG_10BIT (3)
++#define ECC_CNFG_12BIT (4)
++#define ECC_CNFG_14BIT (5)
++#define ECC_CNFG_16BIT (6)
++#define ECC_CNFG_18BIT (7)
++#define ECC_CNFG_20BIT (8)
++#define ECC_CNFG_22BIT (9)
++#define ECC_CNFG_24BIT (0xa)
++#define ECC_CNFG_28BIT (0xb)
++#define ECC_CNFG_32BIT (0xc)
++#define ECC_CNFG_36BIT (0xd)
++#define ECC_CNFG_40BIT (0xe)
++#define ECC_CNFG_44BIT (0xf)
++#define ECC_CNFG_48BIT (0x10)
++#define ECC_CNFG_52BIT (0x11)
++#define ECC_CNFG_56BIT (0x12)
++#define ECC_CNFG_60BIT (0x13)
++#define ECC_MODE_SHIFT (5)
++#define ECC_MS_SHIFT (16)
++#define ECC_ENCDIADDR (0x08)
++#define ECC_ENCIDLE (0x0C)
++#define ENC_IDLE BIT(0)
++#define ECC_ENCPAR(x) (0x10 + (x) * sizeof(u32))
++#define ECC_ENCIRQ_EN (0x80)
++#define ENC_IRQEN BIT(0)
++#define ECC_ENCIRQ_STA (0x84)
++#define ECC_DECCON (0x100)
++#define DEC_EN (1)
++#define DEC_DE (0)
++#define ECC_DECCNFG (0x104)
++#define DEC_EMPTY_EN BIT(31)
++#define DEC_CNFG_CORRECT (0x3 << 12)
++#define ECC_DECIDLE (0x10C)
++#define DEC_IDLE BIT(0)
++#define ECC_DECENUM0 (0x114)
++#define ERR_MASK (0x3f)
++#define ECC_DECDONE (0x124)
++#define ECC_DECIRQ_EN (0x200)
++#define DEC_IRQEN BIT(0)
++#define ECC_DECIRQ_STA (0x204)
++
++#define ECC_TIMEOUT (500000)
++
++#define ECC_IDLE_REG(x) ((x) == ECC_ENC ? ECC_ENCIDLE : ECC_DECIDLE)
++#define ECC_IDLE_MASK(x) ((x) == ECC_ENC ? ENC_IDLE : DEC_IDLE)
++#define ECC_IRQ_REG(x) ((x) == ECC_ENC ? ECC_ENCIRQ_EN : ECC_DECIRQ_EN)
++#define ECC_IRQ_EN(x) ((x) == ECC_ENC ? ENC_IRQEN : DEC_IRQEN)
++#define ECC_CTL_REG(x) ((x) == ECC_ENC ? ECC_ENCCON : ECC_DECCON)
++#define ECC_CODEC_ENABLE(x) ((x) == ECC_ENC ? ENC_EN : DEC_EN)
++#define ECC_CODEC_DISABLE(x) ((x) == ECC_ENC ? ENC_DE : DEC_DE)
++
++struct mtk_ecc {
++ struct device *dev;
++ void __iomem *regs;
++ struct clk *clk;
++
++ struct completion done;
++ struct semaphore sem;
++ u32 sec_mask;
++};
++
++static inline void mtk_ecc_codec_wait_idle(struct mtk_ecc *ecc,
++ enum mtk_ecc_codec codec)
++{
++ struct device *dev = ecc->dev;
++ u32 val;
++ int ret;
++
++ ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(codec), val,
++ val & ECC_IDLE_MASK(codec),
++ 10, ECC_TIMEOUT);
++ if (ret)
++ dev_warn(dev, "%s NOT idle\n",
++ codec == ECC_ENC ? "encoder" : "decoder");
++}
++
++static irqreturn_t mtk_ecc_irq(int irq, void *id)
++{
++ struct mtk_ecc *ecc = id;
++ enum mtk_ecc_codec codec;
++ u32 dec, enc;
++
++ dec = readw(ecc->regs + ECC_DECIRQ_STA) & DEC_IRQEN;
++ if (dec) {
++ codec = ECC_DEC;
++ dec = readw(ecc->regs + ECC_DECDONE);
++ if (dec & ecc->sec_mask) {
++ ecc->sec_mask = 0;
++ complete(&ecc->done);
++ } else
++ return IRQ_HANDLED;
++ } else {
++ enc = readl(ecc->regs + ECC_ENCIRQ_STA) & ENC_IRQEN;
++ if (enc) {
++ codec = ECC_ENC;
++ complete(&ecc->done);
++ } else
++ return IRQ_NONE;
++ }
++
++ writel(0, ecc->regs + ECC_IRQ_REG(codec));
++
++ return IRQ_HANDLED;
++}
++
++static void mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
++{
++ u32 ecc_bit = ECC_CNFG_4BIT, dec_sz, enc_sz;
++ u32 reg;
++
++ switch (config->strength) {
++ case 4:
++ ecc_bit = ECC_CNFG_4BIT;
++ break;
++ case 6:
++ ecc_bit = ECC_CNFG_6BIT;
++ break;
++ case 8:
++ ecc_bit = ECC_CNFG_8BIT;
++ break;
++ case 10:
++ ecc_bit = ECC_CNFG_10BIT;
++ break;
++ case 12:
++ ecc_bit = ECC_CNFG_12BIT;
++ break;
++ case 14:
++ ecc_bit = ECC_CNFG_14BIT;
++ break;
++ case 16:
++ ecc_bit = ECC_CNFG_16BIT;
++ break;
++ case 18:
++ ecc_bit = ECC_CNFG_18BIT;
++ break;
++ case 20:
++ ecc_bit = ECC_CNFG_20BIT;
++ break;
++ case 22:
++ ecc_bit = ECC_CNFG_22BIT;
++ break;
++ case 24:
++ ecc_bit = ECC_CNFG_24BIT;
++ break;
++ case 28:
++ ecc_bit = ECC_CNFG_28BIT;
++ break;
++ case 32:
++ ecc_bit = ECC_CNFG_32BIT;
++ break;
++ case 36:
++ ecc_bit = ECC_CNFG_36BIT;
++ break;
++ case 40:
++ ecc_bit = ECC_CNFG_40BIT;
++ break;
++ case 44:
++ ecc_bit = ECC_CNFG_44BIT;
++ break;
++ case 48:
++ ecc_bit = ECC_CNFG_48BIT;
++ break;
++ case 52:
++ ecc_bit = ECC_CNFG_52BIT;
++ break;
++ case 56:
++ ecc_bit = ECC_CNFG_56BIT;
++ break;
++ case 60:
++ ecc_bit = ECC_CNFG_60BIT;
++ break;
++ default:
++ dev_err(ecc->dev, "invalid strength %d\n", config->strength);
++ }
++
++ if (config->codec == ECC_ENC) {
++ /* configure ECC encoder (in bits) */
++ enc_sz = config->enc_len << 3;
++
++ reg = ecc_bit | (config->ecc_mode << ECC_MODE_SHIFT);
++ reg |= (enc_sz << ECC_MS_SHIFT);
++ writel(reg, ecc->regs + ECC_ENCCNFG);
++
++ if (config->ecc_mode != ECC_NFI_MODE)
++ writel(lower_32_bits(config->addr),
++ ecc->regs + ECC_ENCDIADDR);
++
++ } else {
++ /* configure ECC decoder (in bits) */
++ dec_sz = config->dec_len;
++
++ reg = ecc_bit | (config->ecc_mode << ECC_MODE_SHIFT);
++ reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT;
++ reg |= DEC_EMPTY_EN;
++ writel(reg, ecc->regs + ECC_DECCNFG);
++
++ if (config->sec_mask)
++ ecc->sec_mask = 1 << (config->sec_mask - 1);
++ }
++}
++
++void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
++ int sectors)
++{
++ u32 offset, i, err;
++ u32 bitflips = 0;
++
++ stats->corrected = 0;
++ stats->failed = 0;
++
++ for (i = 0; i < sectors; i++) {
++ offset = (i >> 2) << 2;
++ err = readl(ecc->regs + ECC_DECENUM0 + offset);
++ err = err >> ((i % 4) * 8);
++ err &= ERR_MASK;
++ if (err == ERR_MASK) {
++ /* uncorrectable errors */
++ stats->failed++;
++ continue;
++ }
++
++ stats->corrected += err;
++ bitflips = max_t(u32, bitflips, err);
++ }
++
++ stats->bitflips = bitflips;
++}
++EXPORT_SYMBOL(mtk_ecc_get_stats);
++
++void mtk_ecc_release(struct mtk_ecc *ecc)
++{
++ clk_disable_unprepare(ecc->clk);
++ put_device(ecc->dev);
++}
++EXPORT_SYMBOL(mtk_ecc_release);
++
++static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
++{
++ struct platform_device *pdev;
++ struct mtk_ecc *ecc;
++
++ pdev = of_find_device_by_node(np);
++ if (!pdev || !platform_get_drvdata(pdev))
++ return ERR_PTR(-EPROBE_DEFER);
++
++ get_device(&pdev->dev);
++ ecc = platform_get_drvdata(pdev);
++ clk_prepare_enable(ecc->clk);
++ mtk_ecc_hw_init(ecc);
++
++ return ecc;
++}
++
++struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
++{
++ struct mtk_ecc *ecc = NULL;
++ struct device_node *np;
++
++ np = of_parse_phandle(of_node, "ecc-engine", 0);
++ if (np) {
++ ecc = mtk_ecc_get(np);
++ of_node_put(np);
++ }
++
++ return ecc;
++}
++EXPORT_SYMBOL(of_mtk_ecc_get);
++
++int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
++{
++ enum mtk_ecc_codec codec = config->codec;
++ int ret;
++
++ ret = down_interruptible(&ecc->sem);
++ if (ret) {
++ dev_err(ecc->dev, "interrupted when attempting to lock\n");
++ return ret;
++ }
++
++ mtk_ecc_codec_wait_idle(ecc, codec);
++ mtk_ecc_config(ecc, config);
++ writew(ECC_CODEC_ENABLE(codec), ecc->regs + ECC_CTL_REG(codec));
++
++ init_completion(&ecc->done);
++ writew(ECC_IRQ_EN(codec), ecc->regs + ECC_IRQ_REG(codec));
++
++ return 0;
++}
++EXPORT_SYMBOL(mtk_ecc_enable);
++
++void mtk_ecc_disable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
++{
++ enum mtk_ecc_codec codec = config->codec;
++
++ mtk_ecc_codec_wait_idle(ecc, codec);
++ writew(0, ecc->regs + ECC_IRQ_REG(codec));
++ writew(ECC_CODEC_DISABLE(codec), ecc->regs + ECC_CTL_REG(codec));
++ up(&ecc->sem);
++}
++EXPORT_SYMBOL(mtk_ecc_disable);
++
++int mtk_ecc_wait_irq_done(struct mtk_ecc *ecc, enum mtk_ecc_codec codec)
++{
++ int ret;
++
++ ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500));
++ if (!ret) {
++ dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n",
++ (codec == ECC_ENC) ? "encoder" : "decoder");
++ return -ETIMEDOUT;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(mtk_ecc_wait_irq_done);
++
++int mtk_ecc_encode_non_nfi_mode(struct mtk_ecc *ecc,
++ struct mtk_ecc_config *config, u8 *data, u32 bytes)
++{
++ dma_addr_t addr;
++ u32 *p, len, i;
++ int ret = 0;
++
++ addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE);
++ ret = dma_mapping_error(ecc->dev, addr);
++ if (ret) {
++ dev_err(ecc->dev, "dma mapping error\n");
++ return -EINVAL;
++ }
++
++ config->codec = ECC_ENC;
++ config->addr = addr;
++ ret = mtk_ecc_enable(ecc, config);
++ if (ret) {
++ dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
++ return ret;
++ }
++
++ ret = mtk_ecc_wait_irq_done(ecc, ECC_ENC);
++ if (ret)
++ goto timeout;
++
++ mtk_ecc_codec_wait_idle(ecc, ECC_ENC);
++
++ /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
++ len = (config->strength * ECC_PARITY_BITS + 7) >> 3;
++ p = (u32 *) (data + bytes);
++
++ /* write the parity bytes generated by the ECC back to the OOB region */
++ for (i = 0; i < len; i++)
++ p[i] = readl(ecc->regs + ECC_ENCPAR(i));
++timeout:
++
++ dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
++ mtk_ecc_disable(ecc, config);
++
++ return ret;
++}
++EXPORT_SYMBOL(mtk_ecc_encode_non_nfi_mode);
++
++void mtk_ecc_hw_init(struct mtk_ecc *ecc)
++{
++ mtk_ecc_codec_wait_idle(ecc, ECC_ENC);
++ writew(ENC_DE, ecc->regs + ECC_ENCCON);
++
++ mtk_ecc_codec_wait_idle(ecc, ECC_DEC);
++ writel(DEC_DE, ecc->regs + ECC_DECCON);
++}
++
++void mtk_ecc_update_strength(u32 *p)
++{
++ u32 ecc[] = {4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
++ 40, 44, 48, 52, 56, 60};
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(ecc); i++) {
++ if (*p <= ecc[i]) {
++ if (!i)
++ *p = ecc[i];
++ else if (*p != ecc[i])
++ *p = ecc[i - 1];
++ return;
++ }
++ }
++
++ *p = ecc[ARRAY_SIZE(ecc) - 1];
++}
++EXPORT_SYMBOL(mtk_ecc_update_strength);
++
++static int mtk_ecc_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct mtk_ecc *ecc;
++ struct resource *res;
++ int irq, ret;
++
++ ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
++ if (!ecc)
++ return -ENOMEM;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ ecc->regs = devm_ioremap_resource(dev, res);
++ if (IS_ERR(ecc->regs)) {
++ dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs));
++ return PTR_ERR(ecc->regs);
++ }
++
++ ecc->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(ecc->clk)) {
++ dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
++ return PTR_ERR(ecc->clk);
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ dev_err(dev, "failed to get irq\n");
++ return -EINVAL;
++ }
++
++ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
++ if (ret) {
++ dev_err(dev, "failed to set DMA mask\n");
++ return ret;
++ }
++
++ ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc);
++ if (ret) {
++ dev_err(dev, "failed to request irq\n");
++ return -EINVAL;
++ }
++
++ ecc->dev = dev;
++ sema_init(&ecc->sem, 1);
++ platform_set_drvdata(pdev, ecc);
++ dev_info(dev, "probed\n");
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int mtk_ecc_suspend(struct device *dev)
++{
++ struct mtk_ecc *ecc = dev_get_drvdata(dev);
++
++ clk_disable_unprepare(ecc->clk);
++
++ return 0;
++}
++
++static int mtk_ecc_resume(struct device *dev)
++{
++ struct mtk_ecc *ecc = dev_get_drvdata(dev);
++ int ret;
++
++ ret = clk_prepare_enable(ecc->clk);
++ if (ret) {
++ dev_err(dev, "failed to enable clk\n");
++ return ret;
++ }
++
++ mtk_ecc_hw_init(ecc);
++
++ return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume);
++#endif
++
++static const struct of_device_id mtk_ecc_dt_match[] = {
++ { .compatible = "mediatek,mt2701-ecc" },
++ {},
++};
++
++MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match);
++
++static struct platform_driver mtk_ecc_driver = {
++ .probe = mtk_ecc_probe,
++ .driver = {
++ .name = "mtk-ecc",
++ .of_match_table = of_match_ptr(mtk_ecc_dt_match),
++#ifdef CONFIG_PM_SLEEP
++ .pm = &mtk_ecc_pm_ops,
++#endif
++ },
++};
++
++module_platform_driver(mtk_ecc_driver);
++
++MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
++MODULE_AUTHOR("Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>");
++MODULE_DESCRIPTION("MTK Nand ECC Driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/mtk_ecc.h
+new file mode 100644
+index 0000000..434826f
+--- /dev/null
++++ b/drivers/mtd/nand/mtk_ecc.h
+@@ -0,0 +1,53 @@
++/*
++ * MTK SDG1 ECC controller
++ *
++ * Copyright (c) 2016 Mediatek
++ * Authors: Xiaolei Li <xiaolei.li@mediatek.com>
++ * Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation.
++ */
++
++#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__
++#define __DRIVERS_MTD_NAND_MTK_ECC_H__
++
++#include <linux/types.h>
++
++#define ECC_PARITY_BITS (14)
++
++enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1};
++enum mtk_ecc_codec {ECC_ENC, ECC_DEC};
++
++struct device_node;
++struct mtk_ecc;
++
++struct mtk_ecc_stats {
++ u32 corrected;
++ u32 bitflips;
++ u32 failed;
++};
++
++struct mtk_ecc_config {
++ enum mtk_ecc_mode ecc_mode;
++ enum mtk_ecc_codec codec;
++ dma_addr_t addr;
++ u32 sec_mask;
++ u32 strength;
++ u32 enc_len;
++ u32 dec_len;
++};
++
++int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *);
++void mtk_ecc_disable(struct mtk_ecc *, struct mtk_ecc_config *);
++int mtk_ecc_encode_non_nfi_mode(struct mtk_ecc *, struct mtk_ecc_config *,
++ u8 *, u32);
++void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int);
++int mtk_ecc_wait_irq_done(struct mtk_ecc *, enum mtk_ecc_codec);
++void mtk_ecc_hw_init(struct mtk_ecc *);
++void mtk_ecc_update_strength(u32 *);
++
++struct mtk_ecc *of_mtk_ecc_get(struct device_node *);
++void mtk_ecc_release(struct mtk_ecc *);
++
++#endif
+diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c
+new file mode 100644
+index 0000000..907b90c
+--- /dev/null
++++ b/drivers/mtd/nand/mtk_nand.c
+@@ -0,0 +1,1432 @@
++/*
++ * MTK NAND Flash controller driver.
++ * Copyright (C) 2016 MediaTek Inc.
++ * Authors: Xiaolei Li <xiaolei.li@mediatek.com>
++ * Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/clk.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/mtd.h>
++#include <linux/module.h>
++#include <linux/iopoll.h>
++#include <linux/of.h>
++#include "mtk_ecc.h"
++
++/* NAND controller register definition */
++#define NFI_CNFG (0x00)
++#define CNFG_AHB BIT(0)
++#define CNFG_READ_EN BIT(1)
++#define CNFG_DMA_BURST_EN BIT(2)
++#define CNFG_BYTE_RW BIT(6)
++#define CNFG_HW_ECC_EN BIT(8)
++#define CNFG_AUTO_FMT_EN BIT(9)
++#define CNFG_OP_CUST (6 << 12)
++#define NFI_PAGEFMT (0x04)
++#define PAGEFMT_FDM_ECC_SHIFT (12)
++#define PAGEFMT_FDM_SHIFT (8)
++#define PAGEFMT_SPARE_16 (0)
++#define PAGEFMT_SPARE_26 (1)
++#define PAGEFMT_SPARE_27 (2)
++#define PAGEFMT_SPARE_28 (3)
++#define PAGEFMT_SPARE_32 (4)
++#define PAGEFMT_SPARE_36 (5)
++#define PAGEFMT_SPARE_40 (6)
++#define PAGEFMT_SPARE_44 (7)
++#define PAGEFMT_SPARE_48 (8)
++#define PAGEFMT_SPARE_49 (9)
++#define PAGEFMT_SPARE_50 (0xa)
++#define PAGEFMT_SPARE_51 (0xb)
++#define PAGEFMT_SPARE_52 (0xc)
++#define PAGEFMT_SPARE_62 (0xd)
++#define PAGEFMT_SPARE_63 (0xe)
++#define PAGEFMT_SPARE_64 (0xf)
++#define PAGEFMT_SPARE_SHIFT (4)
++#define PAGEFMT_SEC_SEL_512 BIT(2)
++#define PAGEFMT_512_2K (0)
++#define PAGEFMT_2K_4K (1)
++#define PAGEFMT_4K_8K (2)
++#define PAGEFMT_8K_16K (3)
++/* NFI control */
++#define NFI_CON (0x08)
++#define CON_FIFO_FLUSH BIT(0)
++#define CON_NFI_RST BIT(1)
++#define CON_BRD BIT(8) /* burst read */
++#define CON_BWR BIT(9) /* burst write */
++#define CON_SEC_SHIFT (12)
++/* Timming control register */
++#define NFI_ACCCON (0x0C)
++#define NFI_INTR_EN (0x10)
++#define INTR_AHB_DONE_EN BIT(6)
++#define NFI_INTR_STA (0x14)
++#define NFI_CMD (0x20)
++#define NFI_ADDRNOB (0x30)
++#define NFI_COLADDR (0x34)
++#define NFI_ROWADDR (0x38)
++#define NFI_STRDATA (0x40)
++#define STAR_EN (1)
++#define STAR_DE (0)
++#define NFI_CNRNB (0x44)
++#define NFI_DATAW (0x50)
++#define NFI_DATAR (0x54)
++#define NFI_PIO_DIRDY (0x58)
++#define PIO_DI_RDY (0x01)
++#define NFI_STA (0x60)
++#define STA_CMD BIT(0)
++#define STA_ADDR BIT(1)
++#define STA_BUSY BIT(8)
++#define STA_EMP_PAGE BIT(12)
++#define NFI_FSM_CUSTDATA (0xe << 16)
++#define NFI_FSM_MASK (0xf << 16)
++#define NFI_ADDRCNTR (0x70)
++#define CNTR_MASK GENMASK(16, 12)
++#define NFI_STRADDR (0x80)
++#define NFI_BYTELEN (0x84)
++#define NFI_CSEL (0x90)
++#define NFI_FDML(x) (0xA0 + (x) * sizeof(u32) * 2)
++#define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2)
++#define NFI_FDM_MAX_SIZE (8)
++#define NFI_MASTER_STA (0x224)
++#define MASTER_STA_MASK (0x0FFF)
++#define NFI_EMPTY_THRESH (0x23C)
++
++#define MTK_NAME "mtk-nand"
++#define KB(x) ((x) * 1024UL)
++#define MB(x) (KB(x) * 1024UL)
++
++#define MTK_TIMEOUT (500000)
++#define MTK_RESET_TIMEOUT (1000000)
++#define MTK_MAX_SECTOR (16)
++#define MTK_NAND_MAX_NSELS (2)
++
++typedef void (*bad_mark_swap)(struct mtd_info *, uint8_t *buf, int raw);
++struct mtk_nfc_bad_mark_ctl {
++ bad_mark_swap bm_swap;
++ u32 sec;
++ u32 pos;
++};
++
++/*
++ * FDM: region used to store free OOB data
++ */
++struct mtk_nfc_fdm {
++ u32 reg_size;
++ u32 ecc_size;
++};
++
++struct mtk_nfc_nand_chip {
++ struct list_head node;
++ struct nand_chip nand;
++
++ struct mtk_nfc_bad_mark_ctl bad_mark;
++ struct mtk_nfc_fdm fdm;
++ u32 spare_per_sector;
++
++ int nsels;
++ u8 sels[0];
++ /* nothing after this field */
++};
++
++struct mtk_nfc_clk {
++ struct clk *nfi_clk;
++ struct clk *pad_clk;
++};
++
++struct mtk_nfc {
++ struct nand_hw_control controller;
++ struct mtk_ecc_config ecc_cfg;
++ struct mtk_nfc_clk clk;
++ struct mtk_ecc *ecc;
++
++ struct device *dev;
++ void __iomem *regs;
++
++ struct completion done;
++ struct list_head chips;
++
++ u8 *buffer;
++};
++
++static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand)
++{
++ return container_of(nand, struct mtk_nfc_nand_chip, nand);
++}
++
++static inline uint8_t *data_ptr(struct nand_chip *chip, const uint8_t *p, int i)
++{
++ return (uint8_t *) p + i * chip->ecc.size;
++}
++
++static inline uint8_t *oob_ptr(struct nand_chip *chip, int i)
++{
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ uint8_t *poi;
++
++ if (i < mtk_nand->bad_mark.sec)
++ poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size;
++ else if (i == mtk_nand->bad_mark.sec)
++ poi = chip->oob_poi;
++ else
++ poi = chip->oob_poi + i * mtk_nand->fdm.reg_size;
++
++ return poi;
++}
++
++static inline int mtk_data_len(struct nand_chip *chip)
++{
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++
++ return chip->ecc.size + mtk_nand->spare_per_sector;
++}
++
++static inline uint8_t *mtk_data_ptr(struct nand_chip *chip, int i)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++
++ return nfc->buffer + i * mtk_data_len(chip);
++}
++
++static inline uint8_t *mtk_oob_ptr(struct nand_chip *chip, int i)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++
++ return nfc->buffer + i * mtk_data_len(chip) + chip->ecc.size;
++}
++
++static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg)
++{
++ writel(val, nfc->regs + reg);
++}
++
++static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg)
++{
++ writew(val, nfc->regs + reg);
++}
++
++static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg)
++{
++ writeb(val, nfc->regs + reg);
++}
++
++static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg)
++{
++ return readl_relaxed(nfc->regs + reg);
++}
++
++static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg)
++{
++ return readw_relaxed(nfc->regs + reg);
++}
++
++static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg)
++{
++ return readb_relaxed(nfc->regs + reg);
++}
++
++static void mtk_nfc_hw_reset(struct mtk_nfc *nfc)
++{
++ struct device *dev = nfc->dev;
++ u32 val;
++ int ret;
++
++ /* reset all registers and force the NFI master to terminate */
++ nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
++
++ /* wait for the master to finish the last transaction */
++ ret = readl_poll_timeout(nfc->regs + NFI_MASTER_STA, val,
++ !(val & MASTER_STA_MASK), 50, MTK_RESET_TIMEOUT);
++ if (ret)
++ dev_warn(dev, "master active in reset [0x%x] = 0x%x\n",
++ NFI_MASTER_STA, val);
++
++ /* ensure any status register affected by the NFI master is reset */
++ nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
++ nfi_writew(nfc, STAR_DE, NFI_STRDATA);
++}
++
++static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command)
++{
++ struct device *dev = nfc->dev;
++ u32 val;
++ int ret;
++
++ nfi_writel(nfc, command, NFI_CMD);
++
++ ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val,
++ !(val & STA_CMD), 10, MTK_TIMEOUT);
++ if (ret) {
++ dev_warn(dev, "nfi core timed out entering command mode\n");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr)
++{
++ struct device *dev = nfc->dev;
++ u32 val;
++ int ret;
++
++ nfi_writel(nfc, addr, NFI_COLADDR);
++ nfi_writel(nfc, 0, NFI_ROWADDR);
++ nfi_writew(nfc, 1, NFI_ADDRNOB);
++
++ ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val,
++ !(val & STA_ADDR), 10, MTK_TIMEOUT);
++ if (ret) {
++ dev_warn(dev, "nfi core timed out entering address mode\n");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ u32 fmt, spare;
++
++ if (!mtd->writesize)
++ return 0;
++
++ spare = mtk_nand->spare_per_sector;
++
++ switch (mtd->writesize) {
++ case 512:
++ fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512;
++ break;
++ case KB(2):
++ if (chip->ecc.size == 512)
++ fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512;
++ else
++ fmt = PAGEFMT_512_2K;
++ break;
++ case KB(4):
++ if (chip->ecc.size == 512)
++ fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512;
++ else
++ fmt = PAGEFMT_2K_4K;
++ break;
++ case KB(8):
++ if (chip->ecc.size == 512)
++ fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512;
++ else
++ fmt = PAGEFMT_4K_8K;
++ break;
++ case KB(16):
++ fmt = PAGEFMT_8K_16K;
++ break;
++ default:
++ dev_err(nfc->dev, "invalid page len: %d\n", mtd->writesize);
++ return -EINVAL;
++ }
++
++ /* the hardware doubles the value for this eccsize so let's halve it */
++ if (chip->ecc.size == 1024)
++ spare >>= 1;
++
++ switch (spare) {
++ case 16:
++ fmt |= (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 26:
++ fmt |= (PAGEFMT_SPARE_26 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 27:
++ fmt |= (PAGEFMT_SPARE_27 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 28:
++ fmt |= (PAGEFMT_SPARE_28 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 32:
++ fmt |= (PAGEFMT_SPARE_32 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 36:
++ fmt |= (PAGEFMT_SPARE_36 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 40:
++ fmt |= (PAGEFMT_SPARE_40 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 44:
++ fmt |= (PAGEFMT_SPARE_44 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 48:
++ fmt |= (PAGEFMT_SPARE_48 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 49:
++ fmt |= (PAGEFMT_SPARE_49 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 50:
++ fmt |= (PAGEFMT_SPARE_50 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 51:
++ fmt |= (PAGEFMT_SPARE_51 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 52:
++ fmt |= (PAGEFMT_SPARE_52 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 62:
++ fmt |= (PAGEFMT_SPARE_62 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 63:
++ fmt |= (PAGEFMT_SPARE_63 << PAGEFMT_SPARE_SHIFT);
++ break;
++ case 64:
++ fmt |= (PAGEFMT_SPARE_64 << PAGEFMT_SPARE_SHIFT);
++ break;
++ default:
++ dev_err(nfc->dev, "invalid spare per sector %d\n", spare);
++ return -EINVAL;
++ }
++
++ fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT;
++ fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT;
++ nfi_writew(nfc, fmt, NFI_PAGEFMT);
++
++ nfc->ecc_cfg.strength = chip->ecc.strength;
++ nfc->ecc_cfg.enc_len = chip->ecc.size + mtk_nand->fdm.ecc_size;
++ nfc->ecc_cfg.dec_len = (nfc->ecc_cfg.enc_len << 3)
++ + chip->ecc.strength * ECC_PARITY_BITS;
++
++ return 0;
++}
++
++static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip)
++{
++ struct nand_chip *nand = mtd_to_nand(mtd);
++ struct mtk_nfc *nfc = nand_get_controller_data(nand);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand);
++
++ if (chip < 0)
++ return;
++
++ mtk_nfc_hw_runtime_config(mtd);
++
++ nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL);
++}
++
++static int mtk_nfc_dev_ready(struct mtd_info *mtd)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
++
++ if (nfi_readl(nfc, NFI_STA) & STA_BUSY)
++ return 0;
++
++ return 1;
++}
++
++static void mtk_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
++
++ if (ctrl & NAND_ALE)
++ mtk_nfc_send_address(nfc, dat);
++ else if (ctrl & NAND_CLE) {
++ mtk_nfc_hw_reset(nfc);
++
++ nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG);
++ mtk_nfc_send_command(nfc, dat);
++ }
++}
++
++static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc)
++{
++ int rc;
++ u8 val;
++
++ rc = readb_poll_timeout_atomic(nfc->regs + NFI_PIO_DIRDY, val,
++ val & PIO_DI_RDY, 10, MTK_TIMEOUT);
++ if (rc < 0)
++ dev_err(nfc->dev, "data not ready\n");
++}
++
++static inline uint8_t mtk_nfc_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ u32 reg;
++
++ reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
++ if (reg != NFI_FSM_CUSTDATA) {
++ reg = nfi_readw(nfc, NFI_CNFG);
++ reg |= CNFG_BYTE_RW | CNFG_READ_EN;
++ nfi_writew(nfc, reg, NFI_CNFG);
++
++ reg = (MTK_MAX_SECTOR << CON_SEC_SHIFT) | CON_BRD;
++ nfi_writel(nfc, reg, NFI_CON);
++
++ /* trigger to fetch data */
++ nfi_writew(nfc, STAR_EN, NFI_STRDATA);
++ }
++
++ mtk_nfc_wait_ioready(nfc);
++
++ return nfi_readb(nfc, NFI_DATAR);
++}
++
++static void mtk_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++ int i;
++
++ for (i = 0; i < len; i++)
++ buf[i] = mtk_nfc_read_byte(mtd);
++}
++
++static void mtk_nfc_write_byte(struct mtd_info *mtd, uint8_t byte)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
++ u32 reg;
++
++ reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
++
++ if (reg != NFI_FSM_CUSTDATA) {
++ reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW;
++ nfi_writew(nfc, reg, NFI_CNFG);
++
++ reg = MTK_MAX_SECTOR << CON_SEC_SHIFT | CON_BWR;
++ nfi_writel(nfc, reg, NFI_CON);
++
++ nfi_writew(nfc, STAR_EN, NFI_STRDATA);
++ }
++
++ mtk_nfc_wait_ioready(nfc);
++ nfi_writeb(nfc, byte, NFI_DATAW);
++}
++
++static void mtk_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
++{
++ int i;
++
++ for (i = 0; i < len; i++)
++ mtk_nfc_write_byte(mtd, buf[i]);
++}
++
++static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ int size = chip->ecc.size + mtk_nand->fdm.reg_size;
++
++ nfc->ecc_cfg.ecc_mode = ECC_DMA_MODE;
++ nfc->ecc_cfg.codec = ECC_ENC;
++ return mtk_ecc_encode_non_nfi_mode(nfc->ecc, &nfc->ecc_cfg, data, size);
++}
++
++static void mtk_nfc_no_bad_mark_swap(struct mtd_info *a, uint8_t *b, int c)
++{
++ /* nope */
++}
++
++static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, uint8_t *buf, int raw)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip);
++ u32 bad_pos = nand->bad_mark.pos;
++
++ if (raw)
++ bad_pos += nand->bad_mark.sec * mtk_data_len(chip);
++ else
++ bad_pos += nand->bad_mark.sec * chip->ecc.size;
++
++ swap(chip->oob_poi[0], buf[bad_pos]);
++}
++
++static int mtk_nfc_format_subpage(struct mtd_info *mtd, uint32_t offset,
++ uint32_t len, const uint8_t *buf)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
++ u32 start, end;
++ int i, ret;
++
++ start = offset / chip->ecc.size;
++ end = DIV_ROUND_UP(offset + len, chip->ecc.size);
++
++ memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
++ for (i = 0; i < chip->ecc.steps; i++) {
++
++ memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i),
++ chip->ecc.size);
++
++ if (start > i || i >= end)
++ continue;
++
++ if (i == mtk_nand->bad_mark.sec)
++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
++
++ memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
++
++ /* program the CRC back to the OOB */
++ ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i));
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static void mtk_nfc_format_page(struct mtd_info *mtd, const uint8_t *buf)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
++ u32 i;
++
++ memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
++ for (i = 0; i < chip->ecc.steps; i++) {
++ if (buf)
++ memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i),
++ chip->ecc.size);
++
++ if (i == mtk_nand->bad_mark.sec)
++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
++
++ memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
++ }
++}
++
++static inline void mtk_nfc_read_fdm(struct nand_chip *chip, u32 start,
++ u32 sectors)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ u32 *p;
++ int i;
++
++ for (i = 0; i < sectors; i++) {
++ p = (u32 *) oob_ptr(chip, start + i);
++ p[0] = nfi_readl(nfc, NFI_FDML(i));
++ p[1] = nfi_readl(nfc, NFI_FDMM(i));
++ }
++}
++
++static inline void mtk_nfc_write_fdm(struct nand_chip *chip)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ u32 *p;
++ int i;
++
++ for (i = 0; i < chip->ecc.steps ; i++) {
++ p = (u32 *) oob_ptr(chip, i);
++ nfi_writel(nfc, p[0], NFI_FDML(i));
++ nfi_writel(nfc, p[1], NFI_FDMM(i));
++ }
++}
++
++static int mtk_nfc_do_write_page(struct mtd_info *mtd, struct nand_chip *chip,
++ const uint8_t *buf, int page, int len)
++{
++
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct device *dev = nfc->dev;
++ dma_addr_t addr;
++ u32 reg;
++ int ret;
++
++ addr = dma_map_single(dev, (void *) buf, len, DMA_TO_DEVICE);
++ ret = dma_mapping_error(nfc->dev, addr);
++ if (ret) {
++ dev_err(nfc->dev, "dma mapping error\n");
++ return -EINVAL;
++ }
++
++ reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN;
++ nfi_writew(nfc, reg, NFI_CNFG);
++
++ nfi_writel(nfc, chip->ecc.steps << CON_SEC_SHIFT, NFI_CON);
++ nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR);
++ nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
++
++ init_completion(&nfc->done);
++
++ reg = nfi_readl(nfc, NFI_CON) | CON_BWR;
++ nfi_writel(nfc, reg, NFI_CON);
++ nfi_writew(nfc, STAR_EN, NFI_STRDATA);
++
++ ret = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500));
++ if (!ret) {
++ dev_err(dev, "program ahb done timeout\n");
++ nfi_writew(nfc, 0, NFI_INTR_EN);
++ ret = -ETIMEDOUT;
++ goto timeout;
++ }
++
++ ret = readl_poll_timeout_atomic(nfc->regs + NFI_ADDRCNTR, reg,
++ (reg & CNTR_MASK) >= chip->ecc.steps, 10, MTK_TIMEOUT);
++ if (ret)
++ dev_err(dev, "hwecc write timeout\n");
++
++timeout:
++
++ dma_unmap_single(nfc->dev, addr, len, DMA_TO_DEVICE);
++ nfi_writel(nfc, 0, NFI_CON);
++
++ return ret;
++}
++
++static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
++ const uint8_t *buf, int page, int raw)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ size_t len;
++ const u8 *bufpoi;
++ u32 reg;
++ int ret;
++
++ if (!raw) {
++ /* OOB => FDM: from register, ECC: from HW */
++ reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN;
++ nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG);
++
++ nfc->ecc_cfg.codec = ECC_ENC;
++ nfc->ecc_cfg.ecc_mode = ECC_NFI_MODE;
++ ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg);
++ if (ret) {
++ /* clear NFI config */
++ reg = nfi_readw(nfc, NFI_CNFG);
++ reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
++ nfi_writew(nfc, reg, NFI_CNFG);
++
++ return ret;
++ }
++
++ memcpy(nfc->buffer, buf, mtd->writesize);
++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, raw);
++ bufpoi = nfc->buffer;
++
++ /* write OOB into the FDM registers (OOB area in MTK NAND) */
++ mtk_nfc_write_fdm(chip);
++ } else
++ bufpoi = buf;
++
++ len = mtd->writesize + (raw ? mtd->oobsize : 0);
++ ret = mtk_nfc_do_write_page(mtd, chip, bufpoi, page, len);
++
++ if (!raw)
++ mtk_ecc_disable(nfc->ecc, &nfc->ecc_cfg);
++
++ return ret;
++}
++
++static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd,
++ struct nand_chip *chip, const uint8_t *buf, int oob_on, int page)
++{
++ return mtk_nfc_write_page(mtd, chip, buf, page, 0);
++}
++
++static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++ const uint8_t *buf, int oob_on, int pg)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++
++ mtk_nfc_format_page(mtd, buf);
++ return mtk_nfc_write_page(mtd, chip, nfc->buffer, pg, 1);
++}
++
++static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd,
++ struct nand_chip *chip, uint32_t offset, uint32_t data_len,
++ const uint8_t *buf, int oob_on, int page)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ int ret;
++
++ ret = mtk_nfc_format_subpage(mtd, offset, data_len, buf);
++ if (ret < 0)
++ return ret;
++
++ /* use the data in the private buffer (now with FDM and CRC) */
++ return mtk_nfc_write_page(mtd, chip, nfc->buffer, page, 1);
++}
++
++static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ int ret;
++
++ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
++
++ ret = mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page);
++ if (ret < 0)
++ return -EIO;
++
++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++ ret = chip->waitfunc(mtd, chip);
++
++ return ret & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ struct mtk_ecc_stats stats;
++ int rc, i;
++
++ rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE;
++ if (rc) {
++ memset(buf, 0xff, sectors * chip->ecc.size);
++ for (i = 0; i < sectors; i++)
++ memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size);
++ return 0;
++ }
++
++ mtk_ecc_get_stats(nfc->ecc, &stats, sectors);
++ mtd->ecc_stats.corrected += stats.corrected;
++ mtd->ecc_stats.failed += stats.failed;
++
++ return stats.bitflips;
++}
++
++static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
++ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
++ int page, int raw)
++{
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ u32 spare = mtk_nand->spare_per_sector;
++ u32 column, sectors, start, end, reg;
++ dma_addr_t addr;
++ int bitflips;
++ size_t len;
++ u8 *buf;
++ int rc;
++
++ start = data_offs / chip->ecc.size;
++ end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
++
++ sectors = end - start;
++ column = start * (chip->ecc.size + spare);
++
++ len = sectors * chip->ecc.size + (raw ? sectors * spare : 0);
++ buf = bufpoi + start * chip->ecc.size;
++
++ if (column != 0)
++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1);
++
++ addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
++ rc = dma_mapping_error(nfc->dev, addr);
++ if (rc) {
++ dev_err(nfc->dev, "dma mapping error\n");
++
++ return -EINVAL;
++ }
++
++ reg = nfi_readw(nfc, NFI_CNFG);
++ reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_AHB;
++ if (!raw) {
++ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
++ nfi_writew(nfc, reg, NFI_CNFG);
++
++ nfc->ecc_cfg.ecc_mode = ECC_NFI_MODE;
++ nfc->ecc_cfg.sec_mask = sectors;
++ nfc->ecc_cfg.codec = ECC_DEC;
++ rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg);
++ if (rc) {
++ dev_err(nfc->dev, "ecc enable\n");
++ /* clear NFI_CNFG */
++ reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN |
++ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
++ nfi_writew(nfc, reg, NFI_CNFG);
++ dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
++
++ return rc;
++ }
++ } else
++ nfi_writew(nfc, reg, NFI_CNFG);
++
++ nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON);
++ nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
++ nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR);
++
++ init_completion(&nfc->done);
++ reg = nfi_readl(nfc, NFI_CON) | CON_BRD;
++ nfi_writel(nfc, reg, NFI_CON);
++ nfi_writew(nfc, STAR_EN, NFI_STRDATA);
++
++ rc = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500));
++ if (!rc)
++ dev_warn(nfc->dev, "read ahb/dma done timeout\n");
++
++ rc = readl_poll_timeout_atomic(nfc->regs + NFI_BYTELEN, reg,
++ (reg & CNTR_MASK) >= sectors, 10, MTK_TIMEOUT);
++ if (rc < 0) {
++ dev_err(nfc->dev, "subpage done timeout\n");
++ bitflips = -EIO;
++ } else {
++ bitflips = 0;
++ if (!raw) {
++ rc = mtk_ecc_wait_irq_done(nfc->ecc, ECC_DEC);
++ bitflips = rc < 0 ? -ETIMEDOUT :
++ mtk_nfc_update_ecc_stats(mtd, buf, sectors);
++ mtk_nfc_read_fdm(chip, start, sectors);
++ }
++ }
++
++ dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
++
++ if (raw)
++ goto done;
++
++ mtk_ecc_disable(nfc->ecc, &nfc->ecc_cfg);
++
++ if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec)
++ mtk_nand->bad_mark.bm_swap(mtd, bufpoi, raw);
++done:
++ nfi_writel(nfc, 0, NFI_CON);
++
++ return bitflips;
++}
++
++static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd,
++ struct nand_chip *chip, uint32_t off, uint32_t len, uint8_t *p, int pg)
++{
++ return mtk_nfc_read_subpage(mtd, chip, off, len, p, pg, 0);
++}
++
++static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd,
++ struct nand_chip *chip, uint8_t *p, int oob_on, int pg)
++{
++ return mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, p, pg, 0);
++}
++
++static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++ uint8_t *buf, int oob_on, int page)
++{
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ struct mtk_nfc *nfc = nand_get_controller_data(chip);
++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
++ int i, ret;
++
++ memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
++ ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, nfc->buffer,
++ page, 1);
++ if (ret < 0)
++ return ret;
++
++ for (i = 0; i < chip->ecc.steps; i++) {
++ memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
++ if (i == mtk_nand->bad_mark.sec)
++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
++
++ if (buf)
++ memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i),
++ chip->ecc.size);
++ }
++
++ return ret;
++}
++
++static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
++
++ return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page);
++}
++
++static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc)
++{
++ nfi_writel(nfc, 0x10804211, NFI_ACCCON);
++ nfi_writew(nfc, 0xf1, NFI_CNRNB);
++ nfi_writew(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT);
++
++ mtk_nfc_hw_reset(nfc);
++
++ nfi_readl(nfc, NFI_INTR_STA);
++ nfi_writel(nfc, 0, NFI_INTR_EN);
++}
++
++static irqreturn_t mtk_nfc_irq(int irq, void *id)
++{
++ struct mtk_nfc *nfc = id;
++ u16 sta, ien;
++
++ sta = nfi_readw(nfc, NFI_INTR_STA);
++ ien = nfi_readw(nfc, NFI_INTR_EN);
++
++ if (!(sta & ien))
++ return IRQ_NONE;
++
++ nfi_writew(nfc, ~sta & ien, NFI_INTR_EN);
++ complete(&nfc->done);
++
++ return IRQ_HANDLED;
++}
++
++static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk)
++{
++ int ret;
++
++ ret = clk_prepare_enable(clk->nfi_clk);
++ if (ret) {
++ dev_err(dev, "failed to enable nfi clk\n");
++ return ret;
++ }
++
++ ret = clk_prepare_enable(clk->pad_clk);
++ if (ret) {
++ dev_err(dev, "failed to enable pad clk\n");
++ clk_disable_unprepare(clk->nfi_clk);
++ return ret;
++ }
++
++ return 0;
++}
++
++static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk)
++{
++ clk_disable_unprepare(clk->nfi_clk);
++ clk_disable_unprepare(clk->pad_clk);
++}
++
++static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oob_region)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
++ u32 eccsteps;
++
++ eccsteps = mtd->writesize / chip->ecc.size;
++
++ if (section >= eccsteps)
++ return -ERANGE;
++
++ oob_region->length = fdm->reg_size - fdm->ecc_size;
++ oob_region->offset = section * fdm->reg_size + fdm->ecc_size;
++
++ return 0;
++}
++
++static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *oob_region)
++{
++ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
++ u32 eccsteps;
++
++ if (section)
++ return -ERANGE;
++
++ eccsteps = mtd->writesize / chip->ecc.size;
++ oob_region->offset = mtk_nand->fdm.reg_size * eccsteps;
++ oob_region->length = mtd->oobsize - oob_region->offset;
++
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = {
++ .free = mtk_nfc_ooblayout_free,
++ .ecc = mtk_nfc_ooblayout_ecc,
++};
++
++static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtd_info *mtd)
++{
++ struct nand_chip *nand = mtd_to_nand(mtd);
++ struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand);
++ u32 ecc_bytes;
++
++ ecc_bytes = DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8);
++
++ fdm->reg_size = chip->spare_per_sector - ecc_bytes;
++ if (fdm->reg_size > NFI_FDM_MAX_SIZE)
++ fdm->reg_size = NFI_FDM_MAX_SIZE;
++
++ /* bad block mark storage */
++ fdm->ecc_size = 1;
++}
++
++static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl,
++ struct mtd_info *mtd)
++{
++ struct nand_chip *nand = mtd_to_nand(mtd);
++
++ if (mtd->writesize == 512)
++ bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap;
++ else {
++ bm_ctl->bm_swap = mtk_nfc_bad_mark_swap;
++ bm_ctl->sec = mtd->writesize / mtk_data_len(nand);
++ bm_ctl->pos = mtd->writesize % mtk_data_len(nand);
++ }
++}
++
++static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd)
++{
++ struct nand_chip *nand = mtd_to_nand(mtd);
++ u32 spare[] = {16, 26, 27, 28, 32, 36, 40, 44,
++ 48, 49, 50, 51, 52, 62, 63, 64};
++ u32 eccsteps, i;
++
++ eccsteps = mtd->writesize / nand->ecc.size;
++ *sps = mtd->oobsize / eccsteps;
++
++ if (nand->ecc.size == 1024)
++ *sps >>= 1;
++
++ for (i = 0; i < ARRAY_SIZE(spare); i++) {
++ if (*sps <= spare[i]) {
++ if (!i)
++ *sps = spare[i];
++ else if (*sps != spare[i])
++ *sps = spare[i - 1];
++ break;
++ }
++ }
++
++ if (i >= ARRAY_SIZE(spare))
++ *sps = spare[ARRAY_SIZE(spare) - 1];
++
++ if (nand->ecc.size == 1024)
++ *sps <<= 1;
++}
++
++static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
++{
++ struct nand_chip *nand = mtd_to_nand(mtd);
++ u32 spare;
++
++ /* support only ecc hw mode */
++ if (nand->ecc.mode != NAND_ECC_HW) {
++ dev_err(dev, "ecc.mode not supported\n");
++ return -EINVAL;
++ }
++
++ /* if optional DT settings are not present */
++ if (!nand->ecc.size || !nand->ecc.strength) {
++
++ /* controller only supports sizes 512 and 1024 */
++ nand->ecc.size = (mtd->writesize > 512) ? 1024 : 512;
++
++ /* get controller valid values */
++ mtk_nfc_set_spare_per_sector(&spare, mtd);
++ spare = spare - NFI_FDM_MAX_SIZE;
++ nand->ecc.strength = (spare << 3) / ECC_PARITY_BITS;
++ }
++
++ mtk_ecc_update_strength(&nand->ecc.strength);
++
++ dev_info(dev, "eccsize %d eccstrength %d\n",
++ nand->ecc.size, nand->ecc.strength);
++
++ return 0;
++}
++
++static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
++ struct device_node *np)
++{
++ struct mtk_nfc_nand_chip *chip;
++ struct nand_chip *nand;
++ struct mtd_info *mtd;
++ int nsels, len;
++ u32 tmp;
++ int ret;
++ int i;
++
++ if (!of_get_property(np, "reg", &nsels))
++ return -ENODEV;
++
++ nsels /= sizeof(u32);
++ if (!nsels || nsels > MTK_NAND_MAX_NSELS) {
++ dev_err(dev, "invalid reg property size %d\n", nsels);
++ return -EINVAL;
++ }
++
++ chip = devm_kzalloc(dev,
++ sizeof(*chip) + nsels * sizeof(u8), GFP_KERNEL);
++ if (!chip)
++ return -ENOMEM;
++
++ chip->nsels = nsels;
++ for (i = 0; i < nsels; i++) {
++ ret = of_property_read_u32_index(np, "reg", i, &tmp);
++ if (ret) {
++ dev_err(dev, "reg property failure : %d\n", ret);
++ return ret;
++ }
++ chip->sels[i] = tmp;
++ }
++
++ nand = &chip->nand;
++ nand->controller = &nfc->controller;
++
++ nand_set_flash_node(nand, np);
++ nand_set_controller_data(nand, nfc);
++
++ nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ;
++ nand->dev_ready = mtk_nfc_dev_ready;
++ nand->select_chip = mtk_nfc_select_chip;
++ nand->write_byte = mtk_nfc_write_byte;
++ nand->write_buf = mtk_nfc_write_buf;
++ nand->read_byte = mtk_nfc_read_byte;
++ nand->read_buf = mtk_nfc_read_buf;
++ nand->cmd_ctrl = mtk_nfc_cmd_ctrl;
++
++ /* set default mode in case dt entry is missing */
++ nand->ecc.mode = NAND_ECC_HW;
++
++ nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc;
++ nand->ecc.write_page_raw = mtk_nfc_write_page_raw;
++ nand->ecc.write_page = mtk_nfc_write_page_hwecc;
++ nand->ecc.write_oob_raw = mtk_nfc_write_oob_std;
++ nand->ecc.write_oob = mtk_nfc_write_oob_std;
++
++ nand->ecc.read_subpage = mtk_nfc_read_subpage_hwecc;
++ nand->ecc.read_page_raw = mtk_nfc_read_page_raw;
++ nand->ecc.read_page = mtk_nfc_read_page_hwecc;
++ nand->ecc.read_oob_raw = mtk_nfc_read_oob_std;
++ nand->ecc.read_oob = mtk_nfc_read_oob_std;
++
++ mtd = nand_to_mtd(nand);
++ mtd->owner = THIS_MODULE;
++ mtd->dev.parent = dev;
++ mtd->name = MTK_NAME;
++ mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops);
++
++ mtk_nfc_hw_init(nfc);
++
++ ret = nand_scan_ident(mtd, nsels, NULL);
++ if (ret)
++ return -ENODEV;
++
++ /* store bbt magic in page, cause OOB is not protected */
++ if (nand->bbt_options & NAND_BBT_USE_FLASH)
++ nand->bbt_options |= NAND_BBT_NO_OOB;
++
++ ret = mtk_nfc_ecc_init(dev, mtd);
++ if (ret)
++ return -EINVAL;
++
++ mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd);
++ mtk_nfc_set_fdm(&chip->fdm, mtd);
++ mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd);
++
++ len = mtd->writesize + mtd->oobsize;
++ nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
++ if (!nfc->buffer)
++ return -ENOMEM;
++
++ ret = nand_scan_tail(mtd);
++ if (ret)
++ return -ENODEV;
++
++ ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
++ if (ret) {
++ dev_err(dev, "mtd parse partition error\n");
++ nand_release(mtd);
++ return ret;
++ }
++
++ list_add_tail(&chip->node, &nfc->chips);
++
++ return 0;
++}
++
++static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc)
++{
++ struct device_node *np = dev->of_node;
++ struct device_node *nand_np;
++ int ret;
++
++ for_each_child_of_node(np, nand_np) {
++ ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np);
++ if (ret) {
++ of_node_put(nand_np);
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++static int mtk_nfc_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ struct mtk_nfc *nfc;
++ struct resource *res;
++ int ret, irq;
++
++ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
++ if (!nfc)
++ return -ENOMEM;
++
++ spin_lock_init(&nfc->controller.lock);
++ init_waitqueue_head(&nfc->controller.wq);
++ INIT_LIST_HEAD(&nfc->chips);
++
++ /* probe defer if not ready */
++ nfc->ecc = of_mtk_ecc_get(np);
++ if (IS_ERR(nfc->ecc))
++ return PTR_ERR(nfc->ecc);
++ else if (!nfc->ecc)
++ return -ENODEV;
++
++ nfc->dev = dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ nfc->regs = devm_ioremap_resource(dev, res);
++ if (IS_ERR(nfc->regs)) {
++ ret = PTR_ERR(nfc->regs);
++ dev_err(dev, "no nfi base\n");
++ goto release_ecc;
++ }
++
++ nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
++ if (IS_ERR(nfc->clk.nfi_clk)) {
++ dev_err(dev, "no clk\n");
++ ret = PTR_ERR(nfc->clk.nfi_clk);
++ goto release_ecc;
++ }
++
++ nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk");
++ if (IS_ERR(nfc->clk.pad_clk)) {
++ dev_err(dev, "no pad clk\n");
++ ret = PTR_ERR(nfc->clk.pad_clk);
++ goto release_ecc;
++ }
++
++ ret = mtk_nfc_enable_clk(dev, &nfc->clk);
++ if (ret)
++ goto release_ecc;
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ dev_err(dev, "no nfi irq resource\n");
++ ret = -EINVAL;
++ goto clk_disable;
++ }
++
++ ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc);
++ if (ret) {
++ dev_err(dev, "failed to request nfi irq\n");
++ goto clk_disable;
++ }
++
++ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
++ if (ret) {
++ dev_err(dev, "failed to set dma mask\n");
++ goto clk_disable;
++ }
++
++ platform_set_drvdata(pdev, nfc);
++
++ ret = mtk_nfc_nand_chips_init(dev, nfc);
++ if (ret) {
++ dev_err(dev, "failed to init nand chips\n");
++ goto clk_disable;
++ }
++
++ return 0;
++
++clk_disable:
++ mtk_nfc_disable_clk(&nfc->clk);
++
++release_ecc:
++ mtk_ecc_release(nfc->ecc);
++
++ return ret;
++}
++
++static int mtk_nfc_remove(struct platform_device *pdev)
++{
++ struct mtk_nfc *nfc = platform_get_drvdata(pdev);
++ struct mtk_nfc_nand_chip *chip;
++
++ while (!list_empty(&nfc->chips)) {
++ chip = list_first_entry(&nfc->chips, struct mtk_nfc_nand_chip,
++ node);
++ nand_release(nand_to_mtd(&chip->nand));
++ list_del(&chip->node);
++ }
++
++ mtk_ecc_release(nfc->ecc);
++ mtk_nfc_disable_clk(&nfc->clk);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int mtk_nfc_suspend(struct device *dev)
++{
++ struct mtk_nfc *nfc = dev_get_drvdata(dev);
++
++ mtk_nfc_disable_clk(&nfc->clk);
++
++ return 0;
++}
++
++static int mtk_nfc_resume(struct device *dev)
++{
++ struct mtk_nfc *nfc = dev_get_drvdata(dev);
++ struct mtk_nfc_nand_chip *chip;
++ struct nand_chip *nand;
++ struct mtd_info *mtd;
++ int ret;
++ u32 i;
++
++ udelay(200);
++
++ ret = mtk_nfc_enable_clk(dev, &nfc->clk);
++ if (ret)
++ return ret;
++
++ mtk_nfc_hw_init(nfc);
++
++ list_for_each_entry(chip, &nfc->chips, node) {
++ nand = &chip->nand;
++ mtd = nand_to_mtd(nand);
++ for (i = 0; i < chip->nsels; i++) {
++ nand->select_chip(mtd, i);
++ nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
++ }
++ }
++
++ return 0;
++}
++static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume);
++#endif
++
++static const struct of_device_id mtk_nfc_id_table[] = {
++ { .compatible = "mediatek,mt2701-nfc" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
++
++static struct platform_driver mtk_nfc_driver = {
++ .probe = mtk_nfc_probe,
++ .remove = mtk_nfc_remove,
++ .driver = {
++ .name = MTK_NAME,
++ .of_match_table = mtk_nfc_id_table,
++#ifdef CONFIG_PM_SLEEP
++ .pm = &mtk_nfc_pm_ops,
++#endif
++ },
++};
++
++module_platform_driver(mtk_nfc_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
++MODULE_AUTHOR("Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>");
++MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
+--
+1.7.10.4
+
+++ /dev/null
-From 506c56fe0c3986c13fbca474ee91b061fbc850ca Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 29 Mar 2016 17:20:01 +0200
-Subject: [PATCH 75/91] net: mediatek: fix TX locking
-
-Inside the TX path there is a lock inside the tx_map function. This is
-however too late. The patch moves the lock to the start of the xmit
-function right before the free count check of the DMA ring happens.
-If we do not do this, the code becomes racy leading to TX stalls and
-dropped packets. This happens as there are 2 netdevs running on the
-same physical DMA ring.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 20 ++++++++++----------
- 1 file changed, 10 insertions(+), 10 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -536,7 +536,6 @@ static int mtk_tx_map(struct sk_buff *sk
- struct mtk_eth *eth = mac->hw;
- struct mtk_tx_dma *itxd, *txd;
- struct mtk_tx_buf *tx_buf;
-- unsigned long flags;
- dma_addr_t mapped_addr;
- unsigned int nr_frags;
- int i, n_desc = 1;
-@@ -568,11 +567,6 @@ static int mtk_tx_map(struct sk_buff *sk
- if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
- return -ENOMEM;
-
-- /* normally we can rely on the stack not calling this more than once,
-- * however we have 2 queues running ont he same ring so we need to lock
-- * the ring access
-- */
-- spin_lock_irqsave(ð->page_lock, flags);
- WRITE_ONCE(itxd->txd1, mapped_addr);
- tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
- dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
-@@ -632,8 +626,6 @@ static int mtk_tx_map(struct sk_buff *sk
- WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
- (!nr_frags * TX_DMA_LS0)));
-
-- spin_unlock_irqrestore(ð->page_lock, flags);
--
- netdev_sent_queue(dev, skb->len);
- skb_tx_timestamp(skb);
-
-@@ -661,8 +653,6 @@ err_dma:
- itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
- } while (itxd != txd);
-
-- spin_unlock_irqrestore(ð->page_lock, flags);
--
- return -ENOMEM;
- }
-
-@@ -712,14 +702,22 @@ static int mtk_start_xmit(struct sk_buff
- struct mtk_eth *eth = mac->hw;
- struct mtk_tx_ring *ring = ð->tx_ring;
- struct net_device_stats *stats = &dev->stats;
-+ unsigned long flags;
- bool gso = false;
- int tx_num;
-
-+ /* normally we can rely on the stack not calling this more than once,
-+ * however we have 2 queues running ont he same ring so we need to lock
-+ * the ring access
-+ */
-+ spin_lock_irqsave(ð->page_lock, flags);
-+
- tx_num = mtk_cal_txd_req(skb);
- if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
- mtk_stop_queue(eth);
- netif_err(eth, tx_queued, dev,
- "Tx Ring full when queue awake!\n");
-+ spin_unlock_irqrestore(ð->page_lock, flags);
- return NETDEV_TX_BUSY;
- }
-
-@@ -747,10 +745,12 @@ static int mtk_start_xmit(struct sk_buff
- ring->thresh))
- mtk_wake_queue(eth);
- }
-+ spin_unlock_irqrestore(ð->page_lock, flags);
-
- return NETDEV_TX_OK;
-
- drop:
-+ spin_unlock_irqrestore(ð->page_lock, flags);
- stats->tx_dropped++;
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
--- /dev/null
+From 5dc0d474396e04e6c140d71f0e113eb1c03501c5 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 17 May 2016 05:44:10 +0200
+Subject: [PATCH 076/102] mtd: nand: add power domains to the mediatek driver
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/mtd/nand/mtk_nand.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c
+index 907b90c..bde1a1d 100644
+--- a/drivers/mtd/nand/mtk_nand.c
++++ b/drivers/mtd/nand/mtk_nand.c
+@@ -16,6 +16,7 @@
+
+ #include <linux/platform_device.h>
+ #include <linux/dma-mapping.h>
++#include <linux/pm_runtime.h>
+ #include <linux/interrupt.h>
+ #include <linux/delay.h>
+ #include <linux/clk.h>
+@@ -102,6 +103,7 @@
+ #define NFI_MASTER_STA (0x224)
+ #define MASTER_STA_MASK (0x0FFF)
+ #define NFI_EMPTY_THRESH (0x23C)
++#define NFI_ACCCON1 (0x244)
+
+ #define MTK_NAME "mtk-nand"
+ #define KB(x) ((x) * 1024UL)
+@@ -539,6 +541,8 @@ static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, uint8_t *buf, int raw)
+ struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip);
+ u32 bad_pos = nand->bad_mark.pos;
+
++ return;
++
+ if (raw)
+ bad_pos += nand->bad_mark.sec * mtk_data_len(chip);
+ else
+@@ -946,7 +950,8 @@ static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+
+ static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc)
+ {
+- nfi_writel(nfc, 0x10804211, NFI_ACCCON);
++ nfi_writel(nfc, 0x30c77fff, NFI_ACCCON);
++ nfi_writel(nfc, 0xC03222, NFI_ACCCON1);
+ nfi_writew(nfc, 0xf1, NFI_CNRNB);
+ nfi_writew(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT);
+
+@@ -1328,6 +1333,9 @@ static int mtk_nfc_probe(struct platform_device *pdev)
+ goto clk_disable;
+ }
+
++ pm_runtime_enable(dev);
++ pm_runtime_get_sync(dev);
++
+ platform_set_drvdata(pdev, nfc);
+
+ ret = mtk_nfc_nand_chips_init(dev, nfc);
+@@ -1362,6 +1370,9 @@ static int mtk_nfc_remove(struct platform_device *pdev)
+ mtk_ecc_release(nfc->ecc);
+ mtk_nfc_disable_clk(&nfc->clk);
+
++ pm_runtime_put_sync(&pdev->dev);
++ pm_runtime_disable(&pdev->dev);
++
+ return 0;
+ }
+
+--
+1.7.10.4
+
+++ /dev/null
-From d42de6ec9325c29d0f59c5df74a5cbceb00ddd9d Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 29 Mar 2016 17:24:24 +0200
-Subject: [PATCH 76/91] net: mediatek: move the pending_work struct to the
- device generic struct
-
-The worker always touches both netdevs. It is ethernet core and not MAC
-specific. We only need one worker, which belongs into the ethernets core struct.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 10 ++++------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++--
- 2 files changed, 6 insertions(+), 8 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1193,7 +1193,7 @@ static void mtk_tx_timeout(struct net_de
- eth->netdev[mac->id]->stats.tx_errors++;
- netif_err(eth, tx_err, dev,
- "transmit timed out\n");
-- schedule_work(&mac->pending_work);
-+ schedule_work(ð->pending_work);
- }
-
- static irqreturn_t mtk_handle_irq(int irq, void *_eth)
-@@ -1438,7 +1438,7 @@ static void mtk_pending_work(struct work
-
- /* stop all devices to make sure that dma is properly shut down */
- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- if (!netif_oper_up(eth->netdev[i]))
-+ if (!eth->netdev[i])
- continue;
- mtk_stop(eth->netdev[i]);
- __set_bit(i, &restart);
-@@ -1464,15 +1464,13 @@ static int mtk_cleanup(struct mtk_eth *e
- int i;
-
- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- struct mtk_mac *mac = netdev_priv(eth->netdev[i]);
--
- if (!eth->netdev[i])
- continue;
-
- unregister_netdev(eth->netdev[i]);
- free_netdev(eth->netdev[i]);
-- cancel_work_sync(&mac->pending_work);
- }
-+ cancel_work_sync(ð->pending_work);
-
- return 0;
- }
-@@ -1660,7 +1658,6 @@ static int mtk_add_mac(struct mtk_eth *e
- mac->id = id;
- mac->hw = eth;
- mac->of_node = np;
-- INIT_WORK(&mac->pending_work, mtk_pending_work);
-
- mac->hw_stats = devm_kzalloc(eth->dev,
- sizeof(*mac->hw_stats),
-@@ -1762,6 +1759,7 @@ static int mtk_probe(struct platform_dev
-
- eth->dev = &pdev->dev;
- eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
-+ INIT_WORK(ð->pending_work, mtk_pending_work);
-
- err = mtk_hw_init(eth);
- if (err)
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -363,6 +363,7 @@ struct mtk_rx_ring {
- * @clk_gp1: The gmac1 clock
- * @clk_gp2: The gmac2 clock
- * @mii_bus: If there is a bus we need to create an instance for it
-+ * @pending_work: The workqueue used to reset the dma ring
- */
-
- struct mtk_eth {
-@@ -389,6 +390,7 @@ struct mtk_eth {
- struct clk *clk_gp1;
- struct clk *clk_gp2;
- struct mii_bus *mii_bus;
-+ struct work_struct pending_work;
- };
-
- /* struct mtk_mac - the structure that holds the info about the MACs of the
-@@ -398,7 +400,6 @@ struct mtk_eth {
- * @hw: Backpointer to our main datastruture
- * @hw_stats: Packet statistics counter
- * @phy_dev: The attached PHY if available
-- * @pending_work: The workqueue used to reset the dma ring
- */
- struct mtk_mac {
- int id;
-@@ -406,7 +407,6 @@ struct mtk_mac {
- struct mtk_eth *hw;
- struct mtk_hw_stats *hw_stats;
- struct phy_device *phy_dev;
-- struct work_struct pending_work;
- };
-
- /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+++ /dev/null
-From 2675e2a40d78c55fc2d578ec71cc990170cacc42 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Thu, 7 Apr 2016 17:36:23 +0200
-Subject: [PATCH 77/91] net: mediatek: do not set the QID field in the TX DMA
- descriptors
-
-The QID field gets set to the mac id. This made the DMA linked list queue
-the traffic of each MAC on a different internal queue. However during long
-term testing we found that this will cause traffic stalls as the multi
-queue setup requires a more complete initialisation which is not part of
-the upstream driver yet.
-
-This patch removes the code setting the QID field, resulting in all
-traffic ending up in queue 0 which works without any special setup.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -603,8 +603,7 @@ static int mtk_tx_map(struct sk_buff *sk
- WRITE_ONCE(txd->txd1, mapped_addr);
- WRITE_ONCE(txd->txd3, (TX_DMA_SWC |
- TX_DMA_PLEN0(frag_map_size) |
-- last_frag * TX_DMA_LS0) |
-- mac->id);
-+ last_frag * TX_DMA_LS0));
- WRITE_ONCE(txd->txd4, 0);
-
- tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC;
--- /dev/null
+From b1c85818c3fb00022dc125bb62d657d3fd3cf49c Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Sat, 7 May 2016 06:31:08 +0200
+Subject: [PATCH 077/102] net-next: mediatek: use mdiobus_free() in favour of
+ kfree()
+
+The driver currently uses kfree() to clear the mii_bus. This is not the
+correct way to clear the memory and mdiobus_free() should be used instead.
+This patch fixes the two instances where this happens in the driver.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index bab5d45..0c8d369 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -280,7 +280,7 @@ static int mtk_mdio_init(struct mtk_eth *eth)
+ return 0;
+
+ err_free_bus:
+- kfree(eth->mii_bus);
++ mdiobus_free(eth->mii_bus);
+
+ err_put_node:
+ of_node_put(mii_np);
+@@ -295,7 +295,7 @@ static void mtk_mdio_cleanup(struct mtk_eth *eth)
+
+ mdiobus_unregister(eth->mii_bus);
+ of_node_put(eth->mii_bus->dev.of_node);
+- kfree(eth->mii_bus);
++ mdiobus_free(eth->mii_bus);
+ }
+
+ static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
+--
+1.7.10.4
+
+++ /dev/null
-From 289e6b23aa394126f50048f673ac266686bbf65e Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 29 Mar 2016 14:32:07 +0200
-Subject: [PATCH 78/91] net: mediatek: update the IRQ part of the binding
- document
-
-The current binding document only describes a single interrupt. Update the
-document by adding the 2 other interrupts.
-
-The driver currently only uses a single interrupt. The HW is however able
-to using IRQ grouping to split TX and RX onto separate GIC irqs.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
- Acked-by: Rob Herring <robh@kernel.org>
----
- Documentation/devicetree/bindings/net/mediatek-net.txt | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
---- a/Documentation/devicetree/bindings/net/mediatek-net.txt
-+++ b/Documentation/devicetree/bindings/net/mediatek-net.txt
-@@ -9,7 +9,8 @@ have dual GMAC each represented by a chi
- Required properties:
- - compatible: Should be "mediatek,mt7623-eth"
- - reg: Address and length of the register set for the device
--- interrupts: Should contain the frame engines interrupt
-+- interrupts: Should contain the three frame engines interrupts in numeric
-+ order. These are fe_int0, fe_int1 and fe_int2.
- - clocks: the clock used by the core
- - clock-names: the names of the clock listed in the clocks property. These are
- "ethif", "esw", "gp2", "gp1"
-@@ -42,7 +43,9 @@ eth: ethernet@1b100000 {
- <ðsys CLK_ETHSYS_GP2>,
- <ðsys CLK_ETHSYS_GP1>;
- clock-names = "ethif", "esw", "gp2", "gp1";
-- interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW>;
-+ interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW
-+ GIC_SPI 199 IRQ_TYPE_LEVEL_LOW
-+ GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>;
- power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
- resets = <ðsys MT2701_ETHSYS_ETH_RST>;
- reset-names = "eth";
--- /dev/null
+From 09313f26999e2685e0b9434374e7308e1f447e55 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 22 Apr 2016 11:05:23 +0200
+Subject: [PATCH 078/102] net-next: mediatek: fix gigabit and flow control
+ advertisement
+
+The current code will not setup the PHYs advertisement features correctly.
+Fix this and properly advertise Gigabit features and properly handle
+asymmetric pause frames.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 30 +++++++++++++++++++++++----
+ 1 file changed, 26 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 0c8d369..3436c7b 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -133,6 +133,8 @@ static int mtk_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+ static void mtk_phy_link_adjust(struct net_device *dev)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
++ u16 lcl_adv = 0, rmt_adv = 0;
++ u8 flowctrl;
+ u32 mcr = MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG |
+ MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN |
+ MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN |
+@@ -150,11 +152,30 @@ static void mtk_phy_link_adjust(struct net_device *dev)
+ if (mac->phy_dev->link)
+ mcr |= MAC_MCR_FORCE_LINK;
+
+- if (mac->phy_dev->duplex)
++ if (mac->phy_dev->duplex) {
+ mcr |= MAC_MCR_FORCE_DPX;
+
+- if (mac->phy_dev->pause)
+- mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC;
++ if (mac->phy_dev->pause)
++ rmt_adv = LPA_PAUSE_CAP;
++ if (mac->phy_dev->asym_pause)
++ rmt_adv |= LPA_PAUSE_ASYM;
++
++ if (mac->phy_dev->advertising & ADVERTISED_Pause)
++ lcl_adv |= ADVERTISE_PAUSE_CAP;
++ if (mac->phy_dev->advertising & ADVERTISED_Asym_Pause)
++ lcl_adv |= ADVERTISE_PAUSE_ASYM;
++
++ flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
++
++ if (flowctrl & FLOW_CTRL_TX)
++ mcr |= MAC_MCR_FORCE_TX_FC;
++ if (flowctrl & FLOW_CTRL_RX)
++ mcr |= MAC_MCR_FORCE_RX_FC;
++
++ netif_dbg(mac->hw, link, dev, "rx pause %s, tx pause %s\n",
++ flowctrl & FLOW_CTRL_RX ? "enabled" : "disabled",
++ flowctrl & FLOW_CTRL_TX ? "enabled" : "disabled");
++ }
+
+ mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+
+@@ -236,7 +257,8 @@ static int mtk_phy_connect(struct mtk_mac *mac)
+ mac->phy_dev->autoneg = AUTONEG_ENABLE;
+ mac->phy_dev->speed = 0;
+ mac->phy_dev->duplex = 0;
+- mac->phy_dev->supported &= PHY_BASIC_FEATURES;
++ mac->phy_dev->supported &= PHY_GBIT_FEATURES | SUPPORTED_Pause |
++ SUPPORTED_Asym_Pause;
+ mac->phy_dev->advertising = mac->phy_dev->supported |
+ ADVERTISED_Autoneg;
+ phy_start_aneg(mac->phy_dev);
+--
+1.7.10.4
+
--- /dev/null
+From 09f0b50ae838bd6e2bbf0aa22de9f352122297de Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 22 Apr 2016 11:06:03 +0200
+Subject: [PATCH 079/102] net-next: mediatek: add fixed-phy support
+
+The MT7623 SoC has a builtin gigabit switch. If we want to use it, GMAC1
+needs to be configured using a fixed link speed and flow control settings.
+The easiest way to do this is to used the fixed-phy driver, allowing us to
+reuse the existing mdio polling code to setup the MAC.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 3436c7b..ab61789 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -229,6 +229,9 @@ static int mtk_phy_connect(struct mtk_mac *mac)
+ u32 val, ge_mode;
+
+ np = of_parse_phandle(mac->of_node, "phy-handle", 0);
++ if (!np && of_phy_is_fixed_link(mac->of_node))
++ if (!of_phy_register_fixed_link(mac->of_node))
++ np = of_node_get(mac->of_node);
+ if (!np)
+ return -ENODEV;
+
+@@ -257,6 +260,9 @@ static int mtk_phy_connect(struct mtk_mac *mac)
+ mac->phy_dev->autoneg = AUTONEG_ENABLE;
+ mac->phy_dev->speed = 0;
+ mac->phy_dev->duplex = 0;
++ if (of_phy_is_fixed_link(mac->of_node))
++ mac->phy_dev->supported |= SUPPORTED_Pause |
++ SUPPORTED_Asym_Pause;
+ mac->phy_dev->supported &= PHY_GBIT_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause;
+ mac->phy_dev->advertising = mac->phy_dev->supported |
+--
+1.7.10.4
+
+++ /dev/null
-From f26f228f312fafc090d21036b682bd1062bb731f Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Sat, 23 Apr 2016 11:57:21 +0200
-Subject: [PATCH 79/91] net-next: mediatek: fix BQL support
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 33 ++++++++++++++++-----------
- 1 file changed, 20 insertions(+), 13 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -625,7 +625,16 @@ static int mtk_tx_map(struct sk_buff *sk
- WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
- (!nr_frags * TX_DMA_LS0)));
-
-- netdev_sent_queue(dev, skb->len);
-+ /* we have a single DMA ring so BQL needs to be updated for all devices
-+ * sitting on this ring
-+ */
-+ for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ if (!eth->netdev[i])
-+ continue;
-+
-+ netdev_sent_queue(eth->netdev[i], skb->len);
-+ }
-+
- skb_tx_timestamp(skb);
-
- ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
-@@ -853,21 +862,18 @@ static int mtk_poll_tx(struct mtk_eth *e
- struct mtk_tx_dma *desc;
- struct sk_buff *skb;
- struct mtk_tx_buf *tx_buf;
-- int total = 0, done[MTK_MAX_DEVS];
-- unsigned int bytes[MTK_MAX_DEVS];
-+ int total = 0, done = 0;
-+ unsigned int bytes = 0;
- u32 cpu, dma;
- static int condition;
- int i;
-
-- memset(done, 0, sizeof(done));
-- memset(bytes, 0, sizeof(bytes));
--
- cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
- dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
-
- desc = mtk_qdma_phys_to_virt(ring, cpu);
-
-- while ((cpu != dma) && budget) {
-+ while ((cpu != dma) && done < budget) {
- u32 next_cpu = desc->txd2;
- int mac;
-
-@@ -887,9 +893,8 @@ static int mtk_poll_tx(struct mtk_eth *e
- }
-
- if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) {
-- bytes[mac] += skb->len;
-- done[mac]++;
-- budget--;
-+ bytes += skb->len;
-+ done++;
- }
- mtk_tx_unmap(eth->dev, tx_buf);
-
-@@ -902,11 +907,13 @@ static int mtk_poll_tx(struct mtk_eth *e
-
- mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
-
-+ /* we have a single DMA ring so BQL needs to be updated for all devices
-+ * sitting on this ring
-+ */
- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- if (!eth->netdev[i] || !done[i])
-+ if (!eth->netdev[i])
- continue;
-- netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
-- total += done[i];
-+ netdev_completed_queue(eth->netdev[i], done, bytes);
- }
-
- /* read hw index again make sure no new tx packet */
+++ /dev/null
-From 6918f290a9019425043dbedf7b39bc82a69e23a6 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Fri, 22 Apr 2016 11:05:23 +0200
-Subject: [PATCH 80/91] net-next: mediatek: fix gigabit and flow control
- advertisement
-
-The current code will not setup the PHYs advertisement features correctly.
-Fix this and properly advertise Gigabit features and properly handle
-asymmetric pause frames.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 ++++++++++++++--
- 1 file changed, 14 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -133,6 +133,8 @@ static int mtk_mdio_read(struct mii_bus
- static void mtk_phy_link_adjust(struct net_device *dev)
- {
- struct mtk_mac *mac = netdev_priv(dev);
-+ u16 lcl_adv, rmt_adv = 0;
-+ u8 flowctrl;
- u32 mcr = MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG |
- MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN |
- MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN |
-@@ -154,7 +156,16 @@ static void mtk_phy_link_adjust(struct n
- mcr |= MAC_MCR_FORCE_DPX;
-
- if (mac->phy_dev->pause)
-- mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC;
-+ rmt_adv = LPA_PAUSE_CAP;
-+ if (mac->phy_dev->asym_pause)
-+ rmt_adv |= LPA_PAUSE_ASYM;
-+
-+ lcl_adv = mii_advertise_flowctrl(FLOW_CTRL_RX | FLOW_CTRL_TX);
-+ flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
-+ if (flowctrl & FLOW_CTRL_TX)
-+ mcr |= MAC_MCR_FORCE_TX_FC;
-+ if (flowctrl & FLOW_CTRL_RX)
-+ mcr |= MAC_MCR_FORCE_RX_FC;
-
- mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
-
-@@ -236,7 +247,8 @@ static int mtk_phy_connect(struct mtk_ma
- mac->phy_dev->autoneg = AUTONEG_ENABLE;
- mac->phy_dev->speed = 0;
- mac->phy_dev->duplex = 0;
-- mac->phy_dev->supported &= PHY_BASIC_FEATURES;
-+ mac->phy_dev->supported &= PHY_GBIT_FEATURES | SUPPORTED_Pause |
-+ ~SUPPORTED_Asym_Pause;
- mac->phy_dev->advertising = mac->phy_dev->supported |
- ADVERTISED_Autoneg;
- phy_start_aneg(mac->phy_dev);
--- /dev/null
+From 25eaa5d6483a5899e6bf48b47f762f05c186b4b6 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 22 Apr 2016 11:08:43 +0200
+Subject: [PATCH 080/102] net-next: mediatek: properly handle RGMII modes
+
+If an external Gigabit PHY is connected to either of the MACs we need to
+be able to tell the PHY to use a delay. Not doing so will result in heavy
+packet loss and/or data corruption when using PHYs such as the IC+ IP1001.
+We tell the PHY which MII delay mode to use via the devictree.
+
+The ethernet driver needs to be adapted to handle all 3 rgmii-*id modes
+in the same way as normal rgmii when setting up the MAC.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: John Crispin <john@phrozen.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 ab61789..76ecb1b 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -236,6 +236,9 @@ static int mtk_phy_connect(struct mtk_mac *mac)
+ return -ENODEV;
+
+ switch (of_get_phy_mode(np)) {
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII:
+ ge_mode = 0;
+ break;
+--
+1.7.10.4
+
+++ /dev/null
-From b5ecc24a027dea24f3ff798f87f65dd42015b342 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Fri, 22 Apr 2016 11:06:03 +0200
-Subject: [PATCH 81/91] net-next: mediatek: add fixed-phy support
-
-The MT7623 SoC has a builtin gigabit switch. If we want to use it, GMAC1
-needs to be configured using a fixed link speed and flow control settings.
-The easiest way to do this is to used the fixed-phy driver, allowing us to
-reuse the existing mdio polling code to setup the MAC.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -219,6 +219,9 @@ static int mtk_phy_connect(struct mtk_ma
- u32 val, ge_mode;
-
- np = of_parse_phandle(mac->of_node, "phy-handle", 0);
-+ if (!np && of_phy_is_fixed_link(mac->of_node))
-+ if (!of_phy_register_fixed_link(mac->of_node))
-+ np = of_node_get(mac->of_node);
- if (!np)
- return -ENODEV;
-
--- /dev/null
+From 81cdbda2a08375b9d5915567d2210bf2433e7332 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Sat, 23 Apr 2016 11:57:21 +0200
+Subject: [PATCH 081/102] net-next: mediatek: fix DQL support
+
+The MTK ethernet core has 2 MACs both sitting on the same DMA ring. The
+current code will assign the TX traffic of each MAC to its own DQL. This
+results in the amount of data, that DQL says is in the queue incorrect. As
+the data from multiple devices is infact enqueued. This makes any decision
+based on these value non deterministic. Fix this by tracking all TX
+traffic, regardless of the MAC it belongs to in the DQL of all devices
+using the DMA.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 33 ++++++++++++++++-----------
+ 1 file changed, 20 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 76ecb1b..feedd5a 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -656,7 +656,16 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+ (!nr_frags * TX_DMA_LS0)));
+
+- netdev_sent_queue(dev, skb->len);
++ /* we have a single DMA ring so BQL needs to be updated for all devices
++ * sitting on this ring
++ */
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ if (!eth->netdev[i])
++ continue;
++
++ netdev_sent_queue(eth->netdev[i], skb->len);
++ }
++
+ skb_tx_timestamp(skb);
+
+ ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
+@@ -884,21 +893,18 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ struct mtk_tx_dma *desc;
+ struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
+- int total = 0, done[MTK_MAX_DEVS];
+- unsigned int bytes[MTK_MAX_DEVS];
++ int total = 0, done = 0;
++ unsigned int bytes = 0;
+ u32 cpu, dma;
+ static int condition;
+ int i;
+
+- memset(done, 0, sizeof(done));
+- memset(bytes, 0, sizeof(bytes));
+-
+ cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
+ dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
+
+ desc = mtk_qdma_phys_to_virt(ring, cpu);
+
+- while ((cpu != dma) && budget) {
++ while ((cpu != dma) && done < budget) {
+ u32 next_cpu = desc->txd2;
+ int mac;
+
+@@ -918,9 +924,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ }
+
+ if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) {
+- bytes[mac] += skb->len;
+- done[mac]++;
+- budget--;
++ bytes += skb->len;
++ done++;
+ }
+ mtk_tx_unmap(eth->dev, tx_buf);
+
+@@ -933,11 +938,13 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+
+ mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
+
++ /* we have a single DMA ring so BQL needs to be updated for all devices
++ * sitting on this ring
++ */
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+- if (!eth->netdev[i] || !done[i])
++ if (!eth->netdev[i])
+ continue;
+- netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
+- total += done[i];
++ netdev_completed_queue(eth->netdev[i], done, bytes);
+ }
+
+ /* read hw index again make sure no new tx packet */
+--
+1.7.10.4
+
+++ /dev/null
-From bbd92ed51c48a4586f149767841a5495cbc5a979 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Fri, 22 Apr 2016 11:08:43 +0200
-Subject: [PATCH 82/91] net-next: mediatek: add RX delay support
-
-If an external Gigabit PHY is connected to either of the MACs we need to
-tell the to use a RX delay. Not doing so will result in heavy packet loss
-and/or data corruption of RX traffic.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -226,6 +226,7 @@ static int mtk_phy_connect(struct mtk_ma
- return -ENODEV;
-
- switch (of_get_phy_mode(np)) {
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII:
- ge_mode = 0;
- break;
--- /dev/null
+From 51ca1e9f141499fd7c95bff5401215b706656754 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Sat, 23 Apr 2016 09:06:05 +0200
+Subject: [PATCH 082/102] net-next: mediatek: add missing return code check
+
+The code fails to check if the scratch memory was properly allocated. Add
+this check and return with an error if the allocation failed.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.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 feedd5a..fefbf16 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -498,6 +498,9 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
+
+ eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
+ GFP_KERNEL);
++ if (unlikely(!eth->scratch_head))
++ return -ENOMEM;
++
+ dma_addr = dma_map_single(eth->dev,
+ eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
+ DMA_FROM_DEVICE);
+--
+1.7.10.4
+
+++ /dev/null
-From d20b45f50d6b3352aa7be76eb7a28cffcfe379da Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Sat, 23 Apr 2016 09:06:05 +0200
-Subject: [PATCH 83/91] net-next: mediatek: add missing return code check
-
-The code fails to check if the scratch memory was properly allocated. Add
-this check and return with an error if the allocation failed.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -483,6 +483,9 @@ static int mtk_init_fq_dma(struct mtk_et
-
- eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
- GFP_KERNEL);
-+ if (unlikely(!eth->scratch_head))
-+ return -ENOMEM;
-+
- dma_addr = dma_map_single(eth->dev,
- eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
- DMA_FROM_DEVICE);
--- /dev/null
+From b48745c534ced06005d2ba57198b54a6a160b39d Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Sat, 23 Apr 2016 09:18:28 +0200
+Subject: [PATCH 083/102] net-next: mediatek: fix missing free of scratch
+ memory
+
+Scratch memory gets allocated in mtk_init_fq_dma() but the corresponding
+code to free it is missing inside mtk_dma_free() causing a memory leak.
+With this patch applied, we can run ifconfig/up/down several thousand
+times without any problems.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 18 +++++++++++++-----
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 ++
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index fefbf16..d9664e5 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -484,14 +484,14 @@ static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
+ /* the qdma core needs scratch memory to be setup */
+ static int mtk_init_fq_dma(struct mtk_eth *eth)
+ {
+- dma_addr_t phy_ring_head, phy_ring_tail;
++ dma_addr_t phy_ring_tail;
+ int cnt = MTK_DMA_SIZE;
+ dma_addr_t dma_addr;
+ int i;
+
+ eth->scratch_ring = dma_alloc_coherent(eth->dev,
+ cnt * sizeof(struct mtk_tx_dma),
+- &phy_ring_head,
++ ð->phy_scratch_ring,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (unlikely(!eth->scratch_ring))
+ return -ENOMEM;
+@@ -508,19 +508,19 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
+ return -ENOMEM;
+
+ memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
+- phy_ring_tail = phy_ring_head +
++ phy_ring_tail = eth->phy_scratch_ring +
+ (sizeof(struct mtk_tx_dma) * (cnt - 1));
+
+ for (i = 0; i < cnt; i++) {
+ eth->scratch_ring[i].txd1 =
+ (dma_addr + (i * MTK_QDMA_PAGE_SIZE));
+ if (i < cnt - 1)
+- eth->scratch_ring[i].txd2 = (phy_ring_head +
++ eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring +
+ ((i + 1) * sizeof(struct mtk_tx_dma)));
+ eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
+ }
+
+- mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
++ mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
+ mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
+ mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
+ mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
+@@ -1220,6 +1220,14 @@ static void mtk_dma_free(struct mtk_eth *eth)
+ for (i = 0; i < MTK_MAC_COUNT; i++)
+ if (eth->netdev[i])
+ netdev_reset_queue(eth->netdev[i]);
++ if (eth->scratch_ring) {
++ dma_free_coherent(eth->dev,
++ MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
++ eth->scratch_ring,
++ eth->phy_scratch_ring);
++ eth->scratch_ring = NULL;
++ eth->phy_scratch_ring = 0;
++ }
+ mtk_tx_clean(eth);
+ mtk_rx_clean(eth);
+ kfree(eth->scratch_head);
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index eed626d..57f7e8a 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -357,6 +357,7 @@ struct mtk_rx_ring {
+ * @rx_ring: Pointer to the memore holding info about the RX ring
+ * @rx_napi: The NAPI struct
+ * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
++ * @phy_scratch_ring: physical address of scratch_ring
+ * @scratch_head: The scratch memory that scratch_ring points to.
+ * @clk_ethif: The ethif clock
+ * @clk_esw: The switch clock
+@@ -384,6 +385,7 @@ struct mtk_eth {
+ struct mtk_rx_ring rx_ring;
+ struct napi_struct rx_napi;
+ struct mtk_tx_dma *scratch_ring;
++ dma_addr_t phy_scratch_ring;
+ void *scratch_head;
+ struct clk *clk_ethif;
+ struct clk *clk_esw;
+--
+1.7.10.4
+
+++ /dev/null
-From 2d22628561299e1c7d71e16262131127de3c4216 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Sat, 23 Apr 2016 09:18:28 +0200
-Subject: [PATCH 84/91] net-next: mediatek: fix missing free of scratch memory
-
-Scratch memory gets allocated in mtk_init_fq_dma() but the corresponding
-code to free it is missing inside mtk_dma_free() causing a memory leak.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 18 +++++++++++++-----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 ++
- 2 files changed, 15 insertions(+), 5 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -469,14 +469,14 @@ static inline void mtk_rx_get_desc(struc
- /* the qdma core needs scratch memory to be setup */
- static int mtk_init_fq_dma(struct mtk_eth *eth)
- {
-- dma_addr_t phy_ring_head, phy_ring_tail;
-+ dma_addr_t phy_ring_tail;
- int cnt = MTK_DMA_SIZE;
- dma_addr_t dma_addr;
- int i;
-
- eth->scratch_ring = dma_alloc_coherent(eth->dev,
- cnt * sizeof(struct mtk_tx_dma),
-- &phy_ring_head,
-+ ð->phy_scratch_ring,
- GFP_ATOMIC | __GFP_ZERO);
- if (unlikely(!eth->scratch_ring))
- return -ENOMEM;
-@@ -493,19 +493,19 @@ static int mtk_init_fq_dma(struct mtk_et
- return -ENOMEM;
-
- memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
-- phy_ring_tail = phy_ring_head +
-+ phy_ring_tail = eth->phy_scratch_ring +
- (sizeof(struct mtk_tx_dma) * (cnt - 1));
-
- for (i = 0; i < cnt; i++) {
- eth->scratch_ring[i].txd1 =
- (dma_addr + (i * MTK_QDMA_PAGE_SIZE));
- if (i < cnt - 1)
-- eth->scratch_ring[i].txd2 = (phy_ring_head +
-+ eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring +
- ((i + 1) * sizeof(struct mtk_tx_dma)));
- eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
- }
-
-- mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
-+ mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
- mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
- mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
- mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
-@@ -1205,6 +1205,14 @@ static void mtk_dma_free(struct mtk_eth
- for (i = 0; i < MTK_MAC_COUNT; i++)
- if (eth->netdev[i])
- netdev_reset_queue(eth->netdev[i]);
-+ if (eth->scratch_ring) {
-+ dma_free_coherent(eth->dev,
-+ MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
-+ eth->scratch_ring,
-+ eth->phy_scratch_ring);
-+ eth->scratch_ring = NULL;
-+ eth->phy_scratch_ring = 0;
-+ }
- mtk_tx_clean(eth);
- mtk_rx_clean(eth);
- kfree(eth->scratch_head);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -357,6 +357,7 @@ struct mtk_rx_ring {
- * @rx_ring: Pointer to the memore holding info about the RX ring
- * @rx_napi: The NAPI struct
- * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
-+ * @phy_scratch_ring: physical address of scratch_ring
- * @scratch_head: The scratch memory that scratch_ring points to.
- * @clk_ethif: The ethif clock
- * @clk_esw: The switch clock
-@@ -384,6 +385,7 @@ struct mtk_eth {
- struct mtk_rx_ring rx_ring;
- struct napi_struct rx_napi;
- struct mtk_tx_dma *scratch_ring;
-+ dma_addr_t phy_scratch_ring;
- void *scratch_head;
- struct clk *clk_ethif;
- struct clk *clk_esw;
--- /dev/null
+From 1eea1536dbbbfda418751ec6f5387acb521ddb97 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Sat, 23 Apr 2016 09:25:00 +0200
+Subject: [PATCH 084/102] net-next: mediatek: invalid buffer lookup in
+ mtk_tx_map()
+
+The lookup of the tx_buffer in the error path inside mtk_tx_map() uses the
+wrong descriptor pointer. This looks like a copy & paste error. Change the
+code to use the correct pointer.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index d9664e5..17ca1c1 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -686,7 +686,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+
+ err_dma:
+ do {
+- tx_buf = mtk_desc_to_tx_buf(ring, txd);
++ tx_buf = mtk_desc_to_tx_buf(ring, itxd);
+
+ /* unmap dma */
+ mtk_tx_unmap(&dev->dev, tx_buf);
+--
+1.7.10.4
+
--- /dev/null
+From 98aac832925a99afee8722cdfd5a848dd6086b8f Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Sat, 23 Apr 2016 09:28:25 +0200
+Subject: [PATCH 085/102] net-next: mediatek: dropped rx packets are not being
+ counted properly
+
+There are 2 places inside mtk_poll_rx where rx_dropped is not being
+incremented properly. Fix this by adding the missing code to increment
+the counter.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 17ca1c1..aadd748 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -841,6 +841,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
+ skb_free_frag(new_data);
++ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
+
+@@ -848,6 +849,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ skb = build_skb(data, ring->frag_size);
+ if (unlikely(!skb)) {
+ put_page(virt_to_head_page(new_data));
++ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+--
+1.7.10.4
+
+++ /dev/null
-From 7ae20e15e06eed22f343a566b22dce258f7b8704 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Sat, 23 Apr 2016 09:25:00 +0200
-Subject: [PATCH 85/91] net-next: mediatek: invalid buffer lookup in
- mtk_tx_map()
-
-The lookup of the tx_buffer in the error path inside mtk_tx_map() uses the
-wrong descriptor pointer. This looks like a copy & paste error.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -671,7 +671,7 @@ static int mtk_tx_map(struct sk_buff *sk
-
- err_dma:
- do {
-- tx_buf = mtk_desc_to_tx_buf(ring, txd);
-+ tx_buf = mtk_desc_to_tx_buf(ring, itxd);
-
- /* unmap dma */
- mtk_tx_unmap(&dev->dev, tx_buf);
--- /dev/null
+From 5077ac38a86023124ebbe24cd1b7ecbd0f8edaff Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 03:06:59 +0200
+Subject: [PATCH 086/102] net-next: mediatek: add next data pointer coherency
+ protection
+
+The QDMA engine can fail to update the register pointing to the next TX
+descriptor if this bit does not get set in the QDMA configuration register.
+Not setting this bit can result in invalid values inside the TX rings
+registers which will causes TX stalls.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index aadd748..72908b2 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1292,7 +1292,7 @@ static int mtk_start_dma(struct mtk_eth *eth)
+ mtk_w32(eth,
+ MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN |
+ MTK_RX_2B_OFFSET | MTK_DMA_SIZE_16DWORDS |
+- MTK_RX_BT_32DWORDS,
++ MTK_RX_BT_32DWORDS | MTK_NDP_CO_PRO,
+ MTK_QDMA_GLO_CFG);
+
+ return 0;
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index 57f7e8a..a5eb7c6 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -91,6 +91,7 @@
+ #define MTK_QDMA_GLO_CFG 0x1A04
+ #define MTK_RX_2B_OFFSET BIT(31)
+ #define MTK_RX_BT_32DWORDS (3 << 11)
++#define MTK_NDP_CO_PRO BIT(10)
+ #define MTK_TX_WB_DDONE BIT(6)
+ #define MTK_DMA_SIZE_16DWORDS (2 << 4)
+ #define MTK_RX_DMA_BUSY BIT(3)
+--
+1.7.10.4
+
+++ /dev/null
-From d74187cab7927d3496c01c97051d9c539067ad1b Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Sat, 23 Apr 2016 09:28:25 +0200
-Subject: [PATCH 86/91] net-next: mediatek: dropped rx packets are not being
- counted properly
-
-There are 2 places inside mtk_poll_rx where rx_dropped is not being
-incremented properly. Fix this by adding the missing code to increment
-the counter.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -826,6 +826,7 @@ static int mtk_poll_rx(struct napi_struc
- DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
- skb_free_frag(new_data);
-+ netdev->stats.rx_dropped++;
- goto release_desc;
- }
-
-@@ -833,6 +834,7 @@ static int mtk_poll_rx(struct napi_struc
- skb = build_skb(data, ring->frag_size);
- if (unlikely(!skb)) {
- put_page(virt_to_head_page(new_data));
-+ netdev->stats.rx_dropped++;
- goto release_desc;
- }
- skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+++ /dev/null
-From 4ff9304355036d4a00bdf0e47e869fc770ba1cc5 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Wed, 20 Apr 2016 16:18:07 +0200
-Subject: [PATCH 87/91] net-next: mediatek: add IRQ locking
-
-The code that enables and disables IRQs is missing proper locking. After
-adding the IRQ separation patch and routing the putting the RX and TX IRQs
-on different cores we experienced IRQ stalls. Fix this by adding proper
-locking. We use a dedicated lock to reduce the latency if the IRQ code.
-Otherwise it might wait for bottom code to finish before reenabling or
-disabling IRQs.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 +++++++
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
- 2 files changed, 8 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -316,22 +316,28 @@ static void mtk_mdio_cleanup(struct mtk_
-
- static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
- {
-+ unsigned long flags;
- u32 val;
-
-+ spin_lock_irqsave(ð->irq_lock, flags);
- val = mtk_r32(eth, MTK_QDMA_INT_MASK);
- mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
- /* flush write */
- mtk_r32(eth, MTK_QDMA_INT_MASK);
-+ spin_unlock_irqrestore(ð->irq_lock, flags);
- }
-
- static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
- {
-+ unsigned long flags;
- u32 val;
-
-+ spin_lock_irqsave(ð->irq_lock, flags);
- val = mtk_r32(eth, MTK_QDMA_INT_MASK);
- mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
- /* flush write */
- mtk_r32(eth, MTK_QDMA_INT_MASK);
-+ spin_unlock_irqrestore(ð->irq_lock, flags);
- }
-
- static int mtk_set_mac_address(struct net_device *dev, void *p)
-@@ -1752,6 +1758,7 @@ static int mtk_probe(struct platform_dev
- return -EADDRNOTAVAIL;
-
- spin_lock_init(ð->page_lock);
-+ spin_lock_init(ð->irq_lock);
-
- eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
- "mediatek,ethsys");
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -372,6 +372,7 @@ struct mtk_eth {
- void __iomem *base;
- struct reset_control *rstc;
- spinlock_t page_lock;
-+ spinlock_t irq_lock;
- struct net_device dummy_dev;
- struct net_device *netdev[MTK_MAX_DEVS];
- struct mtk_mac *mac[MTK_MAX_DEVS];
--- /dev/null
+From f9a08e142fd87c72a7803203ce4ecc94806046ca Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 03:14:07 +0200
+Subject: [PATCH 087/102] net-next: mediatek: disable all interrupts during
+ probe
+
+The current code only disables those IRQs that we will later use. To
+ensure that we have a predefined state, we really want to disable all IRQs.
+Change the code to disable all IRQs to achieve this.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 72908b2..ec6140f 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1406,7 +1406,7 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+
+ /* disable delay and normal interrupt */
+ mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+- mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
++ mtk_irq_disable(eth, ~0);
+ mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
+ mtk_w32(eth, 0, MTK_RST_GL);
+
+--
+1.7.10.4
+
+++ /dev/null
-From 41b4500871ab5b1ef27c6fb49ffd8aac8c7e5009 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Wed, 23 Mar 2016 18:31:48 +0100
-Subject: [PATCH 88/91] net-next: mediatek: add support for IRQ grouping
-
-The ethernet core has 3 IRQs. using the IRQ grouping registers we are able
-to separate TX and RX IRQs, which allows us to service them on separate
-cores. This patch splits the irq handler into 2 separate functiosn, one for
-TX and another for RX. The TX housekeeping is split out of the NAPI handler.
-Instead we use a tasklet to handle housekeeping.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 164 ++++++++++++++++++---------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 16 ++-
- 2 files changed, 124 insertions(+), 56 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -790,7 +790,7 @@ drop:
- }
-
- static int mtk_poll_rx(struct napi_struct *napi, int budget,
-- struct mtk_eth *eth, u32 rx_intr)
-+ struct mtk_eth *eth)
- {
- struct mtk_rx_ring *ring = ð->rx_ring;
- int idx = ring->calc_idx;
-@@ -878,19 +878,18 @@ release_desc:
- }
-
- if (done < budget)
-- mtk_w32(eth, rx_intr, MTK_QMTK_INT_STATUS);
-+ mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
-
- return done;
- }
-
--static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
-+static int mtk_poll_tx(struct mtk_eth *eth, int budget)
- {
- struct mtk_tx_ring *ring = ð->tx_ring;
- struct mtk_tx_dma *desc;
- struct sk_buff *skb;
- struct mtk_tx_buf *tx_buf;
-- int total = 0, done = 0;
-- unsigned int bytes = 0;
-+ unsigned int bytes = 0, done = 0;
- u32 cpu, dma;
- static int condition;
- int i;
-@@ -944,63 +943,80 @@ static int mtk_poll_tx(struct mtk_eth *e
- }
-
- /* read hw index again make sure no new tx packet */
-- if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
-- *tx_again = true;
-- else
-+ if (cpu == dma && cpu == mtk_r32(eth, MTK_QTX_DRX_PTR))
- mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
-
-- if (!total)
-- return 0;
--
- if (atomic_read(&ring->free_count) > ring->thresh)
- mtk_wake_queue(eth);
-
-- return total;
-+ return done;
- }
-
--static int mtk_poll(struct napi_struct *napi, int budget)
-+static void mtk_handle_status_irq(struct mtk_eth *eth)
- {
-- struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
-- u32 status, status2, mask, tx_intr, rx_intr, status_intr;
-- int tx_done, rx_done;
-- bool tx_again = false;
--
-- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-- status2 = mtk_r32(eth, MTK_INT_STATUS2);
-- tx_intr = MTK_TX_DONE_INT;
-- rx_intr = MTK_RX_DONE_INT;
-- status_intr = (MTK_GDM1_AF | MTK_GDM2_AF);
-- tx_done = 0;
-- rx_done = 0;
-- tx_again = 0;
--
-- if (status & tx_intr)
-- tx_done = mtk_poll_tx(eth, budget, &tx_again);
--
-- if (status & rx_intr)
-- rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
-+ u32 status2 = mtk_r32(eth, MTK_INT_STATUS2);
-+ u32 status_intr = (MTK_GDM1_AF | MTK_GDM2_AF);
-
- if (unlikely(status2 & status_intr)) {
- mtk_stats_update(eth);
- mtk_w32(eth, status_intr, MTK_INT_STATUS2);
- }
-+}
-+
-+static int mtk_napi_tx(struct napi_struct *napi, int budget)
-+{
-+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi);
-+ u32 status, mask;
-+ int tx_done = 0;
-+
-+ mtk_handle_status_irq(eth);
-
-+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-+ tx_done = mtk_poll_tx(eth, budget);
- if (unlikely(netif_msg_intr(eth))) {
- mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
-- netdev_info(eth->netdev[0],
-- "done tx %d, rx %d, intr 0x%08x/0x%x\n",
-- tx_done, rx_done, status, mask);
-+ dev_info(eth->dev,
-+ "done tx %d, intr 0x%08x/0x%x\n",
-+ tx_done, status, mask);
- }
-
-- if (tx_again || rx_done == budget)
-+ if (tx_done == budget)
- return budget;
-
- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-- if (status & (tx_intr | rx_intr))
-+ if (status & MTK_TX_DONE_INT)
- return budget;
-
- napi_complete(napi);
-- mtk_irq_enable(eth, tx_intr | rx_intr);
-+ mtk_irq_enable(eth, MTK_TX_DONE_INT);
-+
-+ return tx_done;
-+}
-+
-+static int mtk_napi_rx(struct napi_struct *napi, int budget)
-+{
-+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
-+ u32 status, mask;
-+ int rx_done = 0;
-+
-+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-+ rx_done = mtk_poll_rx(napi, budget, eth);
-+ if (unlikely(netif_msg_intr(eth))) {
-+ mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
-+ dev_info(eth->dev,
-+ "done rx %d, intr 0x%08x/0x%x\n",
-+ rx_done, status, mask);
-+ }
-+
-+ if (rx_done == budget)
-+ return budget;
-+
-+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-+ if (status & MTK_RX_DONE_INT)
-+ return budget;
-+
-+ napi_complete(napi);
-+ mtk_irq_enable(eth, MTK_RX_DONE_INT);
-
- return rx_done;
- }
-@@ -1237,22 +1253,44 @@ static void mtk_tx_timeout(struct net_de
- schedule_work(ð->pending_work);
- }
-
--static irqreturn_t mtk_handle_irq(int irq, void *_eth)
-+static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
- {
- struct mtk_eth *eth = _eth;
- u32 status;
-
- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-+ status &= ~MTK_TX_DONE_INT;
-+
- if (unlikely(!status))
- return IRQ_NONE;
-
-- if (likely(status & (MTK_RX_DONE_INT | MTK_TX_DONE_INT))) {
-+ if (status & MTK_RX_DONE_INT) {
- if (likely(napi_schedule_prep(ð->rx_napi)))
- __napi_schedule(ð->rx_napi);
-- } else {
-- mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
-+ mtk_irq_disable(eth, MTK_RX_DONE_INT);
- }
-- mtk_irq_disable(eth, (MTK_RX_DONE_INT | MTK_TX_DONE_INT));
-+ mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
-+{
-+ struct mtk_eth *eth = _eth;
-+ u32 status;
-+
-+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-+ status &= ~MTK_RX_DONE_INT;
-+
-+ if (unlikely(!status))
-+ return IRQ_NONE;
-+
-+ if (status & MTK_TX_DONE_INT) {
-+ if (likely(napi_schedule_prep(ð->tx_napi)))
-+ __napi_schedule(ð->tx_napi);
-+ mtk_irq_disable(eth, MTK_TX_DONE_INT);
-+ }
-+ mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
-
- return IRQ_HANDLED;
- }
-@@ -1265,7 +1303,7 @@ static void mtk_poll_controller(struct n
- u32 int_mask = MTK_TX_DONE_INT | MTK_RX_DONE_INT;
-
- mtk_irq_disable(eth, int_mask);
-- mtk_handle_irq(dev->irq, dev);
-+ mtk_handle_irq(dev->irq[0], dev);
- mtk_irq_enable(eth, int_mask);
- }
- #endif
-@@ -1301,6 +1339,7 @@ static int mtk_open(struct net_device *d
- if (err)
- return err;
-
-+ napi_enable(ð->tx_napi);
- napi_enable(ð->rx_napi);
- mtk_irq_enable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
- }
-@@ -1349,6 +1388,7 @@ static int mtk_stop(struct net_device *d
- return 0;
-
- mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
-+ napi_disable(ð->tx_napi);
- napi_disable(ð->rx_napi);
-
- mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
-@@ -1386,7 +1426,11 @@ static int __init mtk_hw_init(struct mtk
- /* Enable RX VLan Offloading */
- mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
-
-- err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
-+ err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
-+ dev_name(eth->dev), eth);
-+ if (err)
-+ return err;
-+ err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
- dev_name(eth->dev), eth);
- if (err)
- return err;
-@@ -1402,7 +1446,11 @@ static int __init mtk_hw_init(struct mtk
- mtk_w32(eth, 0, MTK_RST_GL);
-
- /* FE int grouping */
-- mtk_w32(eth, 0, MTK_FE_INT_GRP);
-+ mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1);
-+ mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_GRP2);
-+ mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1);
-+ mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2);
-+ mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
-
- for (i = 0; i < 2; i++) {
- u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
-@@ -1450,7 +1498,9 @@ static void mtk_uninit(struct net_device
- phy_disconnect(mac->phy_dev);
- mtk_mdio_cleanup(eth);
- mtk_irq_disable(eth, ~0);
-- free_irq(dev->irq, dev);
-+ free_irq(eth->irq[0], dev);
-+ free_irq(eth->irq[1], dev);
-+ free_irq(eth->irq[2], dev);
- }
-
- static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-@@ -1725,10 +1775,10 @@ static int mtk_add_mac(struct mtk_eth *e
- dev_err(eth->dev, "error bringing up device\n");
- goto free_netdev;
- }
-- eth->netdev[id]->irq = eth->irq;
-+ eth->netdev[id]->irq = eth->irq[0];
- netif_info(eth, probe, eth->netdev[id],
- "mediatek frame engine at 0x%08lx, irq %d\n",
-- eth->netdev[id]->base_addr, eth->netdev[id]->irq);
-+ eth->netdev[id]->base_addr, eth->irq[0]);
-
- return 0;
-
-@@ -1745,6 +1795,7 @@ static int mtk_probe(struct platform_dev
- struct mtk_soc_data *soc;
- struct mtk_eth *eth;
- int err;
-+ int i;
-
- match = of_match_device(of_mtk_match, &pdev->dev);
- soc = (struct mtk_soc_data *)match->data;
-@@ -1780,10 +1831,12 @@ static int mtk_probe(struct platform_dev
- return PTR_ERR(eth->rstc);
- }
-
-- eth->irq = platform_get_irq(pdev, 0);
-- if (eth->irq < 0) {
-- dev_err(&pdev->dev, "no IRQ resource found\n");
-- return -ENXIO;
-+ for (i = 0; i < 3; i++) {
-+ eth->irq[i] = platform_get_irq(pdev, i);
-+ if (eth->irq[i] < 0) {
-+ dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
-+ return -ENXIO;
-+ }
- }
-
- eth->clk_ethif = devm_clk_get(&pdev->dev, "ethif");
-@@ -1824,7 +1877,9 @@ static int mtk_probe(struct platform_dev
- * for NAPI to work
- */
- init_dummy_netdev(ð->dummy_dev);
-- netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_poll,
-+ netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx,
-+ MTK_NAPI_WEIGHT);
-+ netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx,
- MTK_NAPI_WEIGHT);
-
- platform_set_drvdata(pdev, eth);
-@@ -1845,6 +1900,7 @@ static int mtk_remove(struct platform_de
- clk_disable_unprepare(eth->clk_gp1);
- clk_disable_unprepare(eth->clk_gp2);
-
-+ netif_napi_del(ð->tx_napi);
- netif_napi_del(ð->rx_napi);
- mtk_cleanup(eth);
- platform_set_drvdata(pdev, NULL);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -68,6 +68,10 @@
- /* Unicast Filter MAC Address Register - High */
- #define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000))
-
-+/* PDMA Interrupt grouping registers */
-+#define MTK_PDMA_INT_GRP1 0xa50
-+#define MTK_PDMA_INT_GRP2 0xa54
-+
- /* QDMA TX Queue Configuration Registers */
- #define MTK_QTX_CFG(x) (0x1800 + (x * 0x10))
- #define QDMA_RES_THRES 4
-@@ -124,6 +128,11 @@
- #define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
- MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
-
-+/* QDMA Interrupt grouping registers */
-+#define MTK_QDMA_INT_GRP1 0x1a20
-+#define MTK_QDMA_INT_GRP2 0x1a24
-+#define MTK_RLS_DONE_INT BIT(0)
-+
- /* QDMA Interrupt Status Register */
- #define MTK_QDMA_INT_MASK 0x1A1C
-
-@@ -355,7 +364,8 @@ struct mtk_rx_ring {
- * @dma_refcnt: track how many netdevs are using the DMA engine
- * @tx_ring: Pointer to the memore holding info about the TX ring
- * @rx_ring: Pointer to the memore holding info about the RX ring
-- * @rx_napi: The NAPI struct
-+ * @tx_napi: The TX NAPI struct
-+ * @rx_napi: The RX NAPI struct
- * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
- * @phy_scratch_ring: physical address of scratch_ring
- * @scratch_head: The scratch memory that scratch_ring points to.
-@@ -376,7 +386,7 @@ struct mtk_eth {
- struct net_device dummy_dev;
- struct net_device *netdev[MTK_MAX_DEVS];
- struct mtk_mac *mac[MTK_MAX_DEVS];
-- int irq;
-+ int irq[3];
- u32 msg_enable;
- unsigned long sysclk;
- struct regmap *ethsys;
-@@ -384,6 +394,7 @@ struct mtk_eth {
- atomic_t dma_refcnt;
- struct mtk_tx_ring tx_ring;
- struct mtk_rx_ring rx_ring;
-+ struct napi_struct tx_napi;
- struct napi_struct rx_napi;
- struct mtk_tx_dma *scratch_ring;
- dma_addr_t phy_scratch_ring;
-@@ -394,6 +405,7 @@ struct mtk_eth {
- struct clk *clk_gp2;
- struct mii_bus *mii_bus;
- struct work_struct pending_work;
-+
- };
-
- /* struct mtk_mac - the structure that holds the info about the MACs of the
--- /dev/null
+From 34ea0f209e0759158e363039852a04b1facc3acd Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 02:55:27 +0200
+Subject: [PATCH 088/102] net-next: mediatek: fix threshold value
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The logic to calculate the threshold value for stopping the TX queue is
+bad. Currently it will always use 1/2 of the rings size, which is way too
+much. Set the threshold to MAX_SKB_FRAGS. This makes sure that the queue
+is stopped when there is not enough room to accept an additional segment.Â
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index ec6140f..d03f339 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1043,8 +1043,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
+ atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
+ ring->next_free = &ring->dma[0];
+ ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
+- ring->thresh = max((unsigned long)MTK_DMA_SIZE >> 2,
+- MAX_SKB_FRAGS);
++ ring->thresh = MAX_SKB_FRAGS;
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+--
+1.7.10.4
+
+++ /dev/null
-From 46f10e3c9c25668efb85babe9ac5e37d019c2794 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Mon, 11 Apr 2016 03:11:54 +0200
-Subject: [PATCH 89/91] net: mediatek add gsw/mt7530 driver
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/Makefile | 2 +-
- drivers/net/ethernet/mediatek/gsw_mt7620.h | 251 +++++++
- drivers/net/ethernet/mediatek/gsw_mt7623.c | 1084 +++++++++++++++++++++++++++
- drivers/net/ethernet/mediatek/mt7530.c | 808 ++++++++++++++++++++
- drivers/net/ethernet/mediatek/mt7530.h | 20 +
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 59 +-
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 +
- 7 files changed, 2198 insertions(+), 30 deletions(-)
- create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h
- create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7623.c
- create mode 100644 drivers/net/ethernet/mediatek/mt7530.c
- create mode 100644 drivers/net/ethernet/mediatek/mt7530.h
-
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -2,4 +2,4 @@
- # Makefile for the Mediatek SoCs built-in ethernet macs
- #
-
--obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o
-+obj-$(CONFIG_NET_MEDIATEK_SOC) += mt7530.o gsw_mt7623.o mtk_eth_soc.o
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
-@@ -0,0 +1,251 @@
-+/* 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-2016 John Crispin <blogic@openwrt.org>
-+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
-+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
-+ */
-+
-+#ifndef _RALINK_GSW_MT7620_H__
-+#define _RALINK_GSW_MT7620_H__
-+
-+#define GSW_REG_PHY_TIMEOUT (5 * HZ)
-+
-+#define MT7620_GSW_REG_PIAC 0x0004
-+
-+#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_CFG 0x10
-+#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
-+
-+#define MT7621_ESW_PHY_POLLING 0x0000
-+#define MT7620_ESW_PHY_POLLING 0x7000
-+
-+#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)
-+
-+/* ethernet subsystem config register */
-+#define ETHSYS_SYSCFG0 0x14
-+/* ethernet subsystem clock register */
-+#define ETHSYS_CLKCFG0 0x2c
-+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
-+
-+/* p5 RGMII wrapper TX clock control register */
-+#define MT7530_P5RGMIITXCR 0x7b04
-+/* p5 RGMII wrapper RX clock control register */
-+#define MT7530_P5RGMIIRXCR 0x7b00
-+/* TRGMII TDX ODT registers */
-+#define MT7530_TRGMII_TD0_ODT 0x7a54
-+#define MT7530_TRGMII_TD1_ODT 0x7a5c
-+#define MT7530_TRGMII_TD2_ODT 0x7a64
-+#define MT7530_TRGMII_TD3_ODT 0x7a6c
-+#define MT7530_TRGMII_TD4_ODT 0x7a74
-+#define MT7530_TRGMII_TD5_ODT 0x7a7c
-+/* TRGMII TCK ctrl register */
-+#define MT7530_TRGMII_TCK_CTRL 0x7a78
-+/* TRGMII Tx ctrl register */
-+#define MT7530_TRGMII_TXCTRL 0x7a40
-+/* port 6 extended control register */
-+#define MT7530_P6ECR 0x7830
-+/* IO driver control register */
-+#define MT7530_IO_DRV_CR 0x7810
-+/* top signal control register */
-+#define MT7530_TOP_SIG_CTRL 0x7808
-+/* modified hwtrap register */
-+#define MT7530_MHWTRAP 0x7804
-+/* hwtrap status register */
-+#define MT7530_HWTRAP 0x7800
-+/* status interrupt register */
-+#define MT7530_SYS_INT_STS 0x700c
-+/* system nterrupt register */
-+#define MT7530_SYS_INT_EN 0x7008
-+/* system control register */
-+#define MT7530_SYS_CTRL 0x7000
-+/* port MAC status register */
-+#define MT7530_PMSR_P(x) (0x3008 + (x * 0x100))
-+/* port MAC control register */
-+#define MT7530_PMCR_P(x) (0x3000 + (x * 0x100))
-+
-+#define MT7621_XTAL_SHIFT 6
-+#define MT7621_XTAL_MASK 0x7
-+#define MT7621_XTAL_25 6
-+#define MT7621_XTAL_40 3
-+#define MT7621_MDIO_DRV_MASK (3 << 4)
-+#define MT7621_GE1_MODE_MASK (3 << 12)
-+
-+#define TRGMII_TXCTRL_TXC_INV BIT(30)
-+#define P6ECR_INTF_MODE_RGMII BIT(1)
-+#define P5RGMIIRXCR_C_ALIGN BIT(8)
-+#define P5RGMIIRXCR_DELAY_2 BIT(1)
-+#define P5RGMIITXCR_DELAY_2 (BIT(8) | BIT(2))
-+
-+/* TOP_SIG_CTRL bits */
-+#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
-+
-+/* MHWTRAP bits */
-+#define MHWTRAP_MANUAL BIT(16)
-+#define MHWTRAP_P5_MAC_SEL BIT(13)
-+#define MHWTRAP_P6_DIS BIT(8)
-+#define MHWTRAP_P5_RGMII_MODE BIT(7)
-+#define MHWTRAP_P5_DIS BIT(6)
-+#define MHWTRAP_PHY_ACCESS BIT(5)
-+
-+/* HWTRAP bits */
-+#define HWTRAP_XTAL_SHIFT 9
-+#define HWTRAP_XTAL_MASK 0x3
-+
-+/* SYS_CTRL bits */
-+#define SYS_CTRL_SW_RST BIT(1)
-+#define SYS_CTRL_REG_RST BIT(0)
-+
-+/* PMCR bits */
-+#define PMCR_IFG_XMIT_96 BIT(18)
-+#define PMCR_MAC_MODE BIT(16)
-+#define PMCR_FORCE_MODE BIT(15)
-+#define PMCR_TX_EN BIT(14)
-+#define PMCR_RX_EN BIT(13)
-+#define PMCR_BACK_PRES_EN BIT(9)
-+#define PMCR_BACKOFF_EN BIT(8)
-+#define PMCR_TX_FC_EN BIT(5)
-+#define PMCR_RX_FC_EN BIT(4)
-+#define PMCR_FORCE_SPEED_1000 BIT(3)
-+#define PMCR_FORCE_FDX BIT(1)
-+#define PMCR_FORCE_LNK BIT(0)
-+#define PMCR_FIXED_LINK (PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
-+ PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
-+ PMCR_BACK_PRES_EN | PMCR_BACKOFF_EN | \
-+ PMCR_FORCE_SPEED_1000 | PMCR_FORCE_FDX | \
-+ PMCR_FORCE_LNK)
-+
-+#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
-+ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
-+
-+/* TRGMII control registers */
-+#define GSW_INTF_MODE 0x390
-+#define GSW_TRGMII_TD0_ODT 0x354
-+#define GSW_TRGMII_TD1_ODT 0x35c
-+#define GSW_TRGMII_TD2_ODT 0x364
-+#define GSW_TRGMII_TD3_ODT 0x36c
-+#define GSW_TRGMII_TXCTL_ODT 0x374
-+#define GSW_TRGMII_TCK_ODT 0x37c
-+#define GSW_TRGMII_RCK_CTRL 0x300
-+
-+#define INTF_MODE_TRGMII BIT(1)
-+#define TRGMII_RCK_CTRL_RX_RST BIT(31)
-+
-+
-+/* possible XTAL speed */
-+#define MT7623_XTAL_40 0
-+#define MT7623_XTAL_20 1
-+#define MT7623_XTAL_25 3
-+
-+/* GPIO port control registers */
-+#define GPIO_OD33_CTRL8 0x4c0
-+#define GPIO_BIAS_CTRL 0xed0
-+#define GPIO_DRV_SEL10 0xf00
-+
-+/* on MT7620 the functio of port 4 can be software configured */
-+enum {
-+ PORT4_EPHY = 0,
-+ PORT4_EXT,
-+};
-+
-+/* struct mt7620_gsw - the structure that holds the SoC specific data
-+ * @dev: The Device struct
-+ * @base: The base address
-+ * @piac_offset: The PIAC base may change depending on SoC
-+ * @irq: The IRQ we are using
-+ * @port4: The port4 mode on MT7620
-+ * @autopoll: Is MDIO autopolling enabled
-+ * @ethsys: The ethsys register map
-+ * @pctl: The pin control register map
-+ * @clk_trgpll: The trgmii pll clock
-+ */
-+struct mt7620_gsw {
-+ struct mtk_eth *eth;
-+ struct device *dev;
-+ void __iomem *base;
-+ u32 piac_offset;
-+ int irq;
-+ int port4;
-+ unsigned long int autopoll;
-+
-+ struct regmap *ethsys;
-+ struct regmap *pctl;
-+
-+ struct clk *clk_trgpll;
-+
-+ int trgmii_force;
-+ bool wllll;
-+};
-+
-+/* switch register I/O wrappers */
-+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
-+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
-+
-+/* the callback used by the driver core to bringup the switch */
-+int mtk_gsw_init(struct mtk_eth *eth);
-+
-+/* MDIO access wrappers */
-+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 mtk_eth *eth, int port);
-+int mt7620_has_carrier(struct mtk_eth *eth);
-+void mt7620_print_link_state(struct mtk_eth *eth, 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);
-+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
-+
-+u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
-+ u32 phy_register, u32 write_data);
-+u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg);
-+void mt7620_handle_carrier(struct mtk_eth *eth);
-+
-+#endif
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c
-@@ -0,0 +1,1084 @@
-+/* 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-2016 John Crispin <blogic@openwrt.org>
-+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
-+ * Copyright (C) 2013-2016 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 <linux/of_gpio.h>
-+#include <linux/of_mdio.h>
-+#include <linux/clk.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/pm_runtime.h>
-+#include <linux/regmap.h>
-+#include <linux/reset.h>
-+#include <linux/mii.h>
-+#include <linux/interrupt.h>
-+#include <linux/netdevice.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/phy.h>
-+#include <linux/ethtool.h>
-+#include <linux/version.h>
-+#include <linux/atomic.h>
-+
-+#include "mtk_eth_soc.h"
-+#include "gsw_mt7620.h"
-+#include "mt7530.h"
-+
-+#define ETHSYS_CLKCFG0 0x2c
-+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
-+
-+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
-+{
-+ _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+ _mtk_mdio_write(gsw->eth, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
-+ _mtk_mdio_write(gsw->eth, 0x1f, 0x10, val >> 16);
-+}
-+
-+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
-+{
-+ u16 high, low;
-+
-+ _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+ low = _mtk_mdio_read(gsw->eth, 0x1f, (reg >> 2) & 0xf);
-+ high = _mtk_mdio_read(gsw->eth, 0x1f, 0x10);
-+
-+ return (high << 16) | (low & 0xffff);
-+}
-+
-+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
-+{
-+ u32 val = mt7530_mdio_r32(gsw, reg);
-+
-+ val &= mask;
-+ val |= set;
-+ mt7530_mdio_w32(gsw, reg, val);
-+}
-+
-+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
-+{
-+ mtk_w32(gsw->eth, val, reg + 0x10000);
-+}
-+
-+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
-+{
-+ return mtk_r32(gsw->eth, reg + 0x10000);
-+}
-+
-+void mtk_switch_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, unsigned reg)
-+{
-+ u32 val = mtk_switch_r32(gsw, reg);
-+
-+ val &= mask;
-+ val |= set;
-+
-+ mtk_switch_w32(gsw, val, reg);
-+}
-+
-+int mt7623_gsw_config(struct mtk_eth *eth)
-+{
-+ if (eth->mii_bus && eth->mii_bus->phy_map[0x1f])
-+ mt7530_probe(eth->dev, NULL, eth->mii_bus, 1);
-+
-+ return 0;
-+}
-+
-+static irqreturn_t gsw_interrupt_mt7623(int irq, void *_eth)
-+{
-+ struct mtk_eth *eth = (struct mtk_eth *)_eth;
-+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
-+ 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)
-+ dev_info(gsw->dev,
-+ "port %d link up\n", i);
-+ else
-+ dev_info(gsw->dev,
-+ "port %d link down\n", i);
-+ }
-+
-+// mt7620_handle_carrier(eth);
-+ mt7530_mdio_w32(gsw, 0x700c, 0x1f);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static void wait_loop(struct mt7620_gsw *gsw)
-+{
-+ int i;
-+ int read_data;
-+
-+ for (i = 0; i < 320; i = i + 1)
-+ read_data = mtk_switch_r32(gsw, 0x610);
-+}
-+
-+static void trgmii_calibration_7623(struct mt7620_gsw *gsw)
-+{
-+
-+ unsigned int tap_a[5] = { 0, 0, 0, 0, 0 }; /* minumum delay for all correct */
-+ unsigned int tap_b[5] = { 0, 0, 0, 0, 0 }; /* maximum delay for all correct */
-+ unsigned int final_tap[5];
-+ unsigned int rxc_step_size;
-+ unsigned int rxd_step_size;
-+ unsigned int read_data;
-+ unsigned int tmp;
-+ unsigned int rd_wd;
-+ int i;
-+ unsigned int err_cnt[5];
-+ unsigned int init_toggle_data;
-+ unsigned int err_flag[5];
-+ unsigned int err_total_flag;
-+ unsigned int training_word;
-+ unsigned int rd_tap;
-+ u32 val;
-+
-+ u32 TRGMII_7623_base;
-+ u32 TRGMII_7623_RD_0;
-+ u32 TRGMII_RCK_CTRL;
-+
-+ TRGMII_7623_base = 0x300; /* 0xFB110300 */
-+ TRGMII_7623_RD_0 = TRGMII_7623_base + 0x10;
-+ TRGMII_RCK_CTRL = TRGMII_7623_base;
-+ rxd_step_size = 0x1;
-+ rxc_step_size = 0x4;
-+ init_toggle_data = 0x00000055;
-+ training_word = 0x000000AC;
-+
-+ /* RX clock gating in MT7623 */
-+ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
-+
-+ /* Assert RX reset in MT7623 */
-+ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_base + 0x00);
-+
-+ /* Set TX OE edge in MT7623 */
-+ mtk_switch_m32(gsw, 0, 0x00002000, TRGMII_7623_base + 0x78);
-+
-+ /* Disable RX clock gating in MT7623 */
-+ mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
-+
-+ /* Release RX reset in MT7623 */
-+ mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base);
-+
-+ for (i = 0; i < 5; i++)
-+ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
-+
-+ pr_err("Enable Training Mode in MT7530\n");
-+ read_data = mt7530_mdio_r32(gsw, 0x7A40);
-+ read_data |= 0xC0000000;
-+ mt7530_mdio_w32(gsw, 0x7A40, read_data); /* Enable Training Mode in MT7530 */
-+ err_total_flag = 0;
-+ pr_err("Adjust RXC delay in MT7623\n");
-+ read_data = 0x0;
-+ while (err_total_flag == 0 && read_data != 0x68) {
-+ pr_err("2nd Enable EDGE CHK in MT7623\n");
-+ /* Enable EDGE CHK in MT7623 */
-+ for (i = 0; i < 5; i++)
-+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+
-+ wait_loop(gsw);
-+ err_total_flag = 1;
-+ for (i = 0; i < 5; i++) {
-+ err_cnt[i] =
-+ mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 8;
-+ err_cnt[i] &= 0x0000000f;
-+ rd_wd = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 16;
-+ rd_wd &= 0x000000ff;
-+ val = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+ pr_err("ERR_CNT = %d, RD_WD =%x, TRGMII_7623_RD_0=%x\n",
-+ err_cnt[i], rd_wd, val);
-+ if (err_cnt[i] != 0) {
-+ err_flag[i] = 1;
-+ } else if (rd_wd != 0x55) {
-+ err_flag[i] = 1;
-+ } else {
-+ err_flag[i] = 0;
-+ }
-+ err_total_flag = err_flag[i] & err_total_flag;
-+ }
-+
-+ pr_err("2nd Disable EDGE CHK in MT7623\n");
-+ /* Disable EDGE CHK in MT7623 */
-+ for (i = 0; i < 5; i++)
-+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+ wait_loop(gsw);
-+ pr_err("2nd Disable EDGE CHK in MT7623\n");
-+ /* Adjust RXC delay */
-+ /* RX clock gating in MT7623 */
-+ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
-+ read_data = mtk_switch_r32(gsw, TRGMII_7623_base);
-+ if (err_total_flag == 0) {
-+ tmp = (read_data & 0x0000007f) + rxc_step_size;
-+ pr_err(" RXC delay = %d\n", tmp);
-+ read_data >>= 8;
-+ read_data &= 0xffffff80;
-+ read_data |= tmp;
-+ read_data <<= 8;
-+ read_data &= 0xffffff80;
-+ read_data |= tmp;
-+ mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
-+ } else {
-+ tmp = (read_data & 0x0000007f) + 16;
-+ pr_err(" RXC delay = %d\n", tmp);
-+ read_data >>= 8;
-+ read_data &= 0xffffff80;
-+ read_data |= tmp;
-+ read_data <<= 8;
-+ read_data &= 0xffffff80;
-+ read_data |= tmp;
-+ mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
-+ }
-+ read_data &= 0x000000ff;
-+
-+ /* Disable RX clock gating in MT7623 */
-+ mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
-+ for (i = 0; i < 5; i++)
-+ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
-+ }
-+
-+ /* Read RD_WD MT7623 */
-+ for (i = 0; i < 5; i++) {
-+ rd_tap = 0;
-+ while (err_flag[i] != 0 && rd_tap != 128) {
-+ /* Enable EDGE CHK in MT7623 */
-+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+ wait_loop(gsw);
-+
-+ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+ err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */
-+ rd_wd = (read_data >> 16) & 0x000000ff;
-+ if (err_cnt[i] != 0 || rd_wd != 0x55) {
-+ err_flag[i] = 1;
-+ } else {
-+ err_flag[i] = 0;
-+ }
-+ /* Disable EDGE CHK in MT7623 */
-+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+ wait_loop(gsw);
-+ if (err_flag[i] != 0) {
-+ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */
-+ read_data = (read_data & 0xffffff80) | rd_tap;
-+ mtk_switch_w32(gsw, read_data,
-+ TRGMII_7623_RD_0 + i * 8);
-+ tap_a[i] = rd_tap;
-+ } else {
-+ rd_tap = (read_data & 0x0000007f) + 48;
-+ read_data = (read_data & 0xffffff80) | rd_tap;
-+ mtk_switch_w32(gsw, read_data,
-+ TRGMII_7623_RD_0 + i * 8);
-+ }
-+
-+ }
-+ pr_err("MT7623 %dth bit Tap_a = %d\n", i, tap_a[i]);
-+ }
-+ /* pr_err("Last While Loop\n"); */
-+ for (i = 0; i < 5; i++) {
-+ while ((err_flag[i] == 0) && (rd_tap != 128)) {
-+ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */
-+ read_data = (read_data & 0xffffff80) | rd_tap;
-+ mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
-+ /* Enable EDGE CHK in MT7623 */
-+ val =
-+ mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) | 0x40000000;
-+ val &= 0x4fffffff;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_RD_0 + i * 8);
-+ wait_loop(gsw);
-+ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+ err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */
-+ rd_wd = (read_data >> 16) & 0x000000ff;
-+ if (err_cnt[i] != 0 || rd_wd != 0x55) {
-+ err_flag[i] = 1;
-+ } else {
-+ err_flag[i] = 0;
-+ }
-+
-+ /* Disable EDGE CHK in MT7623 */
-+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+ wait_loop(gsw);
-+
-+ }
-+
-+ tap_b[i] = rd_tap; /* -rxd_step_size; */
-+ pr_err("MT7623 %dth bit Tap_b = %d\n", i, tap_b[i]);
-+ final_tap[i] = (tap_a[i] + tap_b[i]) / 2; /* Calculate RXD delay = (TAP_A + TAP_B)/2 */
-+ read_data = (read_data & 0xffffff80) | final_tap[i];
-+ mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
-+ }
-+
-+ read_data = mt7530_mdio_r32(gsw, 0x7A40);
-+ read_data &= 0x3fffffff;
-+ mt7530_mdio_w32(gsw, 0x7A40, read_data);
-+}
-+
-+static void trgmii_calibration_7530(struct mt7620_gsw *gsw)
-+{
-+
-+ unsigned int tap_a[5] = { 0, 0, 0, 0, 0 };
-+ unsigned int tap_b[5] = { 0, 0, 0, 0, 0 };
-+ unsigned int final_tap[5];
-+ unsigned int rxc_step_size;
-+ unsigned int rxd_step_size;
-+ unsigned int read_data;
-+ unsigned int tmp = 0;
-+ int i;
-+ unsigned int err_cnt[5];
-+ unsigned int rd_wd;
-+ unsigned int init_toggle_data;
-+ unsigned int err_flag[5];
-+ unsigned int err_total_flag;
-+ unsigned int training_word;
-+ unsigned int rd_tap;
-+
-+ u32 TRGMII_7623_base;
-+ u32 TRGMII_7530_RD_0;
-+ u32 TRGMII_RCK_CTRL;
-+ u32 TRGMII_7530_base;
-+ u32 TRGMII_7530_TX_base;
-+ u32 val;
-+
-+ TRGMII_7623_base = 0x300;
-+ TRGMII_7530_base = 0x7A00;
-+ TRGMII_7530_RD_0 = TRGMII_7530_base + 0x10;
-+ TRGMII_RCK_CTRL = TRGMII_7623_base;
-+ rxd_step_size = 0x1;
-+ rxc_step_size = 0x8;
-+ init_toggle_data = 0x00000055;
-+ training_word = 0x000000AC;
-+
-+ TRGMII_7530_TX_base = TRGMII_7530_base + 0x50;
-+
-+ /* pr_err("Calibration begin ........\n"); */
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+ read_data = mt7530_mdio_r32(gsw, 0x7a10);
-+ /* pr_err("TRGMII_7530_RD_0 is %x\n", read_data); */
-+
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+ read_data &= 0x3fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */
-+
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x78);
-+ read_data |= 0x00002000;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x78, read_data); /* Set TX OE edge in MT7530 */
-+
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+ read_data |= 0x80000000;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */
-+
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+ read_data &= 0x7fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */
-+
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+ read_data |= 0xC0000000;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */
-+
-+ /* pr_err("Enable Training Mode in MT7623\n"); */
-+ /*Enable Training Mode in MT7623 */
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+ if (gsw->trgmii_force == 2000) {
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0xC0000000;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+ } else {
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+ }
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x078) & 0xfffff0ff;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x078);
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x50) & 0xfffff0ff;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x50);
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x58) & 0xfffff0ff;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x58);
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x60) & 0xfffff0ff;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x60);
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x68) & 0xfffff0ff;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x68);
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x70) & 0xfffff0ff;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x70);
-+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x78) & 0x00000800;
-+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x78);
-+ err_total_flag = 0;
-+ /* pr_err("Adjust RXC delay in MT7530\n"); */
-+ read_data = 0x0;
-+ while (err_total_flag == 0 && (read_data != 0x68)) {
-+ /* pr_err("2nd Enable EDGE CHK in MT7530\n"); */
-+ /* Enable EDGE CHK in MT7530 */
-+ for (i = 0; i < 5; i++) {
-+ read_data =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+ read_data |= 0x40000000;
-+ read_data &= 0x4fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ wait_loop(gsw);
-+ /* pr_err("2nd Disable EDGE CHK in MT7530\n"); */
-+ err_cnt[i] =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+ /* pr_err("***** MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
-+ /* pr_err("MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
-+ err_cnt[i] >>= 8;
-+ err_cnt[i] &= 0x0000ff0f;
-+ rd_wd = err_cnt[i] >> 8;
-+ rd_wd &= 0x000000ff;
-+ err_cnt[i] &= 0x0000000f;
-+ /* read_data = mt7530_mdio_r32(gsw,0x7a10,&read_data); */
-+ if (err_cnt[i] != 0) {
-+ err_flag[i] = 1;
-+ } else if (rd_wd != 0x55) {
-+ err_flag[i] = 1;
-+ } else {
-+ err_flag[i] = 0;
-+ }
-+ if (i == 0) {
-+ err_total_flag = err_flag[i];
-+ } else {
-+ err_total_flag = err_flag[i] & err_total_flag;
-+ }
-+ /* Disable EDGE CHK in MT7530 */
-+ read_data =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+ read_data |= 0x40000000;
-+ read_data &= 0x4fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ wait_loop(gsw);
-+ }
-+ /*Adjust RXC delay */
-+ if (err_total_flag == 0) {
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+ read_data |= 0x80000000;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */
-+
-+ read_data =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+ read_data &= 0x3fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */
-+
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+ tmp = read_data;
-+ tmp &= 0x0000007f;
-+ tmp += rxc_step_size;
-+ /* pr_err("Current rxc delay = %d\n", tmp); */
-+ read_data &= 0xffffff80;
-+ read_data |= tmp;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+ /* pr_err("Current RXC delay = %x\n", read_data); */
-+
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+ read_data &= 0x7fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */
-+
-+ read_data =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+ read_data |= 0xc0000000;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */
-+ pr_err("####### MT7530 RXC delay is %d\n", tmp);
-+ }
-+ read_data = tmp;
-+ }
-+ pr_err("Finish RXC Adjustment while loop\n");
-+
-+ /* pr_err("Read RD_WD MT7530\n"); */
-+ /* Read RD_WD MT7530 */
-+ for (i = 0; i < 5; i++) {
-+ rd_tap = 0;
-+ while (err_flag[i] != 0 && rd_tap != 128) {
-+ /* Enable EDGE CHK in MT7530 */
-+ read_data =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+ read_data |= 0x40000000;
-+ read_data &= 0x4fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ wait_loop(gsw);
-+ err_cnt[i] = (read_data >> 8) & 0x0000000f;
-+ rd_wd = (read_data >> 16) & 0x000000ff;
-+ if (err_cnt[i] != 0 || rd_wd != 0x55) {
-+ err_flag[i] = 1;
-+ } else {
-+ err_flag[i] = 0;
-+ }
-+ if (err_flag[i] != 0) {
-+ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7530 */
-+ read_data = (read_data & 0xffffff80) | rd_tap;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ tap_a[i] = rd_tap;
-+ } else {
-+ tap_a[i] = (read_data & 0x0000007f); /* Record the min delay TAP_A */
-+ rd_tap = tap_a[i] + 0x4;
-+ read_data = (read_data & 0xffffff80) | rd_tap;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ }
-+
-+ /* Disable EDGE CHK in MT7530 */
-+ read_data =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+ read_data |= 0x40000000;
-+ read_data &= 0x4fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ wait_loop(gsw);
-+
-+ }
-+ pr_err("MT7530 %dth bit Tap_a = %d\n", i, tap_a[i]);
-+ }
-+
-+ /* pr_err("Last While Loop\n"); */
-+ for (i = 0; i < 5; i++) {
-+ rd_tap = 0;
-+ while (err_flag[i] == 0 && (rd_tap != 128)) {
-+ /* Enable EDGE CHK in MT7530 */
-+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+ read_data |= 0x40000000;
-+ read_data &= 0x4fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ wait_loop(gsw);
-+ err_cnt[i] = (read_data >> 8) & 0x0000000f;
-+ rd_wd = (read_data >> 16) & 0x000000ff;
-+ if (err_cnt[i] != 0 || rd_wd != 0x55)
-+ err_flag[i] = 1;
-+ else
-+ err_flag[i] = 0;
-+
-+ if (err_flag[i] == 0 && (rd_tap != 128)) {
-+ /* Add RXD delay in MT7530 */
-+ rd_tap = (read_data & 0x0000007f) + rxd_step_size;
-+ read_data = (read_data & 0xffffff80) | rd_tap;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ }
-+ /* Disable EDGE CHK in MT7530 */
-+ read_data =
-+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+ read_data |= 0x40000000;
-+ read_data &= 0x4fffffff;
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+ read_data);
-+ wait_loop(gsw);
-+ }
-+ tap_b[i] = rd_tap; /* - rxd_step_size; */
-+ pr_err("MT7530 %dth bit Tap_b = %d\n", i, tap_b[i]);
-+ /* Calculate RXD delay = (TAP_A + TAP_B)/2 */
-+ final_tap[i] = (tap_a[i] + tap_b[i]) / 2;
-+ /* pr_err("########****** MT7530 %dth bit Final Tap = %d\n", i, final_tap[i]); */
-+
-+ read_data = (read_data & 0xffffff80) | final_tap[i];
-+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, read_data);
-+ }
-+
-+ if (gsw->trgmii_force == 2000)
-+ mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base + 0x40);
-+ else
-+ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x40);
-+
-+}
-+
-+static void mt7530_trgmii_clock_setting(struct mt7620_gsw *gsw, u32 xtal_mode)
-+{
-+
-+ u32 regValue;
-+
-+ /* TRGMII Clock */
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x404);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+
-+ if (xtal_mode == 1) {
-+ /* 25MHz */
-+ if (gsw->trgmii_force == 2600)
-+ /* 325MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1a00);
-+ else if (gsw->trgmii_force == 2000)
-+ /* 250MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1400);
-+ } else if (xtal_mode == 2) {
-+ /* 40MHz */
-+ if (gsw->trgmii_force == 2600)
-+ /* 325MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1040);
-+ else if (gsw->trgmii_force == 2000)
-+ /* 250MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0c80);
-+ }
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x405);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x409);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ if (xtal_mode == 1)
-+ /* 25MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
-+ else
-+ /* 40MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40a);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ if (xtal_mode == 1)
-+ /* 25MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
-+ else
-+ /* 40MHz */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
-+
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x403);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1800);
-+
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x403);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1c00);
-+
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x401);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0xc020);
-+
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x406);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0xa030);
-+
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x406);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0xa038);
-+
-+// udelay(120); /* for MT7623 bring up test */
-+
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x3);
-+
-+ regValue = mt7530_mdio_r32(gsw, 0x7830);
-+ regValue &= 0xFFFFFFFC;
-+ regValue |= 0x00000001;
-+ mt7530_mdio_w32(gsw, 0x7830, regValue);
-+
-+ regValue = mt7530_mdio_r32(gsw, 0x7a40);
-+ regValue &= ~(0x1 << 30);
-+ regValue &= ~(0x1 << 28);
-+ mt7530_mdio_w32(gsw, 0x7a40, regValue);
-+
-+ mt7530_mdio_w32(gsw, 0x7a78, 0x55);
-+// udelay(100); /* for mt7623 bring up test */
-+
-+ mtk_switch_m32(gsw, 0x7fffffff, 0, 0x300);
-+
-+ trgmii_calibration_7623(gsw);
-+ trgmii_calibration_7530(gsw);
-+
-+ mtk_switch_m32(gsw, 0, 0x80000000, 0x300);
-+ mtk_switch_m32(gsw, 0, 0x7fffffff, 0x300);
-+
-+ /*MT7530 RXC reset */
-+ regValue = mt7530_mdio_r32(gsw, 0x7a00);
-+ regValue |= (0x1 << 31);
-+ mt7530_mdio_w32(gsw, 0x7a00, regValue);
-+ mdelay(1);
-+ regValue &= ~(0x1 << 31);
-+ mt7530_mdio_w32(gsw, 0x7a00, regValue);
-+ mdelay(100);
-+}
-+
-+static void mt7623_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw, struct device_node *np)
-+{
-+ u32 i;
-+ u32 val;
-+ u32 xtal_mode;
-+
-+ regmap_update_bits(gsw->ethsys, ETHSYS_CLKCFG0,
-+ ETHSYS_TRGMII_CLK_SEL362_5,
-+ ETHSYS_TRGMII_CLK_SEL362_5);
-+
-+ /* reset the TRGMII core */
-+ mtk_switch_m32(gsw, 0, INTF_MODE_TRGMII, GSW_INTF_MODE);
-+ /* Assert MT7623 RXC reset */
-+ mtk_switch_m32(gsw, 0, TRGMII_RCK_CTRL_RX_RST, GSW_TRGMII_RCK_CTRL);
-+
-+ /* Hardware reset Switch */
-+ device_reset(eth->dev);
-+
-+ /* Wait for Switch Reset Completed*/
-+ for (i = 0; i < 100; i++) {
-+ mdelay(10);
-+ if (mt7530_mdio_r32(gsw, MT7530_HWTRAP))
-+ break;
-+ }
-+
-+ /* turn off all PHYs */
-+ for (i = 0; i <= 4; i++) {
-+ val = _mtk_mdio_read(gsw->eth, i, 0x0);
-+ val |= BIT(11);
-+ _mtk_mdio_write(gsw->eth, i, 0x0, val);
-+ }
-+
-+ /* reset the switch */
-+ mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
-+ SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
-+ udelay(100);
-+
-+ /* GE1, Force 1000M/FD, FC ON */
-+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
-+
-+ /* GE2, Force 1000M/FD, FC ON */
-+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), PMCR_FIXED_LINK_FC);
-+
-+ /* Enable Port 6, P5 as GMAC5, P5 disable */
-+ val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
-+ if (gsw->eth->mac[0] &&
-+ of_phy_is_fixed_link(gsw->eth->mac[0]->of_node))
-+ /* Enable Port 6 */
-+ val &= ~MHWTRAP_P6_DIS;
-+ else
-+ /* Disable Port 6 */
-+ val |= MHWTRAP_P6_DIS;
-+ if (gsw->eth->mac[1] &&
-+ of_phy_is_fixed_link(gsw->eth->mac[1]->of_node)) {
-+ /* Enable Port 5 */
-+ val &= ~MHWTRAP_P5_DIS;
-+ /* Port 5 as PHY */
-+ val &= ~MHWTRAP_P5_MAC_SEL;
-+ } else {
-+ /* Disable Port 5 */
-+ val |= MHWTRAP_P5_DIS;
-+ /* Port 5 as GMAC */
-+ val |= MHWTRAP_P5_MAC_SEL;
-+ val |= BIT(7);
-+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), 0x8000);
-+ }
-+ /* gphy to port 0/4 */
-+ if (gsw->wllll)
-+ val |= BIT(20);
-+ else
-+ val &= ~BIT(20);
-+
-+ /* Set MT7530 phy direct access mode**/
-+ val &= ~MHWTRAP_PHY_ACCESS;
-+ /* manual override of HW-Trap */
-+ val |= MHWTRAP_MANUAL;
-+ mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
-+ dev_info(gsw->dev, "Setting MHWTRAP to 0x%08x\n", val);
-+
-+ val = mt7530_mdio_r32(gsw, 0x7800);
-+ val = (val >> 9) & 0x3;
-+ if (val == 0x3) {
-+ xtal_mode = 1;
-+ /* 25Mhz Xtal - do nothing */
-+ } else if (val == 0x2) {
-+ /* 40Mhz */
-+ xtal_mode = 2;
-+
-+ /* disable MT7530 core clock */
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0);
-+
-+ /* disable MT7530 PLL */
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x2020);
-+
-+ /* for MT7530 core clock = 500Mhz */
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40e);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x119);
-+
-+ /* enable MT7530 PLL */
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x2820);
-+
-+ udelay(20);
-+
-+ /* enable MT7530 core clock */
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+ } else {
-+ xtal_mode = 3;
-+ /* 20Mhz Xtal - TODO */
-+ }
-+
-+ /* RGMII */
-+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1);
-+
-+ /* set MT7530 central align */
-+ val = mt7530_mdio_r32(gsw, 0x7830);
-+ val &= ~1;
-+ val |= 1<<1;
-+ mt7530_mdio_w32(gsw, 0x7830, val);
-+
-+ val = mt7530_mdio_r32(gsw, 0x7a40);
-+ val &= ~(1<<30);
-+ mt7530_mdio_w32(gsw, 0x7a40, val);
-+
-+ mt7530_mdio_w32(gsw, 0x7a78, 0x855);
-+
-+ /* delay setting for 10/1000M */
-+ mt7530_mdio_w32(gsw, 0x7b00, 0x104);
-+ mt7530_mdio_w32(gsw, 0x7b04, 0x10);
-+
-+ /* lower Tx Driving */
-+ mt7530_mdio_w32(gsw, 0x7a54, 0x88);
-+ mt7530_mdio_w32(gsw, 0x7a5c, 0x88);
-+ mt7530_mdio_w32(gsw, 0x7a64, 0x88);
-+ mt7530_mdio_w32(gsw, 0x7a6c, 0x88);
-+ mt7530_mdio_w32(gsw, 0x7a74, 0x88);
-+ mt7530_mdio_w32(gsw, 0x7a7c, 0x88);
-+ mt7530_mdio_w32(gsw, 0x7810, 0x11);
-+
-+ /* Set MT7623/MT7683 TX Driving */
-+ mtk_switch_w32(gsw, 0x88, 0x354);
-+ mtk_switch_w32(gsw, 0x88, 0x35c);
-+ mtk_switch_w32(gsw, 0x88, 0x364);
-+ mtk_switch_w32(gsw, 0x88, 0x36c);
-+ mtk_switch_w32(gsw, 0x88, 0x374);
-+ mtk_switch_w32(gsw, 0x88, 0x37c);
-+
-+ /* Set GE2 driving and slew rate */
-+ regmap_write(gsw->pctl, 0xF00, 0xe00);
-+ /* set GE2 TDSEL */
-+ regmap_write(gsw->pctl, 0x4C0, 0x5);
-+ /* set GE2 TUNE */
-+ regmap_write(gsw->pctl, 0xED0, 0x0);
-+
-+ regmap_write(gsw->pctl, 0xb70, 0);
-+ regmap_write(gsw->pctl, 0x250, 0xffff);
-+ regmap_write(gsw->pctl, 0x260, 0xff);
-+ regmap_write(gsw->pctl, 0x380, 0x37);
-+ regmap_write(gsw->pctl, 0x390, 0x40);
-+
-+ mt7530_trgmii_clock_setting(gsw, xtal_mode);
-+
-+ //LANWANPartition(gsw);
-+
-+ /* disable EEE */
-+ for (i = 0; i <= 4; i++) {
-+ _mtk_mdio_write(gsw->eth, i, 13, 0x7);
-+ _mtk_mdio_write(gsw->eth, i, 14, 0x3C);
-+ _mtk_mdio_write(gsw->eth, i, 13, 0x4007);
-+ _mtk_mdio_write(gsw->eth, i, 14, 0x0);
-+
-+ /* Increase SlvDPSready time */
-+ _mtk_mdio_write(gsw->eth, i, 31, 0x52b5);
-+ _mtk_mdio_write(gsw->eth, i, 16, 0xafae);
-+ _mtk_mdio_write(gsw->eth, i, 18, 0x2f);
-+ _mtk_mdio_write(gsw->eth, i, 16, 0x8fae);
-+
-+ /* Incease post_update_timer */
-+ _mtk_mdio_write(gsw->eth, i, 31, 0x3);
-+ _mtk_mdio_write(gsw->eth, i, 17, 0x4b);
-+
-+ /* Adjust 100_mse_threshold */
-+ _mtk_mdio_write(gsw->eth, i, 13, 0x1e);
-+ _mtk_mdio_write(gsw->eth, i, 14, 0x123);
-+ _mtk_mdio_write(gsw->eth, i, 13, 0x401e);
-+ _mtk_mdio_write(gsw->eth, i, 14, 0xffff);
-+
-+ /* Disable mcc */
-+ _mtk_mdio_write(gsw->eth, i, 13, 0x1e);
-+ _mtk_mdio_write(gsw->eth, i, 14, 0xa6);
-+ _mtk_mdio_write(gsw->eth, i, 13, 0x401e);
-+ _mtk_mdio_write(gsw->eth, i, 14, 0x300);
-+
-+ /* Disable HW auto downshift*/
-+ _mtk_mdio_write(gsw->eth, i, 31, 0x1);
-+ val = _mtk_mdio_read(gsw->eth, i, 0x14);
-+ val &= ~(1<<4);
-+ _mtk_mdio_write(gsw->eth, i, 0x14, val);
-+ }
-+
-+ /* turn on all PHYs */
-+ for (i = 0; i <= 4; i++) {
-+ val = _mtk_mdio_read(gsw->eth, i, 0);
-+ val &= ~BIT(11);
-+ _mtk_mdio_write(gsw->eth, i, 0, val);
-+ }
-+
-+ /* enable irq */
-+ mt7530_mdio_m32(gsw, 0, TOP_SIG_CTRL_NORMAL, MT7530_TOP_SIG_CTRL);
-+}
-+
-+static const struct of_device_id mediatek_gsw_match[] = {
-+ { .compatible = "mediatek,mt7623-gsw" },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
-+
-+int mtk_gsw_init(struct mtk_eth *eth)
-+{
-+ struct device_node *np = eth->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);
-+ if (!gsw)
-+ return -ENODEV;
-+ gsw->eth = eth;
-+ eth->sw_priv = gsw;
-+
-+ mt7623_hw_init(eth, gsw, np);
-+
-+ if (request_threaded_irq(gsw->irq, gsw_interrupt_mt7623, NULL, 0,
-+ "gsw", eth))
-+ pr_err("fail to request irq\n");
-+ mt7530_mdio_w32(gsw, 0x7008, 0x1f);
-+
-+ return 0;
-+}
-+
-+static int mt7623_gsw_probe(struct platform_device *pdev)
-+{
-+ struct device_node *np = pdev->dev.of_node;
-+ struct device_node *pctl;
-+ int reset_pin, ret;
-+ struct mt7620_gsw *gsw;
-+ struct regulator *supply;
-+
-+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
-+ if (!gsw)
-+ return -ENOMEM;
-+
-+ gsw->dev = &pdev->dev;
-+ gsw->trgmii_force = 2000;
-+ gsw->irq = irq_of_parse_and_map(np, 0);
-+ if (gsw->irq < 0)
-+ return -EINVAL;
-+
-+ gsw->ethsys = syscon_regmap_lookup_by_phandle(np, "mediatek,ethsys");
-+ if (IS_ERR(gsw->ethsys))
-+ return PTR_ERR(gsw->ethsys);
-+
-+ reset_pin = of_get_named_gpio(np, "mediatek,reset-pin", 0);
-+ if (reset_pin < 0)
-+ return reset_pin;
-+
-+ pctl = of_parse_phandle(np, "mediatek,pctl-regmap", 0);
-+ if (IS_ERR(pctl))
-+ return PTR_ERR(pctl);
-+
-+ gsw->pctl = syscon_node_to_regmap(pctl);
-+ if (IS_ERR(pctl))
-+ return PTR_ERR(pctl);
-+
-+ ret = devm_gpio_request(&pdev->dev, reset_pin, "mt7530-reset");
-+ if (ret)
-+ return ret;
-+
-+ gsw->clk_trgpll = devm_clk_get(&pdev->dev, "trgpll");
-+
-+ if (IS_ERR(gsw->clk_trgpll))
-+ return -ENODEV;
-+
-+ supply = devm_regulator_get(&pdev->dev, "mt7530");
-+ if (IS_ERR(supply))
-+ return PTR_ERR(supply);
-+
-+ regulator_set_voltage(supply, 1000000, 1000000);
-+ ret = regulator_enable(supply);
-+ if (ret) {
-+ dev_err(&pdev->dev, "Failed to enable reg-7530: %d\n", ret);
-+ return ret;
-+ }
-+
-+ gsw->wllll = of_property_read_bool(np, "mediatek,wllll");
-+
-+ pm_runtime_enable(&pdev->dev);
-+ pm_runtime_get_sync(&pdev->dev);
-+
-+ ret = clk_set_rate(gsw->clk_trgpll, 500000000);
-+ if (ret)
-+ return ret;
-+
-+ regmap_write(gsw->ethsys, 0x34, 0x800000);
-+ regmap_write(gsw->ethsys, 0x34, 0x0);
-+
-+ clk_prepare_enable(gsw->clk_trgpll);
-+
-+ gpio_direction_output(reset_pin, 0);
-+ udelay(1000);
-+ gpio_set_value(reset_pin, 1);
-+ mdelay(100);
-+
-+ platform_set_drvdata(pdev, gsw);
-+
-+ return 0;
-+}
-+
-+static int mt7623_gsw_remove(struct platform_device *pdev)
-+{
-+ struct mt7620_gsw *gsw = platform_get_drvdata(pdev);
-+
-+ clk_disable_unprepare(gsw->clk_trgpll);
-+
-+ pm_runtime_put_sync(&pdev->dev);
-+ pm_runtime_disable(&pdev->dev);
-+
-+ platform_set_drvdata(pdev, NULL);
-+
-+ return 0;
-+}
-+
-+static struct platform_driver gsw_driver = {
-+ .probe = mt7623_gsw_probe,
-+ .remove = mt7623_gsw_remove,
-+ .driver = {
-+ .name = "mt7623-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 MT7623 SoC");
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mt7530.c
-@@ -0,0 +1,808 @@
-+/*
-+ * 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 *eth = container_of(dev, struct mt7530_priv, swdev);
-+ int i;
-+
-+ memset(eth->port_entries, 0, sizeof(eth->port_entries));
-+ memset(eth->vlan_entries, 0, sizeof(eth->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++) {
-+ eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
-+
-+ val->value.i = eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
-+
-+ eth->global_vlan_enable = val->value.i != 0;
-+
-+ return 0;
-+}
-+
-+static u32
-+mt7530_r32(struct mt7530_priv *eth, u32 reg)
-+{
-+ u32 val;
-+ if (eth->bus) {
-+ u16 high, low;
-+
-+ mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+ low = mdiobus_read(eth->bus, 0x1f, (reg >> 2) & 0xf);
-+ high = mdiobus_read(eth->bus, 0x1f, 0x10);
-+
-+ return (high << 16) | (low & 0xffff);
-+ }
-+
-+ val = ioread32(eth->base + reg);
-+ pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
-+
-+ return val;
-+}
-+
-+static void
-+mt7530_w32(struct mt7530_priv *eth, u32 reg, u32 val)
-+{
-+ if (eth->bus) {
-+ mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+ mdiobus_write(eth->bus, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
-+ mdiobus_write(eth->bus, 0x1f, 0x10, val >> 16);
-+ return;
-+ }
-+
-+ pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
-+ iowrite32(val, eth->base + reg);
-+}
-+
-+static void
-+mt7530_vtcr(struct mt7530_priv *eth, u32 cmd, u32 val)
-+{
-+ int i;
-+
-+ mt7530_w32(eth, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
-+
-+ for (i = 0; i < 20; i++) {
-+ u32 val = mt7530_r32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
-+
-+ if (port >= MT7530_NUM_PORTS)
-+ return -EINVAL;
-+
-+ *val = mt7530_r32(eth, 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 *eth = 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;
-+
-+ eth->port_entries[port].pvid = pvid;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
-+{
-+ struct mt7530_priv *eth = 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(eth, 0, val->port_vlan);
-+
-+ member = mt7530_r32(eth, REG_ESW_VLAN_VAWD1);
-+ member >>= 16;
-+ member &= 0xff;
-+
-+ etags = mt7530_r32(eth, 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 *eth = 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);
-+ }
-+ eth->vlan_entries[val->port_vlan].member = member;
-+ eth->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 *eth = 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;
-+
-+ eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
-+ u32 vid;
-+ int vlan;
-+
-+ vlan = val->port_vlan;
-+
-+ vid = mt7530_r32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
-+ int i, j;
-+ u8 tag_ports;
-+ u8 untag_ports;
-+
-+ if (!eth->global_vlan_enable) {
-+ for (i = 0; i < MT7530_NUM_PORTS; i++)
-+ mt7530_w32(eth, REG_ESW_PORT_PCR(i), 0x00ff0000);
-+
-+ for (i = 0; i < MT7530_NUM_PORTS; i++)
-+ mt7530_w32(eth, REG_ESW_PORT_PVC(i), 0x810000c0);
-+
-+ return 0;
-+ }
-+
-+ /* set all ports as security mode */
-+ for (i = 0; i < MT7530_NUM_PORTS; i++)
-+ mt7530_w32(eth, 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 = eth->vlan_entries[i].member;
-+ u8 etags = eth->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(eth, REG_ESW_PORT_PVC(i), pvc_mode);
-+ }
-+
-+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
-+ u16 vid = eth->vlan_entries[i].vid;
-+ u8 member = eth->vlan_entries[i].member;
-+ u8 etags = eth->vlan_entries[i].etags;
-+ u32 val;
-+
-+ /* vid of vlan */
-+ val = mt7530_r32(eth, REG_ESW_VLAN_VTIM(i));
-+ if (i % 2 == 0) {
-+ val &= 0xfff000;
-+ val |= vid;
-+ } else {
-+ val &= 0xfff;
-+ val |= (vid << 12);
-+ }
-+ mt7530_w32(eth, REG_ESW_VLAN_VTIM(i), val);
-+
-+ /* vlan port membership */
-+ if (member)
-+ mt7530_w32(eth, 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(eth, 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(eth, REG_ESW_VLAN_VAWD2, val);
-+
-+ /* write to vlan table */
-+ mt7530_vtcr(eth, 1, i);
-+ }
-+
-+ /* Port Default PVID */
-+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+ u32 val;
-+ val = mt7530_r32(eth, REG_ESW_PORT_PPBV1(i));
-+ val &= ~0xfff;
-+ val |= eth->port_entries[i].pvid;
-+ mt7530_w32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
-+ u32 speed, pmsr;
-+
-+ if (port < 0 || port >= MT7530_NUM_PORTS)
-+ return -EINVAL;
-+
-+ pmsr = mt7530_r32(eth, 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 *eth, 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(eth, port_base + mt7621_mibs[i].offset);
-+ if (mt7621_mibs[i].size == 2) {
-+ u64 hi;
-+
-+ hi = mt7530_r32(eth, 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 *eth = 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(eth, 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_MACH_MT7623)) {
-+ swdev->alias = "mt7623";
-+ swdev->name = "mt7623";
-+ } 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) || IS_ENABLED(CONFIG_MACH_MT7623))
-+ 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;
-+ }
-+
-+ mt7530_reset_switch(swdev);
-+
-+ 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) || IS_ENABLED(CONFIG_MACH_MT7623)) && 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;
-+}
---- /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
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -24,6 +24,9 @@
-
- #include "mtk_eth_soc.h"
-
-+/* the callback used by the driver core to bringup the switch */
-+int mtk_gsw_init(struct mtk_eth *eth);
-+
- static int mtk_msg_level = -1;
- module_param_named(msg_level, mtk_msg_level, int, 0);
- MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
-@@ -69,7 +72,7 @@ static int mtk_mdio_busy_wait(struct mtk
- return 0;
- if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
- break;
-- usleep_range(10, 20);
-+// usleep_range(10, 20);
- }
-
- dev_err(eth->dev, "mdio: MDIO timeout\n");
-@@ -1408,15 +1411,6 @@ static int __init mtk_hw_init(struct mtk
- reset_control_deassert(eth->rstc);
- usleep_range(10, 20);
-
-- /* Set GE2 driving and slew rate */
-- regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00);
--
-- /* set GE2 TDSEL */
-- regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5);
--
-- /* set GE2 TUNE */
-- regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
--
- /* GE1, Force 1000M/FD, FC ON */
- mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0));
-
-@@ -1439,6 +1433,8 @@ static int __init mtk_hw_init(struct mtk
- if (err)
- return err;
-
-+ mtk_gsw_init(eth);
-+
- /* disable delay and normal interrupt */
- mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
- mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
-@@ -1466,6 +1462,8 @@ static int __init mtk_hw_init(struct mtk
- mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
- }
-
-+ mt7623_gsw_config(eth);
-+
- return 0;
- }
-
-@@ -1721,7 +1719,7 @@ static int mtk_add_mac(struct mtk_eth *e
- {
- struct mtk_mac *mac;
- const __be32 *_id = of_get_property(np, "reg", NULL);
-- int id, err;
-+ int id;
-
- if (!_id) {
- dev_err(eth->dev, "missing mac id\n");
-@@ -1755,8 +1753,8 @@ static int mtk_add_mac(struct mtk_eth *e
- GFP_KERNEL);
- if (!mac->hw_stats) {
- dev_err(eth->dev, "failed to allocate counter memory\n");
-- err = -ENOMEM;
-- goto free_netdev;
-+ free_netdev(eth->netdev[id]);
-+ return -ENOMEM;
- }
- spin_lock_init(&mac->hw_stats->stats_lock);
- mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
-@@ -1770,21 +1768,9 @@ static int mtk_add_mac(struct mtk_eth *e
- eth->netdev[id]->features |= MTK_HW_FEATURES;
- eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
-
-- err = register_netdev(eth->netdev[id]);
-- if (err) {
-- dev_err(eth->dev, "error bringing up device\n");
-- goto free_netdev;
-- }
- eth->netdev[id]->irq = eth->irq[0];
-- netif_info(eth, probe, eth->netdev[id],
-- "mediatek frame engine at 0x%08lx, irq %d\n",
-- eth->netdev[id]->base_addr, eth->irq[0]);
-
- return 0;
--
--free_netdev:
-- free_netdev(eth->netdev[id]);
-- return err;
- }
-
- static int mtk_probe(struct platform_device *pdev)
-@@ -1852,14 +1838,13 @@ static int mtk_probe(struct platform_dev
- clk_prepare_enable(eth->clk_gp1);
- clk_prepare_enable(eth->clk_gp2);
-
-+ eth->switch_np = of_parse_phandle(pdev->dev.of_node,
-+ "mediatek,switch", 0);
-+
- eth->dev = &pdev->dev;
- eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
- INIT_WORK(ð->pending_work, mtk_pending_work);
-
-- err = mtk_hw_init(eth);
-- if (err)
-- return err;
--
- for_each_child_of_node(pdev->dev.of_node, mac_np) {
- if (!of_device_is_compatible(mac_np,
- "mediatek,eth-mac"))
-@@ -1873,6 +1858,22 @@ static int mtk_probe(struct platform_dev
- goto err_free_dev;
- }
-
-+ err = mtk_hw_init(eth);
-+ if (err)
-+ return err;
-+
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
-+ if (!eth->netdev[i])
-+ continue;
-+ err = register_netdev(eth->netdev[i]);
-+ if (err)
-+ dev_err(eth->dev, "error bringing up device\n");
-+ else
-+ netif_info(eth, probe, eth->netdev[i],
-+ "mediatek frame engine at 0x%08lx, irq %d\n",
-+ eth->netdev[i]->base_addr, eth->irq[0]);
-+ }
-+
- /* we run 2 devices on the same DMA ring so we need a dummy device
- * for NAPI to work
- */
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -406,6 +406,8 @@ struct mtk_eth {
- struct mii_bus *mii_bus;
- struct work_struct pending_work;
-
-+ struct device_node *switch_np;
-+ void *sw_priv;
- };
-
- /* struct mtk_mac - the structure that holds the info about the MACs of the
-@@ -433,4 +435,6 @@ void mtk_stats_update_mac(struct mtk_mac
- void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
- u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
-
-+int mt7623_gsw_config(struct mtk_eth *eth);
-+
- #endif /* MTK_ETH_H */
--- /dev/null
+From 2cbf3f95a49925317ef4138ceaf7f7f30f353f0f Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 03:17:53 +0200
+Subject: [PATCH 089/102] net-next: mediatek: increase watchdog_timeo
+
+During stress testing, after reducing the threshold value, we have seen
+TX timeouts that were caused by the watchdog_timeo value being too low.
+Increase the value to 5 * HZ which is a value commonly used by many other
+drivers.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index d03f339..52cdb3a 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1720,7 +1720,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+
+ SET_NETDEV_DEV(eth->netdev[id], eth->dev);
+- eth->netdev[id]->watchdog_timeo = HZ;
++ eth->netdev[id]->watchdog_timeo = 5 * HZ;
+ eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
+ eth->netdev[id]->base_addr = (unsigned long)eth->base;
+ eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
+--
+1.7.10.4
+
+++ /dev/null
-From 7d2bdde21bc72eb41878890149f2b9d05fc3af6e Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Mon, 11 Apr 2016 06:00:23 +0200
-Subject: [PATCH 90/91] net: mediatek: v4.4 backports
-
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 23 ++++++++++++++---------
- 1 file changed, 14 insertions(+), 9 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -207,7 +207,7 @@ static int mtk_phy_connect_node(struct m
-
- dev_info(eth->dev,
- "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n",
-- mac->id, phydev_name(phydev), phydev->phy_id,
-+ mac->id, dev_name(&phydev->dev), phydev->phy_id,
- phydev->drv->name);
-
- mac->phy_dev = phydev;
-@@ -1268,9 +1268,10 @@ static irqreturn_t mtk_handle_irq_rx(int
- return IRQ_NONE;
-
- if (status & MTK_RX_DONE_INT) {
-- if (likely(napi_schedule_prep(ð->rx_napi)))
-+ if (likely(napi_schedule_prep(ð->rx_napi))) {
-+ mtk_irq_disable(eth, MTK_RX_DONE_INT);
- __napi_schedule(ð->rx_napi);
-- mtk_irq_disable(eth, MTK_RX_DONE_INT);
-+ }
- }
- mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
-
-@@ -1289,9 +1290,10 @@ static irqreturn_t mtk_handle_irq_tx(int
- return IRQ_NONE;
-
- if (status & MTK_TX_DONE_INT) {
-- if (likely(napi_schedule_prep(ð->tx_napi)))
-+ if (likely(napi_schedule_prep(ð->tx_napi))) {
-+ mtk_irq_disable(eth, MTK_TX_DONE_INT);
- __napi_schedule(ð->tx_napi);
-- mtk_irq_disable(eth, MTK_TX_DONE_INT);
-+ }
- }
- mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
-
-@@ -1383,6 +1385,7 @@ static int mtk_stop(struct net_device *d
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-
-+ netif_carrier_off(dev);
- netif_tx_disable(dev);
- phy_stop(mac->phy_dev);
-
-@@ -1582,11 +1585,13 @@ static int mtk_set_settings(struct net_d
- {
- struct mtk_mac *mac = netdev_priv(dev);
-
-- if (cmd->phy_address != mac->phy_dev->mdio.addr) {
-- mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus,
-- cmd->phy_address);
-- if (!mac->phy_dev)
-+ if (cmd->phy_address != mac->phy_dev->addr) {
-+ if (mac->hw->mii_bus->phy_map[cmd->phy_address]) {
-+ mac->phy_dev =
-+ mac->hw->mii_bus->phy_map[cmd->phy_address];
-+ } else {
- return -ENODEV;
-+ }
- }
-
- return phy_ethtool_sset(mac->phy_dev, cmd);
--- /dev/null
+From 94425de9ede5ef0eafbfced65140c30e7c0b6c0d Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 03:01:13 +0200
+Subject: [PATCH 090/102] net-next: mediatek: fix off by one in the TX ring
+ allocation
+
+The TX ring setup has an off by one error causing it to not utilise all
+descriptors. This has the side effect that we need to reset the next
+pointer at runtime to make it work. Fix the off by one and remove the
+code fixing the ring at runtime.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 52cdb3a..87b48c0 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -934,7 +934,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ }
+ mtk_tx_unmap(eth->dev, tx_buf);
+
+- ring->last_free->txd2 = next_cpu;
+ ring->last_free = desc;
+ atomic_inc(&ring->free_count);
+
+@@ -1042,7 +1041,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
+
+ atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
+ ring->next_free = &ring->dma[0];
+- ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
++ ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
+ ring->thresh = MAX_SKB_FRAGS;
+
+ /* make sure that all changes to the dma ring are flushed before we
+--
+1.7.10.4
+
+++ /dev/null
-From 34e10b96d5ccb99fb78251051bc5652b09359983 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Thu, 28 Apr 2016 07:58:22 +0200
-Subject: [PATCH 91/91] net-next: mediatek WIP
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 89 ++++++++++++---------------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 +-
- 2 files changed, 44 insertions(+), 50 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -326,7 +326,7 @@ static inline void mtk_irq_disable(struc
- val = mtk_r32(eth, MTK_QDMA_INT_MASK);
- mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
- /* flush write */
-- mtk_r32(eth, MTK_QDMA_INT_MASK);
-+// mtk_r32(eth, MTK_QDMA_INT_MASK);
- spin_unlock_irqrestore(ð->irq_lock, flags);
- }
-
-@@ -339,7 +339,7 @@ static inline void mtk_irq_enable(struct
- val = mtk_r32(eth, MTK_QDMA_INT_MASK);
- mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
- /* flush write */
-- mtk_r32(eth, MTK_QDMA_INT_MASK);
-+// mtk_r32(eth, MTK_QDMA_INT_MASK);
- spin_unlock_irqrestore(ð->irq_lock, flags);
- }
-
-@@ -710,10 +710,26 @@ static inline int mtk_cal_txd_req(struct
- return nfrags;
- }
-
-+static int mtk_queue_stopped(struct mtk_eth *eth)
-+{
-+ int i;
-+
-+ for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ if (!eth->netdev[i])
-+ continue;
-+ if (netif_queue_stopped(eth->netdev[i]))
-+ return 1;
-+ }
-+
-+ return 0;
-+}
-+
- static void mtk_wake_queue(struct mtk_eth *eth)
- {
- int i;
-
-+ printk("%s:%s[%d]w\n", __FILE__, __func__, __LINE__);
-+
- for (i = 0; i < MTK_MAC_COUNT; i++) {
- if (!eth->netdev[i])
- continue;
-@@ -725,6 +741,7 @@ static void mtk_stop_queue(struct mtk_et
- {
- int i;
-
-+ printk("%s:%s[%d]s\n", __FILE__, __func__, __LINE__);
- for (i = 0; i < MTK_MAC_COUNT; i++) {
- if (!eth->netdev[i])
- continue;
-@@ -775,12 +792,9 @@ static int mtk_start_xmit(struct sk_buff
- if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
- goto drop;
-
-- if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
-+ if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
- mtk_stop_queue(eth);
-- if (unlikely(atomic_read(&ring->free_count) >
-- ring->thresh))
-- mtk_wake_queue(eth);
-- }
-+
- spin_unlock_irqrestore(ð->page_lock, flags);
-
- return NETDEV_TX_OK;
-@@ -927,7 +941,6 @@ static int mtk_poll_tx(struct mtk_eth *e
- }
- mtk_tx_unmap(eth->dev, tx_buf);
-
-- ring->last_free->txd2 = next_cpu;
- ring->last_free = desc;
- atomic_inc(&ring->free_count);
-
-@@ -945,11 +958,8 @@ static int mtk_poll_tx(struct mtk_eth *e
- netdev_completed_queue(eth->netdev[i], done, bytes);
- }
-
-- /* read hw index again make sure no new tx packet */
-- if (cpu == dma && cpu == mtk_r32(eth, MTK_QTX_DRX_PTR))
-- mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
--
-- if (atomic_read(&ring->free_count) > ring->thresh)
-+ if (mtk_queue_stopped(eth) &&
-+ (atomic_read(&ring->free_count) > ring->thresh))
- mtk_wake_queue(eth);
-
- return done;
-@@ -973,10 +983,11 @@ static int mtk_napi_tx(struct napi_struc
- int tx_done = 0;
-
- mtk_handle_status_irq(eth);
--
-- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-+ mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
- tx_done = mtk_poll_tx(eth, budget);
-+
- if (unlikely(netif_msg_intr(eth))) {
-+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
- mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
- dev_info(eth->dev,
- "done tx %d, intr 0x%08x/0x%x\n",
-@@ -1002,9 +1013,12 @@ static int mtk_napi_rx(struct napi_struc
- u32 status, mask;
- int rx_done = 0;
-
-- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-+ mtk_handle_status_irq(eth);
-+ mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
- rx_done = mtk_poll_rx(napi, budget, eth);
-+
- if (unlikely(netif_msg_intr(eth))) {
-+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
- mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
- dev_info(eth->dev,
- "done rx %d, intr 0x%08x/0x%x\n",
-@@ -1052,9 +1066,8 @@ static int mtk_tx_alloc(struct mtk_eth *
-
- atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
- ring->next_free = &ring->dma[0];
-- ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
-- ring->thresh = max((unsigned long)MTK_DMA_SIZE >> 2,
-- MAX_SKB_FRAGS);
-+ ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
-+ ring->thresh = MAX_SKB_FRAGS;
-
- /* make sure that all changes to the dma ring are flushed before we
- * continue
-@@ -1259,21 +1272,11 @@ static void mtk_tx_timeout(struct net_de
- static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
- {
- struct mtk_eth *eth = _eth;
-- u32 status;
--
-- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-- status &= ~MTK_TX_DONE_INT;
--
-- if (unlikely(!status))
-- return IRQ_NONE;
-
-- if (status & MTK_RX_DONE_INT) {
-- if (likely(napi_schedule_prep(ð->rx_napi))) {
-- mtk_irq_disable(eth, MTK_RX_DONE_INT);
-- __napi_schedule(ð->rx_napi);
-- }
-+ if (likely(napi_schedule_prep(ð->rx_napi))) {
-+ __napi_schedule(ð->rx_napi);
-+ mtk_irq_disable(eth, MTK_RX_DONE_INT);
- }
-- mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
-
- return IRQ_HANDLED;
- }
-@@ -1281,21 +1284,11 @@ static irqreturn_t mtk_handle_irq_rx(int
- static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
- {
- struct mtk_eth *eth = _eth;
-- u32 status;
--
-- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-- status &= ~MTK_RX_DONE_INT;
--
-- if (unlikely(!status))
-- return IRQ_NONE;
-
-- if (status & MTK_TX_DONE_INT) {
-- if (likely(napi_schedule_prep(ð->tx_napi))) {
-- mtk_irq_disable(eth, MTK_TX_DONE_INT);
-- __napi_schedule(ð->tx_napi);
-- }
-+ if (likely(napi_schedule_prep(ð->tx_napi))) {
-+ __napi_schedule(ð->tx_napi);
-+ mtk_irq_disable(eth, MTK_TX_DONE_INT);
- }
-- mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
-
- return IRQ_HANDLED;
- }
-@@ -1326,7 +1319,7 @@ static int mtk_start_dma(struct mtk_eth
- mtk_w32(eth,
- MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN |
- MTK_RX_2B_OFFSET | MTK_DMA_SIZE_16DWORDS |
-- MTK_RX_BT_32DWORDS,
-+ MTK_RX_BT_32DWORDS | MTK_NDP_CO_PRO,
- MTK_QDMA_GLO_CFG);
-
- return 0;
-@@ -1440,7 +1433,7 @@ static int __init mtk_hw_init(struct mtk
-
- /* disable delay and normal interrupt */
- mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
-- mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
-+ mtk_irq_disable(eth, ~0);
- mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
- mtk_w32(eth, 0, MTK_RST_GL);
-
-@@ -1765,7 +1758,7 @@ static int mtk_add_mac(struct mtk_eth *e
- mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
-
- SET_NETDEV_DEV(eth->netdev[id], eth->dev);
-- eth->netdev[id]->watchdog_timeo = HZ;
-+ eth->netdev[id]->watchdog_timeo = 4 * HZ;
- eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
- eth->netdev[id]->base_addr = (unsigned long)eth->base;
- eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -18,9 +18,9 @@
- #define MTK_QDMA_PAGE_SIZE 2048
- #define MTK_MAX_RX_LENGTH 1536
- #define MTK_TX_DMA_BUF_LEN 0x3fff
--#define MTK_DMA_SIZE 256
--#define MTK_NAPI_WEIGHT 64
- #define MTK_MAC_COUNT 2
-+#define MTK_DMA_SIZE (256 * MTK_MAC_COUNT)
-+#define MTK_NAPI_WEIGHT (64 * MTK_MAC_COUNT)
- #define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
- #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
- #define MTK_DMA_DUMMY_DESC 0xffffffff
-@@ -95,6 +95,7 @@
- #define MTK_QDMA_GLO_CFG 0x1A04
- #define MTK_RX_2B_OFFSET BIT(31)
- #define MTK_RX_BT_32DWORDS (3 << 11)
-+#define MTK_NDP_CO_PRO BIT(10)
- #define MTK_TX_WB_DDONE BIT(6)
- #define MTK_DMA_SIZE_16DWORDS (2 << 4)
- #define MTK_RX_DMA_BUSY BIT(3)
--- /dev/null
+From 1473b4cce85760c0202a08e6a48ec51867dc1bf7 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 04:01:38 +0200
+Subject: [PATCH 091/102] net-next: mediatek: only wake the queue if it is
+ stopped
+
+The current code unconditionally wakes up the queue at the end of each
+tx_poll action. Change the code to only wake up the queues if any of
+them have actually been stopped before.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 87b48c0..9da533e 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -716,6 +716,20 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
+ return nfrags;
+ }
+
++static int mtk_queue_stopped(struct mtk_eth *eth)
++{
++ int i;
++
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ if (!eth->netdev[i])
++ continue;
++ if (netif_queue_stopped(eth->netdev[i]))
++ return 1;
++ }
++
++ return 0;
++}
++
+ static void mtk_wake_queue(struct mtk_eth *eth)
+ {
+ int i;
+@@ -960,7 +974,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ if (!total)
+ return 0;
+
+- if (atomic_read(&ring->free_count) > ring->thresh)
++ if (mtk_queue_stopped(eth) &&
++ (atomic_read(&ring->free_count) > ring->thresh))
+ mtk_wake_queue(eth);
+
+ return total;
+--
+1.7.10.4
+
--- /dev/null
+From 538020913db04d199ce4d7e845444880e8200b5f Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 05:40:38 +0200
+Subject: [PATCH 092/102] net-next: mediatek: remove superfluous queue wake up
+ call
+
+The code checks if the queue should be stopped because we are below the
+threshold of free descriptors only to check if it should be started again.
+If we do end up in a state where we are at the threshold limit, it makes
+more sense to just stop the queue and wait for the next IRQ to trigger the
+TX housekeeping again. There is no rush in enqueuing the next packet, it
+needs to wait for all the others in the queue to be dispatched first
+anyway.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 9da533e..a569c32 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -795,12 +795,9 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
+ goto drop;
+
+- if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
++ if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
+ mtk_stop_queue(eth);
+- if (unlikely(atomic_read(&ring->free_count) >
+- ring->thresh))
+- mtk_wake_queue(eth);
+- }
++
+ spin_unlock_irqrestore(ð->page_lock, flags);
+
+ return NETDEV_TX_OK;
+--
+1.7.10.4
+
--- /dev/null
+From 31428406bf4b9da2a322ae947096414ff0489fb5 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 03:57:01 +0200
+Subject: [PATCH 093/102] net-next: mediatek: remove superfluous register
+ reads
+
+The driver was originally written for MIPS based SoC. These required the
+IRQ mask register to be read after writing it to ensure that the content
+was actually applied. As this version only works on ARM based SoC, we can
+safely remove the 2 reads as they ware not required.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index a569c32..6a9fbde 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -335,8 +335,6 @@ static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
+
+ val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+ mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
+- /* flush write */
+- mtk_r32(eth, MTK_QDMA_INT_MASK);
+ }
+
+ static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
+@@ -345,8 +343,6 @@ static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
+
+ val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+ mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
+- /* flush write */
+- mtk_r32(eth, MTK_QDMA_INT_MASK);
+ }
+
+ static int mtk_set_mac_address(struct net_device *dev, void *p)
+--
+1.7.10.4
+
--- /dev/null
+From 441d87495f33fd444a2b2a16f6df07892dac3f89 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 3 May 2016 04:12:35 +0200
+Subject: [PATCH 094/102] net-next: mediatek: don't use intermediate variables
+ to store IRQ masks
+
+The code currently uses variables to store and never modify the bit masks
+of interrupts. This is legacy code from an early version of the driver
+that supported MIPS based SoCs where the IRQ bits depended on the actual
+SoC. As the bits are the same for all ARM based SoC using this driver we
+can remove the intermediate variables.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 22 ++++++++++------------
+ 1 file changed, 10 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 6a9fbde..13ee15f 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -806,7 +806,7 @@ drop:
+ }
+
+ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+- struct mtk_eth *eth, u32 rx_intr)
++ struct mtk_eth *eth)
+ {
+ struct mtk_rx_ring *ring = ð->rx_ring;
+ int idx = ring->calc_idx;
+@@ -894,7 +894,7 @@ release_desc:
+ }
+
+ if (done < budget)
+- mtk_w32(eth, rx_intr, MTK_QMTK_INT_STATUS);
++ mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
+
+ return done;
+ }
+@@ -977,28 +977,26 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ static int mtk_poll(struct napi_struct *napi, int budget)
+ {
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+- u32 status, status2, mask, tx_intr, rx_intr, status_intr;
++ u32 status, status2, mask;
+ int tx_done, rx_done;
+ bool tx_again = false;
+
+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+ status2 = mtk_r32(eth, MTK_INT_STATUS2);
+- tx_intr = MTK_TX_DONE_INT;
+- rx_intr = MTK_RX_DONE_INT;
+- status_intr = (MTK_GDM1_AF | MTK_GDM2_AF);
+ tx_done = 0;
+ rx_done = 0;
+ tx_again = 0;
+
+- if (status & tx_intr)
++ if (status & MTK_TX_DONE_INT)
+ tx_done = mtk_poll_tx(eth, budget, &tx_again);
+
+- if (status & rx_intr)
+- rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
++ if (status & MTK_RX_DONE_INT)
++ rx_done = mtk_poll_rx(napi, budget, eth);
+
+- if (unlikely(status2 & status_intr)) {
++ if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) {
+ mtk_stats_update(eth);
+- mtk_w32(eth, status_intr, MTK_INT_STATUS2);
++ mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF),
++ MTK_INT_STATUS2);
+ }
+
+ if (unlikely(netif_msg_intr(eth))) {
+@@ -1016,7 +1014,7 @@ static int mtk_poll(struct napi_struct *napi, int budget)
+ return budget;
+
+ napi_complete(napi);
+- mtk_irq_enable(eth, tx_intr | rx_intr);
++ mtk_irq_enable(eth, MTK_RX_DONE_INT | MTK_RX_DONE_INT);
+
+ return rx_done;
+ }
+--
+1.7.10.4
+
--- /dev/null
+From dd08d1ac4cfc86fbea5ee207b9615922ede88ec6 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 17 May 2016 06:01:45 +0200
+Subject: [PATCH 095/102] net-next: mediatek: add IRQ locking
+
+The code that enables and disables IRQs is missing proper locking. After
+adding the IRQ separation patch and routing the putting the RX and TX IRQs
+on different cores we experienced IRQ stalls. Fix this by adding proper
+locking. We use a dedicated lock to reduce the latency if the IRQ code.
+Otherwise it might wait for bottom code to finish before reenabling or
+disabling IRQs.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 +++++++
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 13ee15f..c869064 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -331,18 +331,24 @@ static void mtk_mdio_cleanup(struct mtk_eth *eth)
+
+ static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
+ {
++ unsigned long flags;
+ u32 val;
+
++ spin_lock_irqsave(ð->irq_lock, flags);
+ val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+ mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
++ spin_unlock_irqrestore(ð->irq_lock, flags);
+ }
+
+ static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
+ {
++ unsigned long flags;
+ u32 val;
+
++ spin_lock_irqsave(ð->irq_lock, flags);
+ val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+ mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
++ spin_unlock_irqrestore(ð->irq_lock, flags);
+ }
+
+ static int mtk_set_mac_address(struct net_device *dev, void *p)
+@@ -1771,6 +1777,7 @@ static int mtk_probe(struct platform_device *pdev)
+ return -EADDRNOTAVAIL;
+
+ spin_lock_init(ð->page_lock);
++ spin_lock_init(ð->irq_lock);
+
+ eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "mediatek,ethsys");
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index a5eb7c6..3159d2a 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -373,6 +373,7 @@ struct mtk_eth {
+ void __iomem *base;
+ struct reset_control *rstc;
+ spinlock_t page_lock;
++ spinlock_t irq_lock;
+ struct net_device dummy_dev;
+ struct net_device *netdev[MTK_MAX_DEVS];
+ struct mtk_mac *mac[MTK_MAX_DEVS];
+--
+1.7.10.4
+
--- /dev/null
+From 190df1a9dbf4d8809b7f991194ce60e47f2290a2 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Wed, 23 Mar 2016 18:31:48 +0100
+Subject: [PATCH 096/102] net-next: mediatek: add support for IRQ grouping
+
+The ethernet core has 3 IRQs. using the IRQ grouping registers we are able
+to separate TX and RX IRQs, which allows us to service them on separate
+cores. This patch splits the irq handler into 2 separate functions, one for
+TX and another for RX. The TX housekeeping is split out into its own NAPI
+handler.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 156 +++++++++++++++++----------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 15 ++-
+ 2 files changed, 111 insertions(+), 60 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index c869064..718cbb2 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -905,14 +905,13 @@ release_desc:
+ return done;
+ }
+
+-static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
++static int mtk_poll_tx(struct mtk_eth *eth, int budget)
+ {
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ struct mtk_tx_dma *desc;
+ struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
+- int total = 0, done = 0;
+- unsigned int bytes = 0;
++ unsigned int bytes = 0, done = 0;
+ u32 cpu, dma;
+ static int condition;
+ int i;
+@@ -964,63 +963,82 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ netdev_completed_queue(eth->netdev[i], done, bytes);
+ }
+
+- /* read hw index again make sure no new tx packet */
+- if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
+- *tx_again = true;
+- else
+- mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
+-
+- if (!total)
+- return 0;
+-
+ if (mtk_queue_stopped(eth) &&
+ (atomic_read(&ring->free_count) > ring->thresh))
+ mtk_wake_queue(eth);
+
+- return total;
++ return done;
+ }
+
+-static int mtk_poll(struct napi_struct *napi, int budget)
++static void mtk_handle_status_irq(struct mtk_eth *eth)
+ {
+- struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+- u32 status, status2, mask;
+- int tx_done, rx_done;
+- bool tx_again = false;
+-
+- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+- status2 = mtk_r32(eth, MTK_INT_STATUS2);
+- tx_done = 0;
+- rx_done = 0;
+- tx_again = 0;
+-
+- if (status & MTK_TX_DONE_INT)
+- tx_done = mtk_poll_tx(eth, budget, &tx_again);
+-
+- if (status & MTK_RX_DONE_INT)
+- rx_done = mtk_poll_rx(napi, budget, eth);
++ u32 status2 = mtk_r32(eth, MTK_INT_STATUS2);
+
+ if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) {
+ mtk_stats_update(eth);
+ mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF),
+ MTK_INT_STATUS2);
+ }
++}
++
++static int mtk_napi_tx(struct napi_struct *napi, int budget)
++{
++ struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi);
++ u32 status, mask;
++ int tx_done = 0;
++
++ mtk_handle_status_irq(eth);
++ mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
++ tx_done = mtk_poll_tx(eth, budget);
++
++ if (unlikely(netif_msg_intr(eth))) {
++ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
++ mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
++ dev_info(eth->dev,
++ "done tx %d, intr 0x%08x/0x%x\n",
++ tx_done, status, mask);
++ }
++
++ if (tx_done == budget)
++ return budget;
++
++ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
++ if (status & MTK_TX_DONE_INT)
++ return budget;
++
++ napi_complete(napi);
++ mtk_irq_enable(eth, MTK_TX_DONE_INT);
++
++ return tx_done;
++}
++
++static int mtk_napi_rx(struct napi_struct *napi, int budget)
++{
++ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
++ u32 status, mask;
++ int rx_done = 0;
++
++ mtk_handle_status_irq(eth);
++ mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
++ rx_done = mtk_poll_rx(napi, budget, eth);
+
+ if (unlikely(netif_msg_intr(eth))) {
++ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+ mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
+- netdev_info(eth->netdev[0],
+- "done tx %d, rx %d, intr 0x%08x/0x%x\n",
+- tx_done, rx_done, status, mask);
++ dev_info(eth->dev,
++ "done rx %d, intr 0x%08x/0x%x\n",
++ rx_done, status, mask);
+ }
+
+- if (tx_again || rx_done == budget)
++ if (rx_done == budget)
+ return budget;
+
+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+- if (status & (tx_intr | rx_intr))
++ if (status & MTK_RX_DONE_INT)
+ return budget;
+
+ napi_complete(napi);
+- mtk_irq_enable(eth, MTK_RX_DONE_INT | MTK_RX_DONE_INT);
++ mtk_irq_enable(eth, MTK_RX_DONE_INT);
+
+ return rx_done;
+ }
+@@ -1256,22 +1274,26 @@ static void mtk_tx_timeout(struct net_device *dev)
+ schedule_work(ð->pending_work);
+ }
+
+-static irqreturn_t mtk_handle_irq(int irq, void *_eth)
++static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
+ {
+ struct mtk_eth *eth = _eth;
+- u32 status;
+
+- status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+- if (unlikely(!status))
+- return IRQ_NONE;
++ if (likely(napi_schedule_prep(ð->rx_napi))) {
++ __napi_schedule(ð->rx_napi);
++ mtk_irq_disable(eth, MTK_RX_DONE_INT);
++ }
+
+- if (likely(status & (MTK_RX_DONE_INT | MTK_TX_DONE_INT))) {
+- if (likely(napi_schedule_prep(ð->rx_napi)))
+- __napi_schedule(ð->rx_napi);
+- } else {
+- mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
++{
++ struct mtk_eth *eth = _eth;
++
++ if (likely(napi_schedule_prep(ð->tx_napi))) {
++ __napi_schedule(ð->tx_napi);
++ mtk_irq_disable(eth, MTK_TX_DONE_INT);
+ }
+- mtk_irq_disable(eth, (MTK_RX_DONE_INT | MTK_TX_DONE_INT));
+
+ return IRQ_HANDLED;
+ }
+@@ -1284,7 +1306,7 @@ static void mtk_poll_controller(struct net_device *dev)
+ u32 int_mask = MTK_TX_DONE_INT | MTK_RX_DONE_INT;
+
+ mtk_irq_disable(eth, int_mask);
+- mtk_handle_irq(dev->irq, dev);
++ mtk_handle_irq(dev->irq[0], dev);
+ mtk_irq_enable(eth, int_mask);
+ }
+ #endif
+@@ -1320,6 +1342,7 @@ static int mtk_open(struct net_device *dev)
+ if (err)
+ return err;
+
++ napi_enable(ð->tx_napi);
+ napi_enable(ð->rx_napi);
+ mtk_irq_enable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+ }
+@@ -1368,6 +1391,7 @@ static int mtk_stop(struct net_device *dev)
+ return 0;
+
+ mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
++ napi_disable(ð->tx_napi);
+ napi_disable(ð->rx_napi);
+
+ mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
+@@ -1405,7 +1429,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ /* Enable RX VLan Offloading */
+ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+
+- err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
++ err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
++ dev_name(eth->dev), eth);
++ if (err)
++ return err;
++ err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
+ dev_name(eth->dev), eth);
+ if (err)
+ return err;
+@@ -1421,7 +1449,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ mtk_w32(eth, 0, MTK_RST_GL);
+
+ /* FE int grouping */
+- mtk_w32(eth, 0, MTK_FE_INT_GRP);
++ mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1);
++ mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_GRP2);
++ mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1);
++ mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2);
++ mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
+
+ for (i = 0; i < 2; i++) {
+ u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
+@@ -1469,7 +1501,9 @@ static void mtk_uninit(struct net_device *dev)
+ phy_disconnect(mac->phy_dev);
+ mtk_mdio_cleanup(eth);
+ mtk_irq_disable(eth, ~0);
+- free_irq(dev->irq, dev);
++ free_irq(eth->irq[0], dev);
++ free_irq(eth->irq[1], dev);
++ free_irq(eth->irq[2], dev);
+ }
+
+ static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+@@ -1744,10 +1778,10 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ dev_err(eth->dev, "error bringing up device\n");
+ goto free_netdev;
+ }
+- eth->netdev[id]->irq = eth->irq;
++ eth->netdev[id]->irq = eth->irq[0];
+ netif_info(eth, probe, eth->netdev[id],
+ "mediatek frame engine at 0x%08lx, irq %d\n",
+- eth->netdev[id]->base_addr, eth->netdev[id]->irq);
++ eth->netdev[id]->base_addr, eth->irq[0]);
+
+ return 0;
+
+@@ -1764,6 +1798,7 @@ static int mtk_probe(struct platform_device *pdev)
+ struct mtk_soc_data *soc;
+ struct mtk_eth *eth;
+ int err;
++ int i;
+
+ match = of_match_device(of_mtk_match, &pdev->dev);
+ soc = (struct mtk_soc_data *)match->data;
+@@ -1799,10 +1834,12 @@ static int mtk_probe(struct platform_device *pdev)
+ return PTR_ERR(eth->rstc);
+ }
+
+- eth->irq = platform_get_irq(pdev, 0);
+- if (eth->irq < 0) {
+- dev_err(&pdev->dev, "no IRQ resource found\n");
+- return -ENXIO;
++ for (i = 0; i < 3; i++) {
++ eth->irq[i] = platform_get_irq(pdev, i);
++ if (eth->irq[i] < 0) {
++ dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
++ return -ENXIO;
++ }
+ }
+
+ eth->clk_ethif = devm_clk_get(&pdev->dev, "ethif");
+@@ -1843,7 +1880,9 @@ static int mtk_probe(struct platform_device *pdev)
+ * for NAPI to work
+ */
+ init_dummy_netdev(ð->dummy_dev);
+- netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_poll,
++ netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx,
++ MTK_NAPI_WEIGHT);
++ netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx,
+ MTK_NAPI_WEIGHT);
+
+ platform_set_drvdata(pdev, eth);
+@@ -1864,6 +1903,7 @@ static int mtk_remove(struct platform_device *pdev)
+ clk_disable_unprepare(eth->clk_gp1);
+ clk_disable_unprepare(eth->clk_gp2);
+
++ netif_napi_del(ð->tx_napi);
+ netif_napi_del(ð->rx_napi);
+ mtk_cleanup(eth);
+ platform_set_drvdata(pdev, NULL);
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index 3159d2a..f82e3ac 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -68,6 +68,10 @@
+ /* Unicast Filter MAC Address Register - High */
+ #define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000))
+
++/* PDMA Interrupt grouping registers */
++#define MTK_PDMA_INT_GRP1 0xa50
++#define MTK_PDMA_INT_GRP2 0xa54
++
+ /* QDMA TX Queue Configuration Registers */
+ #define MTK_QTX_CFG(x) (0x1800 + (x * 0x10))
+ #define QDMA_RES_THRES 4
+@@ -125,6 +129,11 @@
+ #define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
+ MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
+
++/* QDMA Interrupt grouping registers */
++#define MTK_QDMA_INT_GRP1 0x1a20
++#define MTK_QDMA_INT_GRP2 0x1a24
++#define MTK_RLS_DONE_INT BIT(0)
++
+ /* QDMA Interrupt Status Register */
+ #define MTK_QDMA_INT_MASK 0x1A1C
+
+@@ -356,7 +365,8 @@ struct mtk_rx_ring {
+ * @dma_refcnt: track how many netdevs are using the DMA engine
+ * @tx_ring: Pointer to the memore holding info about the TX ring
+ * @rx_ring: Pointer to the memore holding info about the RX ring
+- * @rx_napi: The NAPI struct
++ * @tx_napi: The TX NAPI struct
++ * @rx_napi: The RX NAPI struct
+ * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
+ * @phy_scratch_ring: physical address of scratch_ring
+ * @scratch_head: The scratch memory that scratch_ring points to.
+@@ -377,7 +387,7 @@ struct mtk_eth {
+ struct net_device dummy_dev;
+ struct net_device *netdev[MTK_MAX_DEVS];
+ struct mtk_mac *mac[MTK_MAX_DEVS];
+- int irq;
++ int irq[3];
+ u32 msg_enable;
+ unsigned long sysclk;
+ struct regmap *ethsys;
+@@ -385,6 +395,7 @@ struct mtk_eth {
+ atomic_t dma_refcnt;
+ struct mtk_tx_ring tx_ring;
+ struct mtk_rx_ring rx_ring;
++ struct napi_struct tx_napi;
+ struct napi_struct rx_napi;
+ struct mtk_tx_dma *scratch_ring;
+ dma_addr_t phy_scratch_ring;
+--
+1.7.10.4
+
--- /dev/null
+From 7c955062aaa563b1894671af3ae250460b3fa82d Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Thu, 5 May 2016 10:01:56 +0200
+Subject: [PATCH 097/102] net-next: mediatek: change my email address
+
+The old address is no longer valid. Use the my new one instead.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 718cbb2..bced659 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -7,7 +7,7 @@
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
++ * Copyright (C) 2009-2016 John Crispin <john@phrozen.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+@@ -1929,5 +1929,5 @@ static struct platform_driver mtk_driver = {
+ module_platform_driver(mtk_driver);
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
++MODULE_AUTHOR("John Crispin <john@phrozen.org>");
+ MODULE_DESCRIPTION("Ethernet driver for MediaTek SoC");
+--
+1.7.10.4
+
--- /dev/null
+From cd1343c14328a5de1a58c47b81b8a2febb31d542 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 10 May 2016 11:16:30 +0200
+Subject: [PATCH 098/102] net-next: mediatek: only trigger the tx watchdog
+ reset when all devices are stalled
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 14 ++++++++++++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
+ 2 files changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index bced659..70e961c 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1267,11 +1267,21 @@ static void mtk_tx_timeout(struct net_device *dev)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
++ int i, reset = 0;
+
+ eth->netdev[mac->id]->stats.tx_errors++;
+ netif_err(eth, tx_err, dev,
+ "transmit timed out\n");
+- schedule_work(ð->pending_work);
++
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ if (!eth->netdev[i] ||
++ time_after(jiffies, dev_trans_start(eth->netdev[i]) +
++ MTK_WDT_TIMEOUT))
++ reset++;
++ }
++
++ if (reset == MTK_MAC_COUNT)
++ schedule_work(ð->pending_work);
+ }
+
+ static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
+@@ -1765,7 +1775,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+
+ SET_NETDEV_DEV(eth->netdev[id], eth->dev);
+- eth->netdev[id]->watchdog_timeo = 5 * HZ;
++ eth->netdev[id]->watchdog_timeo = MTK_WDT_TIMEOUT;
+ eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
+ eth->netdev[id]->base_addr = (unsigned long)eth->base;
+ eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index f82e3ac..e39da72 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,6 +15,7 @@
+ #ifndef MTK_ETH_H
+ #define MTK_ETH_H
+
++#define MTK_WDT_TIMEOUT (4 * HZ)
+ #define MTK_QDMA_PAGE_SIZE 2048
+ #define MTK_MAX_RX_LENGTH 1536
+ #define MTK_TX_DMA_BUF_LEN 0x3fff
+--
+1.7.10.4
+
--- /dev/null
+From 2023b1652745fec5e691a5c9a9742ba6dd45e466 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Wed, 4 May 2016 15:44:01 +0200
+Subject: [PATCH 099/102] MAINTAINERS: change my email address
+
+The old address is no longer valid. Use the my new one instead.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ MAINTAINERS | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 73f0592..c044320 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -6904,7 +6904,7 @@ F: include/uapi/linux/uvcvideo.h
+
+ MEDIATEK ETHERNET DRIVER
+ M: Felix Fietkau <nbd@openwrt.org>
+-M: John Crispin <blogic@openwrt.org>
++M: John Crispin <john@phrozen.org>
+ L: netdev@vger.kernel.org
+ S: Maintained
+ F: drivers/net/ethernet/mediatek/
+--
+1.7.10.4
+
--- /dev/null
+From 69c89cb453c0beac5d8349108cee8f6806e5db19 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 17 May 2016 05:49:17 +0200
+Subject: [PATCH 100/102] MAINTAINERS: add Sean as mediatek ethernet
+ maintainer
+
+Sean has been busy doing most of the QA and stress testing of the driver.
+Add him to the list of maintainers.
+
+Signed-off-by: Sean Wang <keyhaede@gmail.com>
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ MAINTAINERS | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index c044320..6262e05 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -6905,6 +6905,7 @@ F: include/uapi/linux/uvcvideo.h
+ MEDIATEK ETHERNET DRIVER
+ M: Felix Fietkau <nbd@openwrt.org>
+ M: John Crispin <john@phrozen.org>
++M: Sean Wang <keyhaede@gmail.com>
+ L: netdev@vger.kernel.org
+ S: Maintained
+ F: drivers/net/ethernet/mediatek/
+--
+1.7.10.4
+
+++ /dev/null
---- a/drivers/mtd/devices/block2mtd.c
-+++ b/drivers/mtd/devices/block2mtd.c
-@@ -32,6 +32,8 @@
- #include <linux/slab.h>
- #include <linux/major.h>
-
-+static const char * const block2mtd_probe_types[] = { "cmdlinepart", NULL };
-+
- /* Info for the block device */
- struct block2mtd_dev {
- struct list_head list;
-@@ -227,6 +229,7 @@ static struct block2mtd_dev *add_device(
- #endif
- const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
- struct block_device *bdev = ERR_PTR(-ENODEV);
-+ struct mtd_part_parser_data ppdata = { 0 };
- struct block2mtd_dev *dev;
- struct mtd_partition *part;
- char *name;
-@@ -307,11 +310,7 @@ static struct block2mtd_dev *add_device(
- dev->mtd.priv = dev;
- dev->mtd.owner = THIS_MODULE;
-
-- part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL);
-- part->name = name;
-- part->offset = 0;
-- part->size = dev->mtd.size;
-- if (mtd_device_register(&dev->mtd, part, 1)) {
-+ if (mtd_device_parse_register(&dev->mtd, block2mtd_probe_types, &ppdata, NULL, 0)) {
- /* Device didn't get added, so free the entry */
- goto err_destroy_mutex;
- }
--- /dev/null
+From 6b8a7257e7bcb56782c3f8048311670fe6a80209 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Mon, 11 Apr 2016 03:11:54 +0200
+Subject: [PATCH 101/102] net: mediatek add gsw/mt7530 driver
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/Makefile | 2 +-
+ drivers/net/ethernet/mediatek/gsw_mt7620.h | 251 +++++++
+ drivers/net/ethernet/mediatek/gsw_mt7623.c | 1084 +++++++++++++++++++++++++++
+ drivers/net/ethernet/mediatek/mt7530.c | 808 ++++++++++++++++++++
+ drivers/net/ethernet/mediatek/mt7530.h | 20 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 59 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 +
+ 7 files changed, 2199 insertions(+), 30 deletions(-)
+ create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h
+ create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7623.c
+ 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 aa3f1c8..82001c4 100644
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -2,4 +2,4 @@
+ # Makefile for the Mediatek SoCs built-in ethernet macs
+ #
+
+-obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o
++obj-$(CONFIG_NET_MEDIATEK_SOC) += mt7530.o gsw_mt7623.o mtk_eth_soc.o
+diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.h b/drivers/net/ethernet/mediatek/gsw_mt7620.h
+new file mode 100644
+index 0000000..6fca8f2
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
+@@ -0,0 +1,251 @@
++/* 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-2016 John Crispin <blogic@openwrt.org>
++ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
++ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
++ */
++
++#ifndef _RALINK_GSW_MT7620_H__
++#define _RALINK_GSW_MT7620_H__
++
++#define GSW_REG_PHY_TIMEOUT (5 * HZ)
++
++#define MT7620_GSW_REG_PIAC 0x0004
++
++#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_CFG 0x10
++#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
++
++#define MT7621_ESW_PHY_POLLING 0x0000
++#define MT7620_ESW_PHY_POLLING 0x7000
++
++#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)
++
++/* ethernet subsystem config register */
++#define ETHSYS_SYSCFG0 0x14
++/* ethernet subsystem clock register */
++#define ETHSYS_CLKCFG0 0x2c
++#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
++
++/* p5 RGMII wrapper TX clock control register */
++#define MT7530_P5RGMIITXCR 0x7b04
++/* p5 RGMII wrapper RX clock control register */
++#define MT7530_P5RGMIIRXCR 0x7b00
++/* TRGMII TDX ODT registers */
++#define MT7530_TRGMII_TD0_ODT 0x7a54
++#define MT7530_TRGMII_TD1_ODT 0x7a5c
++#define MT7530_TRGMII_TD2_ODT 0x7a64
++#define MT7530_TRGMII_TD3_ODT 0x7a6c
++#define MT7530_TRGMII_TD4_ODT 0x7a74
++#define MT7530_TRGMII_TD5_ODT 0x7a7c
++/* TRGMII TCK ctrl register */
++#define MT7530_TRGMII_TCK_CTRL 0x7a78
++/* TRGMII Tx ctrl register */
++#define MT7530_TRGMII_TXCTRL 0x7a40
++/* port 6 extended control register */
++#define MT7530_P6ECR 0x7830
++/* IO driver control register */
++#define MT7530_IO_DRV_CR 0x7810
++/* top signal control register */
++#define MT7530_TOP_SIG_CTRL 0x7808
++/* modified hwtrap register */
++#define MT7530_MHWTRAP 0x7804
++/* hwtrap status register */
++#define MT7530_HWTRAP 0x7800
++/* status interrupt register */
++#define MT7530_SYS_INT_STS 0x700c
++/* system nterrupt register */
++#define MT7530_SYS_INT_EN 0x7008
++/* system control register */
++#define MT7530_SYS_CTRL 0x7000
++/* port MAC status register */
++#define MT7530_PMSR_P(x) (0x3008 + (x * 0x100))
++/* port MAC control register */
++#define MT7530_PMCR_P(x) (0x3000 + (x * 0x100))
++
++#define MT7621_XTAL_SHIFT 6
++#define MT7621_XTAL_MASK 0x7
++#define MT7621_XTAL_25 6
++#define MT7621_XTAL_40 3
++#define MT7621_MDIO_DRV_MASK (3 << 4)
++#define MT7621_GE1_MODE_MASK (3 << 12)
++
++#define TRGMII_TXCTRL_TXC_INV BIT(30)
++#define P6ECR_INTF_MODE_RGMII BIT(1)
++#define P5RGMIIRXCR_C_ALIGN BIT(8)
++#define P5RGMIIRXCR_DELAY_2 BIT(1)
++#define P5RGMIITXCR_DELAY_2 (BIT(8) | BIT(2))
++
++/* TOP_SIG_CTRL bits */
++#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
++
++/* MHWTRAP bits */
++#define MHWTRAP_MANUAL BIT(16)
++#define MHWTRAP_P5_MAC_SEL BIT(13)
++#define MHWTRAP_P6_DIS BIT(8)
++#define MHWTRAP_P5_RGMII_MODE BIT(7)
++#define MHWTRAP_P5_DIS BIT(6)
++#define MHWTRAP_PHY_ACCESS BIT(5)
++
++/* HWTRAP bits */
++#define HWTRAP_XTAL_SHIFT 9
++#define HWTRAP_XTAL_MASK 0x3
++
++/* SYS_CTRL bits */
++#define SYS_CTRL_SW_RST BIT(1)
++#define SYS_CTRL_REG_RST BIT(0)
++
++/* PMCR bits */
++#define PMCR_IFG_XMIT_96 BIT(18)
++#define PMCR_MAC_MODE BIT(16)
++#define PMCR_FORCE_MODE BIT(15)
++#define PMCR_TX_EN BIT(14)
++#define PMCR_RX_EN BIT(13)
++#define PMCR_BACK_PRES_EN BIT(9)
++#define PMCR_BACKOFF_EN BIT(8)
++#define PMCR_TX_FC_EN BIT(5)
++#define PMCR_RX_FC_EN BIT(4)
++#define PMCR_FORCE_SPEED_1000 BIT(3)
++#define PMCR_FORCE_FDX BIT(1)
++#define PMCR_FORCE_LNK BIT(0)
++#define PMCR_FIXED_LINK (PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
++ PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
++ PMCR_BACK_PRES_EN | PMCR_BACKOFF_EN | \
++ PMCR_FORCE_SPEED_1000 | PMCR_FORCE_FDX | \
++ PMCR_FORCE_LNK)
++
++#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
++ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
++
++/* TRGMII control registers */
++#define GSW_INTF_MODE 0x390
++#define GSW_TRGMII_TD0_ODT 0x354
++#define GSW_TRGMII_TD1_ODT 0x35c
++#define GSW_TRGMII_TD2_ODT 0x364
++#define GSW_TRGMII_TD3_ODT 0x36c
++#define GSW_TRGMII_TXCTL_ODT 0x374
++#define GSW_TRGMII_TCK_ODT 0x37c
++#define GSW_TRGMII_RCK_CTRL 0x300
++
++#define INTF_MODE_TRGMII BIT(1)
++#define TRGMII_RCK_CTRL_RX_RST BIT(31)
++
++
++/* possible XTAL speed */
++#define MT7623_XTAL_40 0
++#define MT7623_XTAL_20 1
++#define MT7623_XTAL_25 3
++
++/* GPIO port control registers */
++#define GPIO_OD33_CTRL8 0x4c0
++#define GPIO_BIAS_CTRL 0xed0
++#define GPIO_DRV_SEL10 0xf00
++
++/* on MT7620 the functio of port 4 can be software configured */
++enum {
++ PORT4_EPHY = 0,
++ PORT4_EXT,
++};
++
++/* struct mt7620_gsw - the structure that holds the SoC specific data
++ * @dev: The Device struct
++ * @base: The base address
++ * @piac_offset: The PIAC base may change depending on SoC
++ * @irq: The IRQ we are using
++ * @port4: The port4 mode on MT7620
++ * @autopoll: Is MDIO autopolling enabled
++ * @ethsys: The ethsys register map
++ * @pctl: The pin control register map
++ * @clk_trgpll: The trgmii pll clock
++ */
++struct mt7620_gsw {
++ struct mtk_eth *eth;
++ struct device *dev;
++ void __iomem *base;
++ u32 piac_offset;
++ int irq;
++ int port4;
++ unsigned long int autopoll;
++
++ struct regmap *ethsys;
++ struct regmap *pctl;
++
++ struct clk *clk_trgpll;
++
++ int trgmii_force;
++ bool wllll;
++};
++
++/* switch register I/O wrappers */
++void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
++u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
++
++/* the callback used by the driver core to bringup the switch */
++int mtk_gsw_init(struct mtk_eth *eth);
++
++/* MDIO access wrappers */
++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 mtk_eth *eth, int port);
++int mt7620_has_carrier(struct mtk_eth *eth);
++void mt7620_print_link_state(struct mtk_eth *eth, 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);
++void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
++
++u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
++ u32 phy_register, u32 write_data);
++u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg);
++void mt7620_handle_carrier(struct mtk_eth *eth);
++
++#endif
+diff --git a/drivers/net/ethernet/mediatek/gsw_mt7623.c b/drivers/net/ethernet/mediatek/gsw_mt7623.c
+new file mode 100644
+index 0000000..0c6b8a6
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c
+@@ -0,0 +1,1084 @@
++/* 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-2016 John Crispin <blogic@openwrt.org>
++ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
++ * Copyright (C) 2013-2016 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 <linux/of_gpio.h>
++#include <linux/of_mdio.h>
++#include <linux/clk.h>
++#include <linux/mfd/syscon.h>
++#include <linux/regulator/consumer.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++#include <linux/mii.h>
++#include <linux/interrupt.h>
++#include <linux/netdevice.h>
++#include <linux/dma-mapping.h>
++#include <linux/phy.h>
++#include <linux/ethtool.h>
++#include <linux/version.h>
++#include <linux/atomic.h>
++
++#include "mtk_eth_soc.h"
++#include "gsw_mt7620.h"
++#include "mt7530.h"
++
++#define ETHSYS_CLKCFG0 0x2c
++#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
++
++void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
++{
++ _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++ _mtk_mdio_write(gsw->eth, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
++ _mtk_mdio_write(gsw->eth, 0x1f, 0x10, val >> 16);
++}
++
++u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
++{
++ u16 high, low;
++
++ _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++ low = _mtk_mdio_read(gsw->eth, 0x1f, (reg >> 2) & 0xf);
++ high = _mtk_mdio_read(gsw->eth, 0x1f, 0x10);
++
++ return (high << 16) | (low & 0xffff);
++}
++
++void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
++{
++ u32 val = mt7530_mdio_r32(gsw, reg);
++
++ val &= mask;
++ val |= set;
++ mt7530_mdio_w32(gsw, reg, val);
++}
++
++void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
++{
++ mtk_w32(gsw->eth, val, reg + 0x10000);
++}
++
++u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
++{
++ return mtk_r32(gsw->eth, reg + 0x10000);
++}
++
++void mtk_switch_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, unsigned reg)
++{
++ u32 val = mtk_switch_r32(gsw, reg);
++
++ val &= mask;
++ val |= set;
++
++ mtk_switch_w32(gsw, val, reg);
++}
++
++int mt7623_gsw_config(struct mtk_eth *eth)
++{
++ if (eth->mii_bus && eth->mii_bus->phy_map[0x1f])
++ mt7530_probe(eth->dev, NULL, eth->mii_bus, 1);
++
++ return 0;
++}
++
++static irqreturn_t gsw_interrupt_mt7623(int irq, void *_eth)
++{
++ struct mtk_eth *eth = (struct mtk_eth *)_eth;
++ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
++ 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)
++ dev_info(gsw->dev,
++ "port %d link up\n", i);
++ else
++ dev_info(gsw->dev,
++ "port %d link down\n", i);
++ }
++
++// mt7620_handle_carrier(eth);
++ mt7530_mdio_w32(gsw, 0x700c, 0x1f);
++
++ return IRQ_HANDLED;
++}
++
++static void wait_loop(struct mt7620_gsw *gsw)
++{
++ int i;
++ int read_data;
++
++ for (i = 0; i < 320; i = i + 1)
++ read_data = mtk_switch_r32(gsw, 0x610);
++}
++
++static void trgmii_calibration_7623(struct mt7620_gsw *gsw)
++{
++
++ unsigned int tap_a[5] = { 0, 0, 0, 0, 0 }; /* minumum delay for all correct */
++ unsigned int tap_b[5] = { 0, 0, 0, 0, 0 }; /* maximum delay for all correct */
++ unsigned int final_tap[5];
++ unsigned int rxc_step_size;
++ unsigned int rxd_step_size;
++ unsigned int read_data;
++ unsigned int tmp;
++ unsigned int rd_wd;
++ int i;
++ unsigned int err_cnt[5];
++ unsigned int init_toggle_data;
++ unsigned int err_flag[5];
++ unsigned int err_total_flag;
++ unsigned int training_word;
++ unsigned int rd_tap;
++ u32 val;
++
++ u32 TRGMII_7623_base;
++ u32 TRGMII_7623_RD_0;
++ u32 TRGMII_RCK_CTRL;
++
++ TRGMII_7623_base = 0x300; /* 0xFB110300 */
++ TRGMII_7623_RD_0 = TRGMII_7623_base + 0x10;
++ TRGMII_RCK_CTRL = TRGMII_7623_base;
++ rxd_step_size = 0x1;
++ rxc_step_size = 0x4;
++ init_toggle_data = 0x00000055;
++ training_word = 0x000000AC;
++
++ /* RX clock gating in MT7623 */
++ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
++
++ /* Assert RX reset in MT7623 */
++ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_base + 0x00);
++
++ /* Set TX OE edge in MT7623 */
++ mtk_switch_m32(gsw, 0, 0x00002000, TRGMII_7623_base + 0x78);
++
++ /* Disable RX clock gating in MT7623 */
++ mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
++
++ /* Release RX reset in MT7623 */
++ mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base);
++
++ for (i = 0; i < 5; i++)
++ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
++
++ pr_err("Enable Training Mode in MT7530\n");
++ read_data = mt7530_mdio_r32(gsw, 0x7A40);
++ read_data |= 0xC0000000;
++ mt7530_mdio_w32(gsw, 0x7A40, read_data); /* Enable Training Mode in MT7530 */
++ err_total_flag = 0;
++ pr_err("Adjust RXC delay in MT7623\n");
++ read_data = 0x0;
++ while (err_total_flag == 0 && read_data != 0x68) {
++ pr_err("2nd Enable EDGE CHK in MT7623\n");
++ /* Enable EDGE CHK in MT7623 */
++ for (i = 0; i < 5; i++)
++ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++
++ wait_loop(gsw);
++ err_total_flag = 1;
++ for (i = 0; i < 5; i++) {
++ err_cnt[i] =
++ mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 8;
++ err_cnt[i] &= 0x0000000f;
++ rd_wd = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 16;
++ rd_wd &= 0x000000ff;
++ val = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++ pr_err("ERR_CNT = %d, RD_WD =%x, TRGMII_7623_RD_0=%x\n",
++ err_cnt[i], rd_wd, val);
++ if (err_cnt[i] != 0) {
++ err_flag[i] = 1;
++ } else if (rd_wd != 0x55) {
++ err_flag[i] = 1;
++ } else {
++ err_flag[i] = 0;
++ }
++ err_total_flag = err_flag[i] & err_total_flag;
++ }
++
++ pr_err("2nd Disable EDGE CHK in MT7623\n");
++ /* Disable EDGE CHK in MT7623 */
++ for (i = 0; i < 5; i++)
++ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++ wait_loop(gsw);
++ pr_err("2nd Disable EDGE CHK in MT7623\n");
++ /* Adjust RXC delay */
++ /* RX clock gating in MT7623 */
++ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
++ read_data = mtk_switch_r32(gsw, TRGMII_7623_base);
++ if (err_total_flag == 0) {
++ tmp = (read_data & 0x0000007f) + rxc_step_size;
++ pr_err(" RXC delay = %d\n", tmp);
++ read_data >>= 8;
++ read_data &= 0xffffff80;
++ read_data |= tmp;
++ read_data <<= 8;
++ read_data &= 0xffffff80;
++ read_data |= tmp;
++ mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
++ } else {
++ tmp = (read_data & 0x0000007f) + 16;
++ pr_err(" RXC delay = %d\n", tmp);
++ read_data >>= 8;
++ read_data &= 0xffffff80;
++ read_data |= tmp;
++ read_data <<= 8;
++ read_data &= 0xffffff80;
++ read_data |= tmp;
++ mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
++ }
++ read_data &= 0x000000ff;
++
++ /* Disable RX clock gating in MT7623 */
++ mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
++ for (i = 0; i < 5; i++)
++ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
++ }
++
++ /* Read RD_WD MT7623 */
++ for (i = 0; i < 5; i++) {
++ rd_tap = 0;
++ while (err_flag[i] != 0 && rd_tap != 128) {
++ /* Enable EDGE CHK in MT7623 */
++ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++ wait_loop(gsw);
++
++ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++ err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */
++ rd_wd = (read_data >> 16) & 0x000000ff;
++ if (err_cnt[i] != 0 || rd_wd != 0x55) {
++ err_flag[i] = 1;
++ } else {
++ err_flag[i] = 0;
++ }
++ /* Disable EDGE CHK in MT7623 */
++ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++ wait_loop(gsw);
++ if (err_flag[i] != 0) {
++ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */
++ read_data = (read_data & 0xffffff80) | rd_tap;
++ mtk_switch_w32(gsw, read_data,
++ TRGMII_7623_RD_0 + i * 8);
++ tap_a[i] = rd_tap;
++ } else {
++ rd_tap = (read_data & 0x0000007f) + 48;
++ read_data = (read_data & 0xffffff80) | rd_tap;
++ mtk_switch_w32(gsw, read_data,
++ TRGMII_7623_RD_0 + i * 8);
++ }
++
++ }
++ pr_err("MT7623 %dth bit Tap_a = %d\n", i, tap_a[i]);
++ }
++ /* pr_err("Last While Loop\n"); */
++ for (i = 0; i < 5; i++) {
++ while ((err_flag[i] == 0) && (rd_tap != 128)) {
++ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */
++ read_data = (read_data & 0xffffff80) | rd_tap;
++ mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
++ /* Enable EDGE CHK in MT7623 */
++ val =
++ mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) | 0x40000000;
++ val &= 0x4fffffff;
++ mtk_switch_w32(gsw, val, TRGMII_7623_RD_0 + i * 8);
++ wait_loop(gsw);
++ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++ err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */
++ rd_wd = (read_data >> 16) & 0x000000ff;
++ if (err_cnt[i] != 0 || rd_wd != 0x55) {
++ err_flag[i] = 1;
++ } else {
++ err_flag[i] = 0;
++ }
++
++ /* Disable EDGE CHK in MT7623 */
++ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++ wait_loop(gsw);
++
++ }
++
++ tap_b[i] = rd_tap; /* -rxd_step_size; */
++ pr_err("MT7623 %dth bit Tap_b = %d\n", i, tap_b[i]);
++ final_tap[i] = (tap_a[i] + tap_b[i]) / 2; /* Calculate RXD delay = (TAP_A + TAP_B)/2 */
++ read_data = (read_data & 0xffffff80) | final_tap[i];
++ mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
++ }
++
++ read_data = mt7530_mdio_r32(gsw, 0x7A40);
++ read_data &= 0x3fffffff;
++ mt7530_mdio_w32(gsw, 0x7A40, read_data);
++}
++
++static void trgmii_calibration_7530(struct mt7620_gsw *gsw)
++{
++
++ unsigned int tap_a[5] = { 0, 0, 0, 0, 0 };
++ unsigned int tap_b[5] = { 0, 0, 0, 0, 0 };
++ unsigned int final_tap[5];
++ unsigned int rxc_step_size;
++ unsigned int rxd_step_size;
++ unsigned int read_data;
++ unsigned int tmp = 0;
++ int i;
++ unsigned int err_cnt[5];
++ unsigned int rd_wd;
++ unsigned int init_toggle_data;
++ unsigned int err_flag[5];
++ unsigned int err_total_flag;
++ unsigned int training_word;
++ unsigned int rd_tap;
++
++ u32 TRGMII_7623_base;
++ u32 TRGMII_7530_RD_0;
++ u32 TRGMII_RCK_CTRL;
++ u32 TRGMII_7530_base;
++ u32 TRGMII_7530_TX_base;
++ u32 val;
++
++ TRGMII_7623_base = 0x300;
++ TRGMII_7530_base = 0x7A00;
++ TRGMII_7530_RD_0 = TRGMII_7530_base + 0x10;
++ TRGMII_RCK_CTRL = TRGMII_7623_base;
++ rxd_step_size = 0x1;
++ rxc_step_size = 0x8;
++ init_toggle_data = 0x00000055;
++ training_word = 0x000000AC;
++
++ TRGMII_7530_TX_base = TRGMII_7530_base + 0x50;
++
++ /* pr_err("Calibration begin ........\n"); */
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++ read_data = mt7530_mdio_r32(gsw, 0x7a10);
++ /* pr_err("TRGMII_7530_RD_0 is %x\n", read_data); */
++
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++ read_data &= 0x3fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */
++
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x78);
++ read_data |= 0x00002000;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x78, read_data); /* Set TX OE edge in MT7530 */
++
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++ read_data |= 0x80000000;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */
++
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++ read_data &= 0x7fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */
++
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++ read_data |= 0xC0000000;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */
++
++ /* pr_err("Enable Training Mode in MT7623\n"); */
++ /*Enable Training Mode in MT7623 */
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++ if (gsw->trgmii_force == 2000) {
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0xC0000000;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++ } else {
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++ }
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x078) & 0xfffff0ff;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x078);
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x50) & 0xfffff0ff;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x50);
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x58) & 0xfffff0ff;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x58);
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x60) & 0xfffff0ff;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x60);
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x68) & 0xfffff0ff;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x68);
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x70) & 0xfffff0ff;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x70);
++ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x78) & 0x00000800;
++ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x78);
++ err_total_flag = 0;
++ /* pr_err("Adjust RXC delay in MT7530\n"); */
++ read_data = 0x0;
++ while (err_total_flag == 0 && (read_data != 0x68)) {
++ /* pr_err("2nd Enable EDGE CHK in MT7530\n"); */
++ /* Enable EDGE CHK in MT7530 */
++ for (i = 0; i < 5; i++) {
++ read_data =
++ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++ read_data |= 0x40000000;
++ read_data &= 0x4fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ wait_loop(gsw);
++ /* pr_err("2nd Disable EDGE CHK in MT7530\n"); */
++ err_cnt[i] =
++ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++ /* pr_err("***** MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
++ /* pr_err("MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
++ err_cnt[i] >>= 8;
++ err_cnt[i] &= 0x0000ff0f;
++ rd_wd = err_cnt[i] >> 8;
++ rd_wd &= 0x000000ff;
++ err_cnt[i] &= 0x0000000f;
++ /* read_data = mt7530_mdio_r32(gsw,0x7a10,&read_data); */
++ if (err_cnt[i] != 0) {
++ err_flag[i] = 1;
++ } else if (rd_wd != 0x55) {
++ err_flag[i] = 1;
++ } else {
++ err_flag[i] = 0;
++ }
++ if (i == 0) {
++ err_total_flag = err_flag[i];
++ } else {
++ err_total_flag = err_flag[i] & err_total_flag;
++ }
++ /* Disable EDGE CHK in MT7530 */
++ read_data =
++ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++ read_data |= 0x40000000;
++ read_data &= 0x4fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ wait_loop(gsw);
++ }
++ /*Adjust RXC delay */
++ if (err_total_flag == 0) {
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++ read_data |= 0x80000000;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */
++
++ read_data =
++ mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++ read_data &= 0x3fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */
++
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++ tmp = read_data;
++ tmp &= 0x0000007f;
++ tmp += rxc_step_size;
++ /* pr_err("Current rxc delay = %d\n", tmp); */
++ read_data &= 0xffffff80;
++ read_data |= tmp;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++ /* pr_err("Current RXC delay = %x\n", read_data); */
++
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++ read_data &= 0x7fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */
++
++ read_data =
++ mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++ read_data |= 0xc0000000;
++ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */
++ pr_err("####### MT7530 RXC delay is %d\n", tmp);
++ }
++ read_data = tmp;
++ }
++ pr_err("Finish RXC Adjustment while loop\n");
++
++ /* pr_err("Read RD_WD MT7530\n"); */
++ /* Read RD_WD MT7530 */
++ for (i = 0; i < 5; i++) {
++ rd_tap = 0;
++ while (err_flag[i] != 0 && rd_tap != 128) {
++ /* Enable EDGE CHK in MT7530 */
++ read_data =
++ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++ read_data |= 0x40000000;
++ read_data &= 0x4fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ wait_loop(gsw);
++ err_cnt[i] = (read_data >> 8) & 0x0000000f;
++ rd_wd = (read_data >> 16) & 0x000000ff;
++ if (err_cnt[i] != 0 || rd_wd != 0x55) {
++ err_flag[i] = 1;
++ } else {
++ err_flag[i] = 0;
++ }
++ if (err_flag[i] != 0) {
++ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7530 */
++ read_data = (read_data & 0xffffff80) | rd_tap;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ tap_a[i] = rd_tap;
++ } else {
++ tap_a[i] = (read_data & 0x0000007f); /* Record the min delay TAP_A */
++ rd_tap = tap_a[i] + 0x4;
++ read_data = (read_data & 0xffffff80) | rd_tap;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ }
++
++ /* Disable EDGE CHK in MT7530 */
++ read_data =
++ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++ read_data |= 0x40000000;
++ read_data &= 0x4fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ wait_loop(gsw);
++
++ }
++ pr_err("MT7530 %dth bit Tap_a = %d\n", i, tap_a[i]);
++ }
++
++ /* pr_err("Last While Loop\n"); */
++ for (i = 0; i < 5; i++) {
++ rd_tap = 0;
++ while (err_flag[i] == 0 && (rd_tap != 128)) {
++ /* Enable EDGE CHK in MT7530 */
++ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++ read_data |= 0x40000000;
++ read_data &= 0x4fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ wait_loop(gsw);
++ err_cnt[i] = (read_data >> 8) & 0x0000000f;
++ rd_wd = (read_data >> 16) & 0x000000ff;
++ if (err_cnt[i] != 0 || rd_wd != 0x55)
++ err_flag[i] = 1;
++ else
++ err_flag[i] = 0;
++
++ if (err_flag[i] == 0 && (rd_tap != 128)) {
++ /* Add RXD delay in MT7530 */
++ rd_tap = (read_data & 0x0000007f) + rxd_step_size;
++ read_data = (read_data & 0xffffff80) | rd_tap;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ }
++ /* Disable EDGE CHK in MT7530 */
++ read_data =
++ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++ read_data |= 0x40000000;
++ read_data &= 0x4fffffff;
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++ read_data);
++ wait_loop(gsw);
++ }
++ tap_b[i] = rd_tap; /* - rxd_step_size; */
++ pr_err("MT7530 %dth bit Tap_b = %d\n", i, tap_b[i]);
++ /* Calculate RXD delay = (TAP_A + TAP_B)/2 */
++ final_tap[i] = (tap_a[i] + tap_b[i]) / 2;
++ /* pr_err("########****** MT7530 %dth bit Final Tap = %d\n", i, final_tap[i]); */
++
++ read_data = (read_data & 0xffffff80) | final_tap[i];
++ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, read_data);
++ }
++
++ if (gsw->trgmii_force == 2000)
++ mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base + 0x40);
++ else
++ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x40);
++
++}
++
++static void mt7530_trgmii_clock_setting(struct mt7620_gsw *gsw, u32 xtal_mode)
++{
++
++ u32 regValue;
++
++ /* TRGMII Clock */
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x1);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x404);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++
++ if (xtal_mode == 1) {
++ /* 25MHz */
++ if (gsw->trgmii_force == 2600)
++ /* 325MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x1a00);
++ else if (gsw->trgmii_force == 2000)
++ /* 250MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x1400);
++ } else if (xtal_mode == 2) {
++ /* 40MHz */
++ if (gsw->trgmii_force == 2600)
++ /* 325MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x1040);
++ else if (gsw->trgmii_force == 2000)
++ /* 250MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x0c80);
++ }
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x405);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x0);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x409);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ if (xtal_mode == 1)
++ /* 25MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
++ else
++ /* 40MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x40a);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ if (xtal_mode == 1)
++ /* 25MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
++ else
++ /* 40MHz */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
++
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x403);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x1800);
++
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x403);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x1c00);
++
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x401);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0xc020);
++
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x406);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0xa030);
++
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x406);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0xa038);
++
++// udelay(120); /* for MT7623 bring up test */
++
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x3);
++
++ regValue = mt7530_mdio_r32(gsw, 0x7830);
++ regValue &= 0xFFFFFFFC;
++ regValue |= 0x00000001;
++ mt7530_mdio_w32(gsw, 0x7830, regValue);
++
++ regValue = mt7530_mdio_r32(gsw, 0x7a40);
++ regValue &= ~(0x1 << 30);
++ regValue &= ~(0x1 << 28);
++ mt7530_mdio_w32(gsw, 0x7a40, regValue);
++
++ mt7530_mdio_w32(gsw, 0x7a78, 0x55);
++// udelay(100); /* for mt7623 bring up test */
++
++ mtk_switch_m32(gsw, 0x7fffffff, 0, 0x300);
++
++ trgmii_calibration_7623(gsw);
++ trgmii_calibration_7530(gsw);
++
++ mtk_switch_m32(gsw, 0, 0x80000000, 0x300);
++ mtk_switch_m32(gsw, 0, 0x7fffffff, 0x300);
++
++ /*MT7530 RXC reset */
++ regValue = mt7530_mdio_r32(gsw, 0x7a00);
++ regValue |= (0x1 << 31);
++ mt7530_mdio_w32(gsw, 0x7a00, regValue);
++ mdelay(1);
++ regValue &= ~(0x1 << 31);
++ mt7530_mdio_w32(gsw, 0x7a00, regValue);
++ mdelay(100);
++}
++
++static void mt7623_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw, struct device_node *np)
++{
++ u32 i;
++ u32 val;
++ u32 xtal_mode;
++
++ regmap_update_bits(gsw->ethsys, ETHSYS_CLKCFG0,
++ ETHSYS_TRGMII_CLK_SEL362_5,
++ ETHSYS_TRGMII_CLK_SEL362_5);
++
++ /* reset the TRGMII core */
++ mtk_switch_m32(gsw, 0, INTF_MODE_TRGMII, GSW_INTF_MODE);
++ /* Assert MT7623 RXC reset */
++ mtk_switch_m32(gsw, 0, TRGMII_RCK_CTRL_RX_RST, GSW_TRGMII_RCK_CTRL);
++
++ /* Hardware reset Switch */
++ device_reset(eth->dev);
++
++ /* Wait for Switch Reset Completed*/
++ for (i = 0; i < 100; i++) {
++ mdelay(10);
++ if (mt7530_mdio_r32(gsw, MT7530_HWTRAP))
++ break;
++ }
++
++ /* turn off all PHYs */
++ for (i = 0; i <= 4; i++) {
++ val = _mtk_mdio_read(gsw->eth, i, 0x0);
++ val |= BIT(11);
++ _mtk_mdio_write(gsw->eth, i, 0x0, val);
++ }
++
++ /* reset the switch */
++ mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
++ SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
++ udelay(100);
++
++ /* GE1, Force 1000M/FD, FC ON */
++ mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
++
++ /* GE2, Force 1000M/FD, FC ON */
++ mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), PMCR_FIXED_LINK_FC);
++
++ /* Enable Port 6, P5 as GMAC5, P5 disable */
++ val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
++ if (gsw->eth->mac[0] &&
++ of_phy_is_fixed_link(gsw->eth->mac[0]->of_node))
++ /* Enable Port 6 */
++ val &= ~MHWTRAP_P6_DIS;
++ else
++ /* Disable Port 6 */
++ val |= MHWTRAP_P6_DIS;
++ if (gsw->eth->mac[1] &&
++ of_phy_is_fixed_link(gsw->eth->mac[1]->of_node)) {
++ /* Enable Port 5 */
++ val &= ~MHWTRAP_P5_DIS;
++ /* Port 5 as PHY */
++ val &= ~MHWTRAP_P5_MAC_SEL;
++ } else {
++ /* Disable Port 5 */
++ val |= MHWTRAP_P5_DIS;
++ /* Port 5 as GMAC */
++ val |= MHWTRAP_P5_MAC_SEL;
++ val |= BIT(7);
++ mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), 0x8000);
++ }
++ /* gphy to port 0/4 */
++ if (gsw->wllll)
++ val |= BIT(20);
++ else
++ val &= ~BIT(20);
++
++ /* Set MT7530 phy direct access mode**/
++ val &= ~MHWTRAP_PHY_ACCESS;
++ /* manual override of HW-Trap */
++ val |= MHWTRAP_MANUAL;
++ mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
++ dev_info(gsw->dev, "Setting MHWTRAP to 0x%08x\n", val);
++
++ val = mt7530_mdio_r32(gsw, 0x7800);
++ val = (val >> 9) & 0x3;
++ if (val == 0x3) {
++ xtal_mode = 1;
++ /* 25Mhz Xtal - do nothing */
++ } else if (val == 0x2) {
++ /* 40Mhz */
++ xtal_mode = 2;
++
++ /* disable MT7530 core clock */
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x0);
++
++ /* disable MT7530 PLL */
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x2020);
++
++ /* for MT7530 core clock = 500Mhz */
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x40e);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x119);
++
++ /* enable MT7530 PLL */
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x2820);
++
++ udelay(20);
++
++ /* enable MT7530 core clock */
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++ } else {
++ xtal_mode = 3;
++ /* 20Mhz Xtal - TODO */
++ }
++
++ /* RGMII */
++ _mtk_mdio_write(gsw->eth, 0, 14, 0x1);
++
++ /* set MT7530 central align */
++ val = mt7530_mdio_r32(gsw, 0x7830);
++ val &= ~1;
++ val |= 1<<1;
++ mt7530_mdio_w32(gsw, 0x7830, val);
++
++ val = mt7530_mdio_r32(gsw, 0x7a40);
++ val &= ~(1<<30);
++ mt7530_mdio_w32(gsw, 0x7a40, val);
++
++ mt7530_mdio_w32(gsw, 0x7a78, 0x855);
++
++ /* delay setting for 10/1000M */
++ mt7530_mdio_w32(gsw, 0x7b00, 0x104);
++ mt7530_mdio_w32(gsw, 0x7b04, 0x10);
++
++ /* lower Tx Driving */
++ mt7530_mdio_w32(gsw, 0x7a54, 0x88);
++ mt7530_mdio_w32(gsw, 0x7a5c, 0x88);
++ mt7530_mdio_w32(gsw, 0x7a64, 0x88);
++ mt7530_mdio_w32(gsw, 0x7a6c, 0x88);
++ mt7530_mdio_w32(gsw, 0x7a74, 0x88);
++ mt7530_mdio_w32(gsw, 0x7a7c, 0x88);
++ mt7530_mdio_w32(gsw, 0x7810, 0x11);
++
++ /* Set MT7623/MT7683 TX Driving */
++ mtk_switch_w32(gsw, 0x88, 0x354);
++ mtk_switch_w32(gsw, 0x88, 0x35c);
++ mtk_switch_w32(gsw, 0x88, 0x364);
++ mtk_switch_w32(gsw, 0x88, 0x36c);
++ mtk_switch_w32(gsw, 0x88, 0x374);
++ mtk_switch_w32(gsw, 0x88, 0x37c);
++
++ /* Set GE2 driving and slew rate */
++ regmap_write(gsw->pctl, 0xF00, 0xe00);
++ /* set GE2 TDSEL */
++ regmap_write(gsw->pctl, 0x4C0, 0x5);
++ /* set GE2 TUNE */
++ regmap_write(gsw->pctl, 0xED0, 0x0);
++
++ regmap_write(gsw->pctl, 0xb70, 0);
++ regmap_write(gsw->pctl, 0x250, 0xffff);
++ regmap_write(gsw->pctl, 0x260, 0xff);
++ regmap_write(gsw->pctl, 0x380, 0x37);
++ regmap_write(gsw->pctl, 0x390, 0x40);
++
++ mt7530_trgmii_clock_setting(gsw, xtal_mode);
++
++ //LANWANPartition(gsw);
++
++ /* disable EEE */
++ for (i = 0; i <= 4; i++) {
++ _mtk_mdio_write(gsw->eth, i, 13, 0x7);
++ _mtk_mdio_write(gsw->eth, i, 14, 0x3C);
++ _mtk_mdio_write(gsw->eth, i, 13, 0x4007);
++ _mtk_mdio_write(gsw->eth, i, 14, 0x0);
++
++ /* Increase SlvDPSready time */
++ _mtk_mdio_write(gsw->eth, i, 31, 0x52b5);
++ _mtk_mdio_write(gsw->eth, i, 16, 0xafae);
++ _mtk_mdio_write(gsw->eth, i, 18, 0x2f);
++ _mtk_mdio_write(gsw->eth, i, 16, 0x8fae);
++
++ /* Incease post_update_timer */
++ _mtk_mdio_write(gsw->eth, i, 31, 0x3);
++ _mtk_mdio_write(gsw->eth, i, 17, 0x4b);
++
++ /* Adjust 100_mse_threshold */
++ _mtk_mdio_write(gsw->eth, i, 13, 0x1e);
++ _mtk_mdio_write(gsw->eth, i, 14, 0x123);
++ _mtk_mdio_write(gsw->eth, i, 13, 0x401e);
++ _mtk_mdio_write(gsw->eth, i, 14, 0xffff);
++
++ /* Disable mcc */
++ _mtk_mdio_write(gsw->eth, i, 13, 0x1e);
++ _mtk_mdio_write(gsw->eth, i, 14, 0xa6);
++ _mtk_mdio_write(gsw->eth, i, 13, 0x401e);
++ _mtk_mdio_write(gsw->eth, i, 14, 0x300);
++
++ /* Disable HW auto downshift*/
++ _mtk_mdio_write(gsw->eth, i, 31, 0x1);
++ val = _mtk_mdio_read(gsw->eth, i, 0x14);
++ val &= ~(1<<4);
++ _mtk_mdio_write(gsw->eth, i, 0x14, val);
++ }
++
++ /* turn on all PHYs */
++ for (i = 0; i <= 4; i++) {
++ val = _mtk_mdio_read(gsw->eth, i, 0);
++ val &= ~BIT(11);
++ _mtk_mdio_write(gsw->eth, i, 0, val);
++ }
++
++ /* enable irq */
++ mt7530_mdio_m32(gsw, 0, TOP_SIG_CTRL_NORMAL, MT7530_TOP_SIG_CTRL);
++}
++
++static const struct of_device_id mediatek_gsw_match[] = {
++ { .compatible = "mediatek,mt7623-gsw" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
++
++int mtk_gsw_init(struct mtk_eth *eth)
++{
++ struct device_node *np = eth->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);
++ if (!gsw)
++ return -ENODEV;
++ gsw->eth = eth;
++ eth->sw_priv = gsw;
++
++ mt7623_hw_init(eth, gsw, np);
++
++ if (request_threaded_irq(gsw->irq, gsw_interrupt_mt7623, NULL, 0,
++ "gsw", eth))
++ pr_err("fail to request irq\n");
++ mt7530_mdio_w32(gsw, 0x7008, 0x1f);
++
++ return 0;
++}
++
++static int mt7623_gsw_probe(struct platform_device *pdev)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct device_node *pctl;
++ int reset_pin, ret;
++ struct mt7620_gsw *gsw;
++ struct regulator *supply;
++
++ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
++ if (!gsw)
++ return -ENOMEM;
++
++ gsw->dev = &pdev->dev;
++ gsw->trgmii_force = 2000;
++ gsw->irq = irq_of_parse_and_map(np, 0);
++ if (gsw->irq < 0)
++ return -EINVAL;
++
++ gsw->ethsys = syscon_regmap_lookup_by_phandle(np, "mediatek,ethsys");
++ if (IS_ERR(gsw->ethsys))
++ return PTR_ERR(gsw->ethsys);
++
++ reset_pin = of_get_named_gpio(np, "mediatek,reset-pin", 0);
++ if (reset_pin < 0)
++ return reset_pin;
++
++ pctl = of_parse_phandle(np, "mediatek,pctl-regmap", 0);
++ if (IS_ERR(pctl))
++ return PTR_ERR(pctl);
++
++ gsw->pctl = syscon_node_to_regmap(pctl);
++ if (IS_ERR(pctl))
++ return PTR_ERR(pctl);
++
++ ret = devm_gpio_request(&pdev->dev, reset_pin, "mt7530-reset");
++ if (ret)
++ return ret;
++
++ gsw->clk_trgpll = devm_clk_get(&pdev->dev, "trgpll");
++
++ if (IS_ERR(gsw->clk_trgpll))
++ return -ENODEV;
++
++ supply = devm_regulator_get(&pdev->dev, "mt7530");
++ if (IS_ERR(supply))
++ return PTR_ERR(supply);
++
++ regulator_set_voltage(supply, 1000000, 1000000);
++ ret = regulator_enable(supply);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to enable reg-7530: %d\n", ret);
++ return ret;
++ }
++
++ gsw->wllll = of_property_read_bool(np, "mediatek,wllll");
++
++ pm_runtime_enable(&pdev->dev);
++ pm_runtime_get_sync(&pdev->dev);
++
++ ret = clk_set_rate(gsw->clk_trgpll, 500000000);
++ if (ret)
++ return ret;
++
++ regmap_write(gsw->ethsys, 0x34, 0x800000);
++ regmap_write(gsw->ethsys, 0x34, 0x0);
++
++ clk_prepare_enable(gsw->clk_trgpll);
++
++ gpio_direction_output(reset_pin, 0);
++ udelay(1000);
++ gpio_set_value(reset_pin, 1);
++ mdelay(100);
++
++ platform_set_drvdata(pdev, gsw);
++
++ return 0;
++}
++
++static int mt7623_gsw_remove(struct platform_device *pdev)
++{
++ struct mt7620_gsw *gsw = platform_get_drvdata(pdev);
++
++ clk_disable_unprepare(gsw->clk_trgpll);
++
++ pm_runtime_put_sync(&pdev->dev);
++ pm_runtime_disable(&pdev->dev);
++
++ platform_set_drvdata(pdev, NULL);
++
++ return 0;
++}
++
++static struct platform_driver gsw_driver = {
++ .probe = mt7623_gsw_probe,
++ .remove = mt7623_gsw_remove,
++ .driver = {
++ .name = "mt7623-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 MT7623 SoC");
+diff --git a/drivers/net/ethernet/mediatek/mt7530.c b/drivers/net/ethernet/mediatek/mt7530.c
+new file mode 100644
+index 0000000..2e9d280
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mt7530.c
+@@ -0,0 +1,808 @@
++/*
++ * 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 *eth = container_of(dev, struct mt7530_priv, swdev);
++ int i;
++
++ memset(eth->port_entries, 0, sizeof(eth->port_entries));
++ memset(eth->vlan_entries, 0, sizeof(eth->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++) {
++ eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
++
++ val->value.i = eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
++
++ eth->global_vlan_enable = val->value.i != 0;
++
++ return 0;
++}
++
++static u32
++mt7530_r32(struct mt7530_priv *eth, u32 reg)
++{
++ u32 val;
++ if (eth->bus) {
++ u16 high, low;
++
++ mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++ low = mdiobus_read(eth->bus, 0x1f, (reg >> 2) & 0xf);
++ high = mdiobus_read(eth->bus, 0x1f, 0x10);
++
++ return (high << 16) | (low & 0xffff);
++ }
++
++ val = ioread32(eth->base + reg);
++ pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
++
++ return val;
++}
++
++static void
++mt7530_w32(struct mt7530_priv *eth, u32 reg, u32 val)
++{
++ if (eth->bus) {
++ mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++ mdiobus_write(eth->bus, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
++ mdiobus_write(eth->bus, 0x1f, 0x10, val >> 16);
++ return;
++ }
++
++ pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
++ iowrite32(val, eth->base + reg);
++}
++
++static void
++mt7530_vtcr(struct mt7530_priv *eth, u32 cmd, u32 val)
++{
++ int i;
++
++ mt7530_w32(eth, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
++
++ for (i = 0; i < 20; i++) {
++ u32 val = mt7530_r32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
++
++ if (port >= MT7530_NUM_PORTS)
++ return -EINVAL;
++
++ *val = mt7530_r32(eth, 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 *eth = 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;
++
++ eth->port_entries[port].pvid = pvid;
++
++ return 0;
++}
++
++static int
++mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
++{
++ struct mt7530_priv *eth = 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(eth, 0, val->port_vlan);
++
++ member = mt7530_r32(eth, REG_ESW_VLAN_VAWD1);
++ member >>= 16;
++ member &= 0xff;
++
++ etags = mt7530_r32(eth, 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 *eth = 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);
++ }
++ eth->vlan_entries[val->port_vlan].member = member;
++ eth->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 *eth = 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;
++
++ eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
++ u32 vid;
++ int vlan;
++
++ vlan = val->port_vlan;
++
++ vid = mt7530_r32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
++ int i, j;
++ u8 tag_ports;
++ u8 untag_ports;
++
++ if (!eth->global_vlan_enable) {
++ for (i = 0; i < MT7530_NUM_PORTS; i++)
++ mt7530_w32(eth, REG_ESW_PORT_PCR(i), 0x00ff0000);
++
++ for (i = 0; i < MT7530_NUM_PORTS; i++)
++ mt7530_w32(eth, REG_ESW_PORT_PVC(i), 0x810000c0);
++
++ return 0;
++ }
++
++ /* set all ports as security mode */
++ for (i = 0; i < MT7530_NUM_PORTS; i++)
++ mt7530_w32(eth, 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 = eth->vlan_entries[i].member;
++ u8 etags = eth->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(eth, REG_ESW_PORT_PVC(i), pvc_mode);
++ }
++
++ for (i = 0; i < MT7530_NUM_VLANS; i++) {
++ u16 vid = eth->vlan_entries[i].vid;
++ u8 member = eth->vlan_entries[i].member;
++ u8 etags = eth->vlan_entries[i].etags;
++ u32 val;
++
++ /* vid of vlan */
++ val = mt7530_r32(eth, REG_ESW_VLAN_VTIM(i));
++ if (i % 2 == 0) {
++ val &= 0xfff000;
++ val |= vid;
++ } else {
++ val &= 0xfff;
++ val |= (vid << 12);
++ }
++ mt7530_w32(eth, REG_ESW_VLAN_VTIM(i), val);
++
++ /* vlan port membership */
++ if (member)
++ mt7530_w32(eth, 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(eth, 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(eth, REG_ESW_VLAN_VAWD2, val);
++
++ /* write to vlan table */
++ mt7530_vtcr(eth, 1, i);
++ }
++
++ /* Port Default PVID */
++ for (i = 0; i < MT7530_NUM_PORTS; i++) {
++ u32 val;
++ val = mt7530_r32(eth, REG_ESW_PORT_PPBV1(i));
++ val &= ~0xfff;
++ val |= eth->port_entries[i].pvid;
++ mt7530_w32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
++ u32 speed, pmsr;
++
++ if (port < 0 || port >= MT7530_NUM_PORTS)
++ return -EINVAL;
++
++ pmsr = mt7530_r32(eth, 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 *eth, 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(eth, port_base + mt7621_mibs[i].offset);
++ if (mt7621_mibs[i].size == 2) {
++ u64 hi;
++
++ hi = mt7530_r32(eth, 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 *eth = 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(eth, 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_MACH_MT7623)) {
++ swdev->alias = "mt7623";
++ swdev->name = "mt7623";
++ } 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) || IS_ENABLED(CONFIG_MACH_MT7623))
++ 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;
++ }
++
++ mt7530_reset_switch(swdev);
++
++ 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) || IS_ENABLED(CONFIG_MACH_MT7623)) && 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 70e961c..7788ba6 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -24,6 +24,9 @@
+
+ #include "mtk_eth_soc.h"
+
++/* the callback used by the driver core to bringup the switch */
++int mtk_gsw_init(struct mtk_eth *eth);
++
+ static int mtk_msg_level = -1;
+ module_param_named(msg_level, mtk_msg_level, int, 0);
+ MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
+@@ -69,7 +72,7 @@ static int mtk_mdio_busy_wait(struct mtk_eth *eth)
+ return 0;
+ if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
+ break;
+- usleep_range(10, 20);
++// usleep_range(10, 20);
+ }
+
+ dev_err(eth->dev, "mdio: MDIO timeout\n");
+@@ -1421,15 +1424,6 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ reset_control_deassert(eth->rstc);
+ usleep_range(10, 20);
+
+- /* Set GE2 driving and slew rate */
+- regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00);
+-
+- /* set GE2 TDSEL */
+- regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5);
+-
+- /* set GE2 TUNE */
+- regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
+-
+ /* GE1, Force 1000M/FD, FC ON */
+ mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0));
+
+@@ -1452,6 +1446,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ if (err)
+ return err;
+
++ mtk_gsw_init(eth);
++
+ /* disable delay and normal interrupt */
+ mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ mtk_irq_disable(eth, ~0);
+@@ -1479,6 +1475,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+ }
+
++ mt7623_gsw_config(eth);
++
+ return 0;
+ }
+
+@@ -1734,7 +1732,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ {
+ struct mtk_mac *mac;
+ const __be32 *_id = of_get_property(np, "reg", NULL);
+- int id, err;
++ int id;
+
+ if (!_id) {
+ dev_err(eth->dev, "missing mac id\n");
+@@ -1768,8 +1766,8 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ GFP_KERNEL);
+ if (!mac->hw_stats) {
+ dev_err(eth->dev, "failed to allocate counter memory\n");
+- err = -ENOMEM;
+- goto free_netdev;
++ free_netdev(eth->netdev[id]);
++ return -ENOMEM;
+ }
+ spin_lock_init(&mac->hw_stats->stats_lock);
+ mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+@@ -1783,21 +1781,9 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ eth->netdev[id]->features |= MTK_HW_FEATURES;
+ eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
+
+- err = register_netdev(eth->netdev[id]);
+- if (err) {
+- dev_err(eth->dev, "error bringing up device\n");
+- goto free_netdev;
+- }
+ eth->netdev[id]->irq = eth->irq[0];
+- netif_info(eth, probe, eth->netdev[id],
+- "mediatek frame engine at 0x%08lx, irq %d\n",
+- eth->netdev[id]->base_addr, eth->irq[0]);
+
+ return 0;
+-
+-free_netdev:
+- free_netdev(eth->netdev[id]);
+- return err;
+ }
+
+ static int mtk_probe(struct platform_device *pdev)
+@@ -1865,14 +1851,13 @@ static int mtk_probe(struct platform_device *pdev)
+ clk_prepare_enable(eth->clk_gp1);
+ clk_prepare_enable(eth->clk_gp2);
+
++ eth->switch_np = of_parse_phandle(pdev->dev.of_node,
++ "mediatek,switch", 0);
++
+ eth->dev = &pdev->dev;
+ eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
+ INIT_WORK(ð->pending_work, mtk_pending_work);
+
+- err = mtk_hw_init(eth);
+- if (err)
+- return err;
+-
+ for_each_child_of_node(pdev->dev.of_node, mac_np) {
+ if (!of_device_is_compatible(mac_np,
+ "mediatek,eth-mac"))
+@@ -1886,6 +1871,22 @@ static int mtk_probe(struct platform_device *pdev)
+ goto err_free_dev;
+ }
+
++ err = mtk_hw_init(eth);
++ if (err)
++ return err;
++
++ for (i = 0; i < MTK_MAX_DEVS; i++) {
++ if (!eth->netdev[i])
++ continue;
++ err = register_netdev(eth->netdev[i]);
++ if (err)
++ dev_err(eth->dev, "error bringing up device\n");
++ else
++ netif_info(eth, probe, eth->netdev[i],
++ "mediatek frame engine at 0x%08lx, irq %d\n",
++ eth->netdev[i]->base_addr, eth->irq[0]);
++ }
++
+ /* we run 2 devices on the same DMA ring so we need a dummy device
+ * for NAPI to work
+ */
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index e39da72..75692cc 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -407,6 +407,9 @@ struct mtk_eth {
+ struct clk *clk_gp2;
+ struct mii_bus *mii_bus;
+ struct work_struct pending_work;
++
++ struct device_node *switch_np;
++ void *sw_priv;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
+@@ -434,4 +437,6 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+ u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
++int mt7623_gsw_config(struct mtk_eth *eth);
++
+ #endif /* MTK_ETH_H */
+--
+1.7.10.4
+
--- /dev/null
+From c1ff5519a7fd849da5d169036d8175383f807962 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Mon, 11 Apr 2016 06:00:23 +0200
+Subject: [PATCH 102/102] net: mediatek: v4.4 backports
+
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 7788ba6..22caad3 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -217,7 +217,7 @@ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
+
+ dev_info(eth->dev,
+ "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n",
+- mac->id, phydev_name(phydev), phydev->phy_id,
++ mac->id, dev_name(&phydev->dev), phydev->phy_id,
+ phydev->drv->name);
+
+ mac->phy_dev = phydev;
+@@ -1396,6 +1396,7 @@ static int mtk_stop(struct net_device *dev)
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
++ netif_carrier_off(dev);
+ netif_tx_disable(dev);
+ phy_stop(mac->phy_dev);
+
+@@ -1595,11 +1596,13 @@ static int mtk_set_settings(struct net_device *dev,
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+
+- if (cmd->phy_address != mac->phy_dev->mdio.addr) {
+- mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus,
+- cmd->phy_address);
+- if (!mac->phy_dev)
++ if (cmd->phy_address != mac->phy_dev->addr) {
++ if (mac->hw->mii_bus->phy_map[cmd->phy_address]) {
++ mac->phy_dev =
++ mac->hw->mii_bus->phy_map[cmd->phy_address];
++ } else {
+ return -ENODEV;
++ }
+ }
+
+ return phy_ethtool_sset(mac->phy_dev, cmd);
+--
+1.7.10.4
+
--- /dev/null
+--- a/drivers/mtd/devices/block2mtd.c
++++ b/drivers/mtd/devices/block2mtd.c
+@@ -32,6 +32,8 @@
+ #include <linux/slab.h>
+ #include <linux/major.h>
+
++static const char * const block2mtd_probe_types[] = { "cmdlinepart", NULL };
++
+ /* Info for the block device */
+ struct block2mtd_dev {
+ struct list_head list;
+@@ -227,6 +229,7 @@ static struct block2mtd_dev *add_device(
+ #endif
+ const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
+ struct block_device *bdev = ERR_PTR(-ENODEV);
++ struct mtd_part_parser_data ppdata = { 0 };
+ struct block2mtd_dev *dev;
+ struct mtd_partition *part;
+ char *name;
+@@ -307,11 +310,7 @@ static struct block2mtd_dev *add_device(
+ dev->mtd.priv = dev;
+ dev->mtd.owner = THIS_MODULE;
+
+- part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL);
+- part->name = name;
+- part->offset = 0;
+- part->size = dev->mtd.size;
+- if (mtd_device_register(&dev->mtd, part, 1)) {
++ if (mtd_device_parse_register(&dev->mtd, block2mtd_probe_types, &ppdata, NULL, 0)) {
+ /* Device didn't get added, so free the entry */
+ goto err_destroy_mutex;
+ }