mediatek: update patches
authorJohn Crispin <john@phrozen.org>
Mon, 23 May 2016 09:20:20 +0000 (11:20 +0200)
committerJohn Crispin <john@phrozen.org>
Mon, 23 May 2016 09:20:20 +0000 (11:20 +0200)
* fixes NAND
* adds latest ethernet patches

Signed-off-by: John Crispin <john@phrozen.org>
151 files changed:
target/linux/mediatek/Makefile
target/linux/mediatek/base-files/etc/board.d/02_network
target/linux/mediatek/config-4.4
target/linux/mediatek/files/arch/arm/boot/dts/_mt7623.dtsi [new file with mode: 0644]
target/linux/mediatek/files/arch/arm/boot/dts/mt7623-NAND.dts
target/linux/mediatek/files/arch/arm/boot/dts/mt7623-eMMC.dts
target/linux/mediatek/image/Makefile
target/linux/mediatek/patches-4.4/0001-NET-multi-phy-support.patch
target/linux/mediatek/patches-4.4/0002-soc-mediatek-Separate-scpsys-driver-common-code.patch
target/linux/mediatek/patches-4.4/0003-soc-mediatek-Init-MT8173-scpsys-driver-earlier.patch
target/linux/mediatek/patches-4.4/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch
target/linux/mediatek/patches-4.4/0005-soc-mediatek-Add-MT2701-MT7623-scpsys-driver.patch
target/linux/mediatek/patches-4.4/0006-clk-mediatek-Refine-the-makefile-to-support-multiple.patch
target/linux/mediatek/patches-4.4/0007-dt-bindings-ARM-Mediatek-Document-bindings-for-MT270.patch
target/linux/mediatek/patches-4.4/0008-clk-mediatek-Add-dt-bindings-for-MT2701-clocks.patch
target/linux/mediatek/patches-4.4/0009-clk-mediatek-Add-MT2701-clock-support.patch
target/linux/mediatek/patches-4.4/0010-reset-mediatek-mt2701-reset-controller-dt-binding-fi.patch
target/linux/mediatek/patches-4.4/0011-reset-mediatek-mt2701-reset-driver.patch
target/linux/mediatek/patches-4.4/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch
target/linux/mediatek/patches-4.4/0013-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt2.patch
target/linux/mediatek/patches-4.4/0014-pinctrl-dt-bindings-Add-pinfunc-header-file-for-mt27.patch
target/linux/mediatek/patches-4.4/0015-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt7.patch
target/linux/mediatek/patches-4.4/0016-pinctrl-dt-bindings-Add-pinctrl-file-for-mt7623.patch
target/linux/mediatek/patches-4.4/0017-clk-add-hifsys-reset.patch
target/linux/mediatek/patches-4.4/0018-dt-bindings-Add-a-binding-for-Mediatek-xHCI-host-con.patch
target/linux/mediatek/patches-4.4/0019-xhci-mediatek-support-MTK-xHCI-host-controller.patch
target/linux/mediatek/patches-4.4/0020-arm64-dts-mediatek-add-xHCI-usb-phy-for-mt8173.patch
target/linux/mediatek/patches-4.4/0021-Document-DT-Add-bindings-for-mediatek-MT7623-SoC-Pla.patch
target/linux/mediatek/patches-4.4/0022-soc-mediatek-add-compat-string-for-mt7623-to-scpsys.patch
target/linux/mediatek/patches-4.4/0023-ARM-dts-mediatek-add-MT7623-basic-support.patch
target/linux/mediatek/patches-4.4/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch
target/linux/mediatek/patches-4.4/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch
target/linux/mediatek/patches-4.4/0026-scpsys-various-fixes.patch
target/linux/mediatek/patches-4.4/0027-soc-mediatek-PMIC-wrap-Clear-the-vldclr-if-state-mac.patch
target/linux/mediatek/patches-4.4/0028-ARM-mediatek-add-MT7623-smp-bringup-code.patch
target/linux/mediatek/patches-4.4/0029-soc-mediatek-PMIC-wrap-clear-the-STAUPD_TRIG-bit-of-.patch
target/linux/mediatek/patches-4.4/0030-ARM-mediatek-add-mt2701-smp-bringup-code.patch
target/linux/mediatek/patches-4.4/0031-dt-bindings-ARM-Mediatek-add-MT2701-7623-string-to-t.patch
target/linux/mediatek/patches-4.4/0032-soc-mediatek-PMIC-wrap-don-t-duplicate-the-wrapper-d.patch
target/linux/mediatek/patches-4.4/0033-soc-mediatek-PMIC-wrap-add-wrapper-callbacks-for-ini.patch
target/linux/mediatek/patches-4.4/0034-soc-mediatek-PMIC-wrap-split-SoC-specific-init-into-.patch
target/linux/mediatek/patches-4.4/0035-soc-mediatek-PMIC-wrap-WRAP_INT_EN-needs-a-different.patch
target/linux/mediatek/patches-4.4/0036-soc-mediatek-PMIC-wrap-SPI_WRITE-needs-a-different-b.patch
target/linux/mediatek/patches-4.4/0037-soc-mediatek-PMIC-wrap-move-wdt_src-into-the-pmic_wr.patch
target/linux/mediatek/patches-4.4/0038-soc-mediatek-PMIC-wrap-remove-pwrap_is_mt8135-and-pw.patch
target/linux/mediatek/patches-4.4/0039-soc-mediatek-PMIC-wrap-add-a-slave-specific-struct.patch
target/linux/mediatek/patches-4.4/0040-soc-mediatek-PMIC-wrap-add-mt6323-slave-support.patch
target/linux/mediatek/patches-4.4/0041-soc-mediatek-PMIC-wrap-add-MT2701-7623-support.patch
target/linux/mediatek/patches-4.4/0042-dt-bindings-mfd-Add-bindings-for-the-MediaTek-MT6323.patch
target/linux/mediatek/patches-4.4/0043-mfd-mt6397-int_con-and-int_status-may-vary-in-locati.patch
target/linux/mediatek/patches-4.4/0044-mfd-mt6397-add-support-for-different-Slave-types.patch
target/linux/mediatek/patches-4.4/0045-mfd-mt6397-add-MT6323-support-to-MT6397-driver.patch
target/linux/mediatek/patches-4.4/0046-regulator-Add-document-for-MT6323-regulator.patch
target/linux/mediatek/patches-4.4/0047-regulator-mt6323-Add-support-for-MT6323-regulator.patch
target/linux/mediatek/patches-4.4/0048-net-next-mediatek-document-MediaTek-SoC-ethernet-bin.patch
target/linux/mediatek/patches-4.4/0049-net-next-mediatek-add-support-for-MT7623-ethernet.patch
target/linux/mediatek/patches-4.4/0050-net-next-mediatek-add-Kconfig-and-Makefile.patch
target/linux/mediatek/patches-4.4/0051-net-next-mediatek-add-an-entry-to-MAINTAINERS.patch
target/linux/mediatek/patches-4.4/0052-clk-dont-disable-unused-clocks.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0052-mtd-nand-add-an-mtd_to_nand-helper.patch [deleted file]
target/linux/mediatek/patches-4.4/0053-clk-mediatek-enable-critical-clocks.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0053-mtd-nand-add-nand_to_mtd-helper.patch [deleted file]
target/linux/mediatek/patches-4.4/0054-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0054-mtd-nand-add-helpers-to-access-priv.patch [deleted file]
target/linux/mediatek/patches-4.4/0055-cpufreq-mediatek-add-driver.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0055-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch [deleted file]
target/linux/mediatek/patches-4.4/0056-arm-mediatek-make-a7-timer-work-Signed-off-by-John-C.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0056-mtd-add-get-set-of_node-flash_node-helpers.patch [deleted file]
target/linux/mediatek/patches-4.4/0057-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch [deleted file]
target/linux/mediatek/patches-4.4/0057-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0058-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch [deleted file]
target/linux/mediatek/patches-4.4/0058-net-mediatek-unlock-on-error-in-mtk_tx_map.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0059-mtd-nand-backport-fixes.patch [deleted file]
target/linux/mediatek/patches-4.4/0059-net-mediatek-use-dma_addr_t-correctly.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0060-clk-dont-disable-unused-clocks.patch [deleted file]
target/linux/mediatek/patches-4.4/0060-net-mediatek-remove-incorrect-dma_mask-assignment.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0061-clk-mediatek-enable-critical-clocks.patch [deleted file]
target/linux/mediatek/patches-4.4/0061-net-mediatek-check-device_reset-return-code.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0062-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch [deleted file]
target/linux/mediatek/patches-4.4/0062-net-mediatek-watchdog_timeo-was-not-set.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0063-cpufreq-mediatek-add-driver.patch [deleted file]
target/linux/mediatek/patches-4.4/0063-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0064-arm-mediatek-make-a7-timer-work.patch [deleted file]
target/linux/mediatek/patches-4.4/0064-net-mediatek-remove-superflous-reset-call.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0065-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch [deleted file]
target/linux/mediatek/patches-4.4/0065-net-mediatek-fix-stop-and-wakeup-of-queue.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0066-net-mediatek-fix-mtk_pending_work.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0066-net-mediatek-unlock-on-error-in-mtk_tx_map.patch [deleted file]
target/linux/mediatek/patches-4.4/0067-net-mediatek-fix-TX-locking.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0067-net-mediatek-use-dma_addr_t-correctly.patch [deleted file]
target/linux/mediatek/patches-4.4/0068-net-mediatek-move-the-pending_work-struct-to-the-dev.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0068-net-mediatek-remove-incorrect-dma_mask-assignment.patch [deleted file]
target/linux/mediatek/patches-4.4/0069-net-mediatek-check-device_reset-return-code.patch [deleted file]
target/linux/mediatek/patches-4.4/0069-net-mediatek-do-not-set-the-QID-field-in-the-TX-DMA-.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0070-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0070-net-mediatek-watchdog_timeo-was-not-set.patch [deleted file]
target/linux/mediatek/patches-4.4/0071-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch [deleted file]
target/linux/mediatek/patches-4.4/0071-pwm-add-pwm-mediatek.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0072-mtd-backport-v4.7-0day-patches-from-Boris.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0072-net-mediatek-remove-superflous-reset-call.patch [deleted file]
target/linux/mediatek/patches-4.4/0073-net-mediatek-fix-stop-and-wakeup-of-queue.patch [deleted file]
target/linux/mediatek/patches-4.4/0073-of-mtd-prepare-helper-reading-NAND-ECC-algo-from-DT.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0074-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0074-net-mediatek-fix-mtk_pending_work.patch [deleted file]
target/linux/mediatek/patches-4.4/0075-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0075-net-mediatek-fix-TX-locking.patch [deleted file]
target/linux/mediatek/patches-4.4/0076-mtd-nand-add-power-domains-to-the-mediatek-driver.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0076-net-mediatek-move-the-pending_work-struct-to-the-dev.patch [deleted file]
target/linux/mediatek/patches-4.4/0077-net-mediatek-do-not-set-the-QID-field-in-the-TX-DMA-.patch [deleted file]
target/linux/mediatek/patches-4.4/0077-net-next-mediatek-use-mdiobus_free-in-favour-of-kfre.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0078-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch [deleted file]
target/linux/mediatek/patches-4.4/0078-net-next-mediatek-fix-gigabit-and-flow-control-adver.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0079-net-next-mediatek-add-fixed-phy-support.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0079-net-next-mediatek-fix-BQL-support.patch [deleted file]
target/linux/mediatek/patches-4.4/0080-net-next-mediatek-fix-gigabit-and-flow-control-adver.patch [deleted file]
target/linux/mediatek/patches-4.4/0080-net-next-mediatek-properly-handle-RGMII-modes.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0081-net-next-mediatek-add-fixed-phy-support.patch [deleted file]
target/linux/mediatek/patches-4.4/0081-net-next-mediatek-fix-DQL-support.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0082-net-next-mediatek-add-RX-delay-support.patch [deleted file]
target/linux/mediatek/patches-4.4/0082-net-next-mediatek-add-missing-return-code-check.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0083-net-next-mediatek-add-missing-return-code-check.patch [deleted file]
target/linux/mediatek/patches-4.4/0083-net-next-mediatek-fix-missing-free-of-scratch-memory.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0084-net-next-mediatek-fix-missing-free-of-scratch-memory.patch [deleted file]
target/linux/mediatek/patches-4.4/0084-net-next-mediatek-invalid-buffer-lookup-in-mtk_tx_ma.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0085-net-next-mediatek-dropped-rx-packets-are-not-being-c.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0085-net-next-mediatek-invalid-buffer-lookup-in-mtk_tx_ma.patch [deleted file]
target/linux/mediatek/patches-4.4/0086-net-next-mediatek-add-next-data-pointer-coherency-pr.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0086-net-next-mediatek-dropped-rx-packets-are-not-being-c.patch [deleted file]
target/linux/mediatek/patches-4.4/0087-net-next-mediatek-add-IRQ-locking.patch [deleted file]
target/linux/mediatek/patches-4.4/0087-net-next-mediatek-disable-all-interrupts-during-prob.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0088-net-next-mediatek-add-support-for-IRQ-grouping.patch [deleted file]
target/linux/mediatek/patches-4.4/0088-net-next-mediatek-fix-threshold-value.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0089-net-mediatek-add-gsw-mt7530-driver.patch [deleted file]
target/linux/mediatek/patches-4.4/0089-net-next-mediatek-increase-watchdog_timeo.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0090-net-mediatek-v4.4-backports.patch [deleted file]
target/linux/mediatek/patches-4.4/0090-net-next-mediatek-fix-off-by-one-in-the-TX-ring-allo.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0091-net-next-mediatek-WIP.patch [deleted file]
target/linux/mediatek/patches-4.4/0091-net-next-mediatek-only-wake-the-queue-if-it-is-stopp.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0092-net-next-mediatek-remove-superfluous-queue-wake-up-c.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0093-net-next-mediatek-remove-superfluous-register-reads.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0094-net-next-mediatek-don-t-use-intermediate-variables-t.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0095-net-next-mediatek-add-IRQ-locking.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0096-net-next-mediatek-add-support-for-IRQ-grouping.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0097-net-next-mediatek-change-my-email-address.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0098-net-next-mediatek-only-trigger-the-tx-watchdog-reset.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0099-MAINTAINERS-change-my-email-address.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0100-MAINTAINERS-add-Sean-as-mediatek-ethernet-maintainer.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0100-block2mtd.patch [deleted file]
target/linux/mediatek/patches-4.4/0101-net-mediatek-add-gsw-mt7530-driver.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0102-net-mediatek-v4.4-backports.patch [new file with mode: 0644]
target/linux/mediatek/patches-4.4/0201-block2mtd.patch [new file with mode: 0644]

index 6b3baa44ec01383f687083d49377ff45d35f9efb..17deab16a30326accc18d7362c48acf38d11626a 100644 (file)
@@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk
 ARCH:=arm
 BOARD:=mediatek
 BOARDNAME:=Mediatek Ralink ARM
-FEATURES:=squashfs
+FEATURES:=squashfs jffs2
 CPU_TYPE:=cortex-a7
 MAINTAINER:=John Crispin <john@phrozen.org>
 
index b936d0e1502f9daf2e28d8f6d6eeffcdea77899f..1d1b5c021ad2ce513207bc0557f9f8cab0b85eda 100755 (executable)
@@ -10,6 +10,8 @@ mediatek_setup_interfaces()
        local board="$1"
 
        case $board in
+       eMMC | \
+       NAND | \
        mt7623_evb)
                ucidef_set_interfaces_lan_wan "eth0" "eth1"
                ucidef_add_switch "switch0" \
index cd8b915fd6426578454516f2679a537c4225aae3..6221024d5986733fd386da28d631ffb644a3713d 100644 (file)
@@ -296,9 +296,10 @@ CONFIG_MODULES_USE_ELF_REL=y
 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
@@ -406,6 +407,10 @@ CONFIG_PREEMPT_COUNT=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
diff --git a/target/linux/mediatek/files/arch/arm/boot/dts/_mt7623.dtsi b/target/linux/mediatek/files/arch/arm/boot/dts/_mt7623.dtsi
new file mode 100644 (file)
index 0000000..4afcc75
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+ * 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>,
+                        <&ethsys CLK_ETHSYS_ESW>,
+                        <&ethsys CLK_ETHSYS_GP2>,
+                        <&ethsys 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 = <&ethsys 6>;
+               reset-names = "eth";
+
+               mediatek,ethsys = <&ethsys>;
+               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 = <&ethsys 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 = <&ethsys>;
+               status = "disabled";
+       };
+};
index d25e46ebae78a945644ccecf66a5de5613f001e2..43551f7a074c512c34f7ca16d6643e7900eb401f 100644 (file)
 
 /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";
+};
index 2b75b44541d4b4a994b91713a728650cf334b2bc..630240c730135b37be96a58faf19de67ecf8adee 100644 (file)
@@ -14,7 +14,7 @@
 
 /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";
+};
index 7169907142bef4db0e86fcb032153babfacc7edf..b88d2dce2fae88d4930dadfdb1a124011ce23fee 100644 (file)
@@ -17,7 +17,7 @@ ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),)
 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) \
@@ -29,8 +29,13 @@ define Image/Build/squashfs
        $(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
index 35a2d4f4397914139f1f0b4a4c37511d0a0285cf..6ff4e7507b68dadb044ff1fe2a08c35d46f900f7 100644 (file)
@@ -1,7 +1,7 @@
-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>
 ---
@@ -9,9 +9,11 @@ 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;
@@ -21,7 +23,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                        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;
@@ -31,7 +33,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                }
  
                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;
@@ -41,6 +43,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                        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 {
@@ -51,3 +55,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        enum phy_state state;
  
+-- 
+1.7.10.4
+
index 074f68cbc97cf3c83dc56d54b39b76c6aa7d4853..9514e861867d79699b8beb75a359889dc2af78c6 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -17,6 +17,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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
@@ -42,6 +44,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +        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 @@
@@ -49,6 +53,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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 @@
@@ -231,6 +238,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +};
 +
 +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 @@
@@ -248,7 +257,7 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 -#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
@@ -259,6 +268,7 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 -#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
@@ -418,7 +428,7 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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;
  }
  
@@ -508,13 +518,13 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +              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);
@@ -539,7 +549,7 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
                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;
@@ -567,7 +577,7 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  
                /*
                 * 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.
         */
  
@@ -606,6 +616,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 -};
 -
 -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 @@
@@ -663,3 +676,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +                              struct scp *scp, int num);
 +
 +#endif /* __DRV_SOC_MTK_H */
+-- 
+1.7.10.4
+
index 9446bd12d43fa547b0ebe4304ad4471f3e658270..cdcb6c2ecb1b19cceb9921080f4ee405276021d1 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -12,9 +12,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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 = {
        },
  };
  
@@ -31,3 +33,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +
 +subsys_initcall(scpsys_drv_init);
 +module_exit(scpsys_drv_exit);
+-- 
+1.7.10.4
+
index dca106f618d6ca94bf8f2e8098a640425d5c75a0..33e3178d4836c7dc012236c1252eace30765a4cc 100644 (file)
@@ -1,7 +1,7 @@
-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.
 
@@ -12,6 +12,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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 @@
@@ -42,3 +45,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +#define MT2701_POWER_DOMAIN_IFR_MSC   8
 +
 +#endif /* _DT_BINDINGS_POWER_MT2701_POWER_H */
+-- 
+1.7.10.4
+
index db1aeed9b9f438cb2b705ca5d563c37a959099c6..388dc08adcf9f764bbaa95e489cc22e99eeb2ab7 100644 (file)
@@ -1,7 +1,7 @@
-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.
 
@@ -14,6 +14,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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
@@ -31,13 +33,18 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +        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 @@
@@ -202,3 +209,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +
 +MODULE_DESCRIPTION("MediaTek MT2701 scpsys driver");
 +MODULE_LICENSE("GPL v2");
+-- 
+1.7.10.4
+
index 675e2ae9a43bba9764ff73a7edeabe00fe451614..40c38ca9b412915c2f2a7fc5e6058b488526f7ce 100644 (file)
@@ -1,8 +1,8 @@
-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.
@@ -16,6 +16,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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"
@@ -23,6 +25,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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 @@
@@ -49,6 +54,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +      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 @@
@@ -59,3 +66,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 -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
+
index 4ffae5dfdb27c3aea8f5441b2604cd98a20f068d..9a0dc11436a7d05802ca579cb6c5b084aa9693ab 100644 (file)
@@ -1,7 +1,7 @@
-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,
@@ -25,9 +25,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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:
@@ -35,6 +37,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
        - "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 @@
@@ -60,6 +65,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +      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 @@
@@ -85,6 +93,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +      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 @@
@@ -110,9 +121,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +      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:
@@ -120,6 +133,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
        - "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.
@@ -130,9 +145,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
        - "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:
@@ -140,6 +157,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
        - "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.
@@ -150,9 +169,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
        - "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:
@@ -160,9 +181,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
        - "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:
@@ -170,3 +193,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
        - "mediatek,mt8173-vdecsys", "syscon"
  - #clock-cells: Must be 1
  
+-- 
+1.7.10.4
+
index 29e0ae46f2e2ea10bf4f86f99ce7f1ffd5574121..4057365849f1dadb074e121471d74578fea78b5f 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -13,6 +13,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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 @@
@@ -497,3 +500,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +#define CLK_BDP_NR                            50
 +
 +#endif /* _DT_BINDINGS_CLK_MT2701_H */
+-- 
+1.7.10.4
+
index 5f87079b27afe202ff4921e673af978b6058b25f..488fd3f8241df7781b98d0675e0431ad1f18db57 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -19,6 +19,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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
@@ -36,6 +38,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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 @@
@@ -44,9 +48,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +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));
  }
  
@@ -73,7 +79,7 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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);
  }
  
@@ -104,7 +110,7 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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,
  };
  
@@ -123,9 +129,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  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;
@@ -134,6 +142,9 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  
  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 @@
@@ -1347,9 +1358,11 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +}
 +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;
        }
  }
@@ -1378,6 +1391,8 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
 +                      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 {
@@ -1429,3 +1444,6 @@ Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
  
  struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
  
+-- 
+1.7.10.4
+
index e25a70b597298ee32615bf3aa21e437e6d6711fe..e2988b6b18a1106c15635e0ea0d8b1521ad628fa 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -14,6 +14,9 @@ Signed-off-by: Shunli Wang <shunli.wang@mediatek.com>
  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 @@
@@ -91,3 +94,6 @@ Signed-off-by: Shunli Wang <shunli.wang@mediatek.com>
 +#define MT2701_TOPRGU_BDP_DISP_RST            13
 +
 +#endif  /* _DT_BINDINGS_RESET_CONTROLLER_MT2701 */
+-- 
+1.7.10.4
+
index b0171d9e664067a905c7f02aadf0d38962fc7899..e69c043741c447f8d11ead2fa8b66b78e85ab4e6 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -14,9 +14,11 @@ Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
  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);
@@ -25,7 +27,7 @@ Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
  }
  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);
@@ -34,3 +36,6 @@ Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
  }
  CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt2701-pericfg", mtk_pericfg_init);
  
+-- 
+1.7.10.4
+
index 01349d66fa3e33074e4cb3e1663c5b43fd92d50c..8d699eb8801e2b2a959d45bd160d9a90fb44b9e5 100644 (file)
@@ -1,8 +1,8 @@
-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.
@@ -14,6 +14,8 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
  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
@@ -27,3 +29,6 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
  config MACH_MT6589
        bool "MediaTek MT6589 SoCs support"
        default ARCH_MEDIATEK
+-- 
+1.7.10.4
+
index 74f35f70b33a5ddaae6cfad090cc154e2c87d095..8308771aa15a95252a330d4edb3897f3b699635c 100644 (file)
@@ -1,7 +1,7 @@
-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>
@@ -11,9 +11,11 @@ Reviewed-by: Mathias Brugger <matthias.bgg@gmail.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.
@@ -29,3 +31,6 @@ Reviewed-by: Mathias Brugger <matthias.bgg@gmail.com>
  - 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
+
index 7664fc837bb5dee58a549c269ededfe71a57afff..38e3eaeb6112f8813597f27add94c9f14c940812 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -21,6 +21,9 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
  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 @@
@@ -759,6 +762,8 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
 +#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
@@ -774,6 +779,8 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
  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 @@
@@ -784,6 +791,9 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
  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 @@
@@ -1373,6 +1383,8 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
 +}
 +
 +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 @@
@@ -1384,7 +1396,7 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
  };
  
  /*
-@@ -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);
  
@@ -1394,7 +1406,7 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
        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:
@@ -1402,7 +1414,7 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
                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:
@@ -1410,7 +1422,7 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
                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);
  
@@ -1425,7 +1437,7 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
        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);
@@ -1436,6 +1448,8 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
        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 {
@@ -1464,6 +1478,9 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
        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 @@
@@ -3790,3 +3807,6 @@ Acked-by: Linus Walleij <linus.walleij@linaro.org>
 +};
 +
 +#endif /* __PINCTRL_MTK_MT2701_H */
+-- 
+1.7.10.4
+
index 2272b1a1f8b3fb9395118408a577f0971ed2aff3..7fed07c3bc8969b60d1146d24c88f304be6896b7 100644 (file)
@@ -1,7 +1,7 @@
-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>
@@ -11,6 +11,8 @@ 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:
@@ -21,6 +23,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        "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 @@
@@ -545,3 +550,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +
 +#endif /* __DTS_MT7623_PINFUNC_H */
 +
+-- 
+1.7.10.4
+
index 35082ea546b76fabee887f0759fb7b88e9e66b3d..e4440828fbdf1e528f133c058971207ea4b04057 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -17,6 +17,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
@@ -32,9 +34,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
@@ -42,6 +46,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 @@
@@ -425,6 +432,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +}
 +
 +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 @@
@@ -2365,6 +2375,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +};
 +
 +#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 @@
@@ -2377,3 +2389,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  #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
+
index 79647d31ff447849e18cd9d43167e5c1e719ea9e..d443971aa711d26b1839ec75261af85ec2a07ab4 100644 (file)
@@ -1,7 +1,7 @@
-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,
 
@@ -18,9 +18,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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);
@@ -29,6 +31,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  }
  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 @@
@@ -45,3 +49,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +#define MT2701_HIFSYS_PCIE2_RST                       26
 +
  #endif  /* _DT_BINDINGS_RESET_CONTROLLER_MT2701 */
+-- 
+1.7.10.4
+
index 61f1fb59cc0886ebd9622e585b5479adb61cbb0b..357ef7ef0577f98bbe5ea0f25b60ed693876b4cd 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -13,6 +13,9 @@ Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
  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 @@
@@ -67,3 +70,6 @@ Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
 +      mediatek,syscon-wakeup = <&pericfg>;
 +      mediatek,wakeup-src = <1>;
 +};
+-- 
+1.7.10.4
+
index 846afc5ee1ad9b073ef32200db009cffd9b4a9d9..a7dd38a643df2b9d9326b5d64f2529d75c0989a0 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -31,6 +31,8 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
  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
@@ -49,6 +51,8 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
  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
@@ -69,6 +73,9 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
  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 @@
@@ -487,6 +494,9 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
 +      }
 +}
 +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 @@
@@ -1253,6 +1263,9 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
 +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 @@
@@ -1418,6 +1431,8 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
 +#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 @@
@@ -1428,7 +1443,7 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
  
  /*
   * 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;
  
@@ -1455,7 +1470,7 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
        /* 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 */
@@ -1464,6 +1479,8 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
                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 @@
@@ -1474,7 +1491,7 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
  
  #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;
@@ -1487,7 +1504,7 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
        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);
  
@@ -1497,7 +1514,7 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
        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;
        }
  
@@ -1513,9 +1530,11 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
        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)
@@ -1523,3 +1542,6 @@ Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
        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
+
index a0e96440b1981c3ce60b1dbc7f445f32d3202b8e..5a5476f8bf58f94316e2e4da911b80edbd3c8edd 100644 (file)
@@ -1,7 +1,7 @@
-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
 
@@ -11,6 +11,8 @@ Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
  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 @@
@@ -47,6 +49,8 @@ Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
 +      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 @@
@@ -105,3 +109,6 @@ Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
                mmsys: clock-controller@14000000 {
                        compatible = "mediatek,mt8173-mmsys", "syscon";
                        reg = <0 0x14000000 0 0x1000>;
+-- 
+1.7.10.4
+
index a2b37d894c24aa2591f6404f7ed9156d3bb22ddc..5fdfc04a3da6c2da654efa1a68d5860084f4c458 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -13,6 +13,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
@@ -33,6 +35,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  - 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 @@
@@ -43,6 +47,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
    * "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:
@@ -53,3 +59,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        * "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
+
index 206fce55c44007bf5bab8e2c282db8ae39f90dac..282686cbbe7be8c35c8c3097887c635025b6bbdd 100644 (file)
@@ -1,16 +1,19 @@
-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",
        }, {
@@ -19,3 +22,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                /* sentinel */
        }
  };
+-- 
+1.7.10.4
+
index a38a820ad4352c0b1b22a226f820e738d10db5be..22cd1edbca95a556a3ab4a6045a9304924d3bec4 100644 (file)
@@ -1,21 +1,23 @@
-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) += \
@@ -26,9 +28,12 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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>
@@ -318,127 +323,32 @@ Signed-off-by: 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>;
 +              };
 +      };
@@ -469,6 +379,48 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +      };
 +};
 +
++&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>;
@@ -503,9 +455,12 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +      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>
@@ -846,6 +801,7 @@ Signed-off-by: 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";
@@ -1065,8 +1021,15 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +                      compatible = "mediatek,eth-mac";
 +                      reg = <1>;
 +
-+                      phy-handle = <&phy5>;
 +                      status = "disabled";
++                      
++                      phy-mode = "rgmii";
++                      
++                      fixed-link {
++                              speed = <1000>;
++                              full-duplex;
++                              pause;
++                      };
 +              };
 +      
 +              mdio-bus {
@@ -1099,6 +1062,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +              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
@@ -1112,9 +1077,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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",
@@ -1122,3 +1089,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        "mediatek,mt8127",
        "mediatek,mt8135",
        NULL,
+-- 
+1.7.10.4
+
index fc924f83a905a39854bd96cf582579baa35ffea9..0df02b6ae64c3caabf55e84b1d654650c23400d1 100644 (file)
@@ -1,7 +1,7 @@
-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>
 ---
@@ -9,6 +9,9 @@ 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 @@
@@ -152,3 +155,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +                      status = "okay";
 +              };
 +      };
+-- 
+1.7.10.4
+
index 52e03b011d8b0410f40706a782b827aad4ee7f4e..5079c2b5696fe00ef2a932cfa3a4e96ea9b35204 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -17,6 +17,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
@@ -27,9 +29,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
  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
  
@@ -45,13 +49,18 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +        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 @@
@@ -696,3 +705,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +}
 +
 +module_init(mtk_pcie_init);
+-- 
+1.7.10.4
+
index 663a34131da79d4deb705edc896fceb9f1a13180..263539232fa561956b428b4e288bfeac31cf2385 100644 (file)
@@ -1,7 +1,7 @@
-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 ++
@@ -9,9 +9,11 @@ Subject: [PATCH 26/91] scpsys: various fixes
  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);
@@ -20,9 +22,11 @@ Subject: [PATCH 26/91] scpsys: various fixes
  }
  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,
        },
@@ -37,6 +41,8 @@ Subject: [PATCH 26/91] scpsys: various fixes
        [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 @@
@@ -54,3 +60,6 @@ Subject: [PATCH 26/91] scpsys: various fixes
 +#define MT2701_POWER_DOMAIN_IFR_MSC   2
  
  #endif /* _DT_BINDINGS_POWER_MT2701_POWER_H */
+-- 
+1.7.10.4
+
index 6be1b186655068f0019b1e60bef0cf51fb3906bb..f80a677134a796dfc9ec4a1c78263b89b436ab65 100644 (file)
@@ -1,7 +1,7 @@
-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,
@@ -20,9 +20,11 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  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;
  }
  
@@ -43,7 +45,7 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  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);
@@ -55,7 +57,7 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  
        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);
@@ -67,3 +69,6 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  
        pwrap_writel(wrp, (adr >> 1) << 16, PWRAP_WACS2_CMD);
  
+-- 
+1.7.10.4
+
index 5b8490ac9b106e2d670884835a13dbc314ea37a1..130850e1975048764157048fac76dc475fb4698a 100644 (file)
@@ -1,7 +1,7 @@
-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.
 
@@ -11,9 +11,11 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  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 },
  };
  
@@ -26,7 +28,7 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  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 },
@@ -34,3 +36,6 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  };
  
  static void __iomem *mtk_smp_base;
+-- 
+1.7.10.4
+
index ef8c49e32bc8d15744c0a3693d8aa6457ee141a6..3c38ac4935556f60c0b89c721ec6824cfac4676f 100644 (file)
@@ -1,8 +1,8 @@
-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.
@@ -14,6 +14,8 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  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 @@
@@ -32,7 +34,7 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  /* 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)
  {
@@ -41,7 +43,7 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
        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);
@@ -56,3 +58,6 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
        pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
        pwrap_writel(wrp, ~((1 << 31) | (1 << 1)), PWRAP_INT_EN);
  
+-- 
+1.7.10.4
+
index 7ae503980223b361d74720dcd9638e352af6c5f3..940fa44337d52d0410aa80c3a9e6829ed9311e81 100644 (file)
@@ -1,7 +1,7 @@
-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.
 
@@ -11,9 +11,11 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  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 },
@@ -21,3 +23,6 @@ Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
  };
  
  static const struct of_device_id mtk_smp_boot_infos[] __initconst = {
+-- 
+1.7.10.4
+
index 348f81c0926aaa776eeb4d05a83ae885bc2f3f0f..6a3ec46a484f2a2f1d108c5eb0db6db07a597377 100644 (file)
@@ -1,8 +1,8 @@
-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>
@@ -11,6 +11,8 @@ Cc: devicetree@vger.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
@@ -21,3 +23,6 @@ Cc: devicetree@vger.kernel.org
        "mediatek,mt8135-pwrap" for MT8135 SoCs
        "mediatek,mt8173-pwrap" for MT8173 SoCs
  - interrupts: IRQ for pwrap in SOC
+-- 
+1.7.10.4
+
index a65e6583133a2a7bc199a36a9439b34047076910..b7afdbc273f8058eb7b381d6a04268201b959192 100644 (file)
@@ -1,8 +1,8 @@
-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
@@ -13,6 +13,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -53,7 +55,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  }
  
  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);
  
@@ -62,7 +64,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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);
@@ -71,7 +73,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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);
@@ -79,7 +81,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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);
  
@@ -91,3 +93,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        wrp->dev = &pdev->dev;
  
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap");
+-- 
+1.7.10.4
+
index fac7174ab5eb2f9156563bc06696f9ea3503f9d2..25d7f746c4c3ebed55f75ddd4c5c44fc60731bff 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -12,6 +12,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -53,7 +55,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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;
  }
  
@@ -90,7 +92,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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);
  
@@ -99,7 +101,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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,
  };
  
@@ -120,3 +122,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  static struct of_device_id of_pwrap_match_tbl[] = {
        {
                .compatible = "mediatek,mt8135-pwrap",
+-- 
+1.7.10.4
+
index 4d400c9bf8848f34a53412cc8099323ff34829e5..3a74a6a42b55fa76f460d86c66e440d5b08ac6a1 100644 (file)
@@ -1,8 +1,8 @@
-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
@@ -16,6 +16,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -26,7 +28,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  };
  
  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;
  }
  
@@ -68,7 +70,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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);
  
@@ -104,7 +106,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        }
  
        /* 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,
@@ -112,7 +114,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  };
  
  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,
@@ -120,3 +122,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  };
  
  static struct of_device_id of_pwrap_match_tbl[] = {
+-- 
+1.7.10.4
+
index 859666670e0b0726cbd678dac7a91af835c69177..c51f84cbb8e39db44ab9e8bad739930161f220f1 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -11,6 +11,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -21,7 +23,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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,
@@ -29,7 +31,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .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,
@@ -37,7 +39,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .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);
@@ -46,3 +48,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        irq = platform_get_irq(pdev, 0);
        ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH,
+-- 
+1.7.10.4
+
index dbf81c8d3fea56a0bb1a4cb768f5cc8cfd66c03d..e47a4c5b98d802f6273e45595f4bb64fef8959e4 100644 (file)
@@ -1,8 +1,8 @@
-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
@@ -13,6 +13,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -23,7 +25,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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);
  
@@ -43,7 +45,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                                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)),
@@ -51,7 +53,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .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)),
@@ -59,3 +61,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .init_reg_clock = pwrap_mt8173_init_reg_clock,
        .init_soc_specific = pwrap_mt8173_init_soc_specific,
  };
+-- 
+1.7.10.4
+
index 2c701d420d6f0c506d62c9510e17bd59f4338d42..e08209671833ec23822453015d63dd668771688e 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -13,6 +13,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -23,7 +25,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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,
@@ -31,7 +33,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .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,
@@ -39,7 +41,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .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)
  {
@@ -48,7 +50,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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
         */
@@ -59,3 +61,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
        pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN);
  
+-- 
+1.7.10.4
+
index 79ea3a2d7a97e33850b283053e7c783563e942d2..94d4b8c7529b1ea22058626c925346db87670eb9 100644 (file)
@@ -1,8 +1,8 @@
-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
@@ -18,6 +18,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -42,7 +44,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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);
  
@@ -59,7 +61,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        }
  
        /* 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);
  
@@ -68,7 +70,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                /* 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);
  
@@ -77,7 +79,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                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);
  
@@ -86,7 +88,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                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,
@@ -94,7 +96,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .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,
@@ -102,7 +104,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        .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;
        }
  
@@ -111,3 +113,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                                "pwrap-bridge");
                wrp->bridge_base = devm_ioremap_resource(wrp->dev, res);
+-- 
+1.7.10.4
+
index b72834a978233c19078a91039705935a824158b5..5b1ab7a70f0902b1c717b221081e21c238acfe19 100644 (file)
@@ -1,7 +1,8 @@
-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
@@ -12,6 +13,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 @@
@@ -123,7 +126,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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);
@@ -133,7 +136,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                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;
  
@@ -143,7 +146,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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 */
@@ -162,7 +165,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        /* 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 */
@@ -171,7 +174,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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 */
@@ -186,7 +189,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                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 */
@@ -199,7 +202,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                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 */
@@ -212,7 +215,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                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 */
@@ -221,7 +224,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        /* 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 */
@@ -230,7 +233,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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 */
@@ -246,7 +249,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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,
  };
  
@@ -268,7 +271,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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);
@@ -286,7 +289,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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;
@@ -294,3 +297,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        wrp->dev = &pdev->dev;
  
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap");
+-- 
+1.7.10.4
+
index f4111f1354b064c8c34a8c67f033c9f6d4e27cba..70840e265146913860639507dc8cb5863e821859 100644 (file)
@@ -1,7 +1,7 @@
-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().
@@ -11,6 +11,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 {
@@ -49,7 +51,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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);
  
@@ -69,7 +71,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        /* 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,
  };
  
@@ -81,7 +83,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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[] = {
        {
@@ -91,3 +93,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                .compatible = "mediatek,mt6397",
                .data = &pmic_mt6397,
        }, {
+-- 
+1.7.10.4
+
index dfe1890ac559a278439051e686069d6ff034d6d1..aa5e3fb6caf6b14d70596eca49c61d675149e3f0 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -11,6 +11,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 @@
@@ -136,7 +138,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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;
  }
  
@@ -168,7 +170,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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;
@@ -176,7 +178,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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;
  }
  
@@ -201,7 +203,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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);
  
@@ -220,7 +222,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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[] = {
        {
@@ -230,3 +232,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
                .compatible = "mediatek,mt8135-pwrap",
                .data = &pwrap_mt8135,
        }, {
+-- 
+1.7.10.4
+
index 5fe1965f05ec5ef48c31bb5bb69c305e01e2699b..f38b522cc714ec3bd092e705566a264e4f6f6585 100644 (file)
@@ -1,8 +1,8 @@
-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>
@@ -11,6 +11,8 @@ Cc: devicetree@vger.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 @@
@@ -22,7 +24,7 @@ Cc: devicetree@vger.kernel.org
  - 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
@@ -48,3 +50,6 @@ Cc: devicetree@vger.kernel.org
  - codec
        Required properties:
                - compatible: "mediatek,mt6397-codec"
+-- 
+1.7.10.4
+
index ca4751cb574849e04e449e9fa9be9fc73f6bf0f6..c08541e2b08729a124d512a032e9c217d5cef2da 100644 (file)
@@ -1,7 +1,7 @@
-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.
@@ -13,9 +13,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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);
  
@@ -28,7 +30,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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;
  
@@ -39,7 +41,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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 */
@@ -50,7 +52,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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);
  
@@ -61,7 +63,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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);
  
@@ -72,7 +74,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        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;
@@ -84,6 +86,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        /*
         * 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 {
@@ -95,3 +99,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  };
  
  #endif /* __MFD_MT6397_CORE_H__ */
+-- 
+1.7.10.4
+
index dd3e404b0874225e8bac33216c86243c564e85c9..3d0cf28b2472f4ab4fbcc9f622e188d3ce412c33 100644 (file)
@@ -1,13 +1,15 @@
-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 @@
@@ -20,7 +22,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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;
@@ -98,3 +100,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
        return ret;
  }
+-- 
+1.7.10.4
+
index 64c2897ab3b94376c1f73d7ff5f4f389b6403cf9..cec58eacad479c0b6e3a067d69e89da17686f715 100644 (file)
@@ -1,7 +1,7 @@
-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>
 ---
@@ -12,6 +12,8 @@ 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 @@
@@ -29,7 +31,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  #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[] = {
        },
  };
  
@@ -43,7 +45,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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) {
@@ -59,7 +61,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        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" },
@@ -67,6 +69,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
        { }
  };
  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 @@
@@ -106,6 +111,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +};
 +
 +#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 @@
@@ -517,3 +525,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +#define MT6323_ACCDET_CON16       0x079A
 +
 +#endif /* __MFD_MT6323_REGISTERS_H__ */
+-- 
+1.7.10.4
+
index 899300468c61d133f175a8a6ce4e6db29158a65a..78cd0c132dfbd8cdd1845e50546ecc4c3bdded4c 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -10,6 +10,9 @@ 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 @@
@@ -252,3 +255,6 @@ Cc: devicetree@vger.kernel.org
 +                      };
 +              };
 +      };
+-- 
+1.7.10.4
+
index 53189a93de3a7f0bfd485861352371c48f033a9a..d914eceaee68cf985f62a20e7569c41417b64e05 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -18,9 +18,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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.
  
@@ -36,9 +38,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
@@ -46,6 +50,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 @@
@@ -481,6 +488,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +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 @@
@@ -536,3 +546,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +#define MT6323_MAX_REGULATOR  MT6323_ID_RG_MAX
 +
 +#endif /* __LINUX_REGULATOR_MT6323_H */
+-- 
+1.7.10.4
+
index d796c0587ed5313209b96980a4fd63cd94a95d23..b4da16b7c75f88f66886f3d23e98a7dca460409f 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -15,6 +15,9 @@ Cc: devicetree@vger.kernel.org
  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 @@
@@ -95,3 +98,6 @@ Cc: devicetree@vger.kernel.org
 +              };
 +      };
 +};
+-- 
+1.7.10.4
+
index c71f1537575610abbbe9def22a489a5d243ccd50..5efcc375202ecba8ec35ef652022846abdadc3a7 100644 (file)
@@ -1,7 +1,7 @@
-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
@@ -26,6 +26,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 @@
@@ -1836,6 +1839,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +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 @@
@@ -2260,3 +2266,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
 +
 +#endif /* MTK_ETH_H */
+-- 
+1.7.10.4
+
index f14b07adfb603daf3b0505f3928feaab10b4c239..cc4e671c0f946a3d49508c3faf3fa9b7f9fc75c1 100644 (file)
@@ -1,7 +1,7 @@
-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.
 
@@ -15,6 +15,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
@@ -25,6 +27,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
@@ -35,6 +39,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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 @@
@@ -55,6 +62,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +        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 @@
@@ -63,3 +73,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +#
 +
 +obj-$(CONFIG_NET_MEDIATEK_SOC)                        += mtk_eth_soc.o
+-- 
+1.7.10.4
+
index 7d7c72f4fc96e7b97ee3f8e9d0281d32c4f05965..d3f84abbd5a6e3d63d25d6d82ad456e14ca4642f 100644 (file)
@@ -1,7 +1,7 @@
-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.
 
@@ -11,9 +11,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  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
  
@@ -27,3 +29,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  MEDIATEK MT7601U WIRELESS LAN DRIVER
  M:    Jakub Kicinski <kubakici@wp.pl>
  L:    linux-wireless@vger.kernel.org
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0052-clk-dont-disable-unused-clocks.patch b/target/linux/mediatek/patches-4.4/0052-clk-dont-disable-unused-clocks.patch
new file mode 100644 (file)
index 0000000..277fbca
--- /dev/null
@@ -0,0 +1,26 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0052-mtd-nand-add-an-mtd_to_nand-helper.patch b/target/linux/mediatek/patches-4.4/0052-mtd-nand-add-an-mtd_to_nand-helper.patch
deleted file mode 100644 (file)
index 58a7f55..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-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
-  */
diff --git a/target/linux/mediatek/patches-4.4/0053-clk-mediatek-enable-critical-clocks.patch b/target/linux/mediatek/patches-4.4/0053-clk-mediatek-enable-critical-clocks.patch
new file mode 100644 (file)
index 0000000..f22f786
--- /dev/null
@@ -0,0 +1,74 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0053-mtd-nand-add-nand_to_mtd-helper.patch b/target/linux/mediatek/patches-4.4/0053-mtd-nand-add-nand_to_mtd-helper.patch
deleted file mode 100644 (file)
index 075cebe..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-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
-  */
diff --git a/target/linux/mediatek/patches-4.4/0054-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch b/target/linux/mediatek/patches-4.4/0054-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch
new file mode 100644 (file)
index 0000000..057f95e
--- /dev/null
@@ -0,0 +1,306 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0054-mtd-nand-add-helpers-to-access-priv.patch b/target/linux/mediatek/patches-4.4/0054-mtd-nand-add-helpers-to-access-priv.patch
deleted file mode 100644 (file)
index 3c25e20..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-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
-  */
diff --git a/target/linux/mediatek/patches-4.4/0055-cpufreq-mediatek-add-driver.patch b/target/linux/mediatek/patches-4.4/0055-cpufreq-mediatek-add-driver.patch
new file mode 100644 (file)
index 0000000..897b8c7
--- /dev/null
@@ -0,0 +1,443 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0055-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch b/target/linux/mediatek/patches-4.4/0055-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch
deleted file mode 100644 (file)
index b24e760..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0056-arm-mediatek-make-a7-timer-work-Signed-off-by-John-C.patch b/target/linux/mediatek/patches-4.4/0056-arm-mediatek-make-a7-timer-work-Signed-off-by-John-C.patch
new file mode 100644 (file)
index 0000000..18ecb80
--- /dev/null
@@ -0,0 +1,38 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0056-mtd-add-get-set-of_node-flash_node-helpers.patch b/target/linux/mediatek/patches-4.4/0056-mtd-add-get-set-of_node-flash_node-helpers.patch
deleted file mode 100644 (file)
index 3b618ac..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-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
diff --git a/target/linux/mediatek/patches-4.4/0057-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch b/target/linux/mediatek/patches-4.4/0057-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch
deleted file mode 100644 (file)
index eca087c..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-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>;
-+
-+              ...
-+      };
diff --git a/target/linux/mediatek/patches-4.4/0057-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch b/target/linux/mediatek/patches-4.4/0057-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch
new file mode 100644 (file)
index 0000000..a25385e
--- /dev/null
@@ -0,0 +1,32 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0058-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch b/target/linux/mediatek/patches-4.4/0058-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch
deleted file mode 100644 (file)
index 2d09f45..0000000
+++ /dev/null
@@ -1,1782 +0,0 @@
-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
-+
diff --git a/target/linux/mediatek/patches-4.4/0058-net-mediatek-unlock-on-error-in-mtk_tx_map.patch b/target/linux/mediatek/patches-4.4/0058-net-mediatek-unlock-on-error-in-mtk_tx_map.patch
new file mode 100644 (file)
index 0000000..9f7dfd5
--- /dev/null
@@ -0,0 +1,29 @@
+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(&eth->page_lock, flags);
++
+       return -ENOMEM;
+ }
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0059-mtd-nand-backport-fixes.patch b/target/linux/mediatek/patches-4.4/0059-mtd-nand-backport-fixes.patch
deleted file mode 100644 (file)
index be428fd..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0059-net-mediatek-use-dma_addr_t-correctly.patch b/target/linux/mediatek/patches-4.4/0059-net-mediatek-use-dma_addr_t-correctly.patch
new file mode 100644 (file)
index 0000000..f428d0b
--- /dev/null
@@ -0,0 +1,35 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0060-clk-dont-disable-unused-clocks.patch b/target/linux/mediatek/patches-4.4/0060-clk-dont-disable-unused-clocks.patch
deleted file mode 100644 (file)
index 51f63f9..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0060-net-mediatek-remove-incorrect-dma_mask-assignment.patch b/target/linux/mediatek/patches-4.4/0060-net-mediatek-remove-incorrect-dma_mask-assignment.patch
new file mode 100644 (file)
index 0000000..56c9809
--- /dev/null
@@ -0,0 +1,36 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0061-clk-mediatek-enable-critical-clocks.patch b/target/linux/mediatek/patches-4.4/0061-clk-mediatek-enable-critical-clocks.patch
deleted file mode 100644 (file)
index 8f06d47..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-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);
diff --git a/target/linux/mediatek/patches-4.4/0061-net-mediatek-check-device_reset-return-code.patch b/target/linux/mediatek/patches-4.4/0061-net-mediatek-check-device_reset-return-code.patch
new file mode 100644 (file)
index 0000000..f87a3e6
--- /dev/null
@@ -0,0 +1,38 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0062-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch b/target/linux/mediatek/patches-4.4/0062-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch
deleted file mode 100644 (file)
index f27ac80..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-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 */
diff --git a/target/linux/mediatek/patches-4.4/0062-net-mediatek-watchdog_timeo-was-not-set.patch b/target/linux/mediatek/patches-4.4/0062-net-mediatek-watchdog_timeo-was-not-set.patch
new file mode 100644 (file)
index 0000000..498393e
--- /dev/null
@@ -0,0 +1,28 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0063-cpufreq-mediatek-add-driver.patch b/target/linux/mediatek/patches-4.4/0063-cpufreq-mediatek-add-driver.patch
deleted file mode 100644 (file)
index c167925..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-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);
diff --git a/target/linux/mediatek/patches-4.4/0063-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch b/target/linux/mediatek/patches-4.4/0063-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch
new file mode 100644 (file)
index 0000000..f40a1a0
--- /dev/null
@@ -0,0 +1,30 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0064-arm-mediatek-make-a7-timer-work.patch b/target/linux/mediatek/patches-4.4/0064-arm-mediatek-make-a7-timer-work.patch
deleted file mode 100644 (file)
index 7fc5a39..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-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 */
diff --git a/target/linux/mediatek/patches-4.4/0064-net-mediatek-remove-superflous-reset-call.patch b/target/linux/mediatek/patches-4.4/0064-net-mediatek-remove-superflous-reset-call.patch
new file mode 100644 (file)
index 0000000..44769d0
--- /dev/null
@@ -0,0 +1,31 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0065-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch b/target/linux/mediatek/patches-4.4/0065-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch
deleted file mode 100644 (file)
index cff150f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-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,
diff --git a/target/linux/mediatek/patches-4.4/0065-net-mediatek-fix-stop-and-wakeup-of-queue.patch b/target/linux/mediatek/patches-4.4/0065-net-mediatek-fix-stop-and-wakeup-of-queue.patch
new file mode 100644 (file)
index 0000000..43b083d
--- /dev/null
@@ -0,0 +1,89 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0066-net-mediatek-fix-mtk_pending_work.patch b/target/linux/mediatek/patches-4.4/0066-net-mediatek-fix-mtk_pending_work.patch
new file mode 100644 (file)
index 0000000..1974195
--- /dev/null
@@ -0,0 +1,63 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0066-net-mediatek-unlock-on-error-in-mtk_tx_map.patch b/target/linux/mediatek/patches-4.4/0066-net-mediatek-unlock-on-error-in-mtk_tx_map.patch
deleted file mode 100644 (file)
index c82f061..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-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(&eth->page_lock, flags);
-+
-       return -ENOMEM;
- }
diff --git a/target/linux/mediatek/patches-4.4/0067-net-mediatek-fix-TX-locking.patch b/target/linux/mediatek/patches-4.4/0067-net-mediatek-fix-TX-locking.patch
new file mode 100644 (file)
index 0000000..da25259
--- /dev/null
@@ -0,0 +1,98 @@
+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(&eth->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(&eth->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(&eth->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 = &eth->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(&eth->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(&eth->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(&eth->page_lock, flags);
+       return NETDEV_TX_OK;
+ drop:
++      spin_unlock_irqrestore(&eth->page_lock, flags);
+       stats->tx_dropped++;
+       dev_kfree_skb(skb);
+       return NETDEV_TX_OK;
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0067-net-mediatek-use-dma_addr_t-correctly.patch b/target/linux/mediatek/patches-4.4/0067-net-mediatek-use-dma_addr_t-correctly.patch
deleted file mode 100644 (file)
index 5a0815d..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0068-net-mediatek-move-the-pending_work-struct-to-the-dev.patch b/target/linux/mediatek/patches-4.4/0068-net-mediatek-move-the-pending_work-struct-to-the-dev.patch
new file mode 100644 (file)
index 0000000..18f9783
--- /dev/null
@@ -0,0 +1,109 @@
+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(&eth->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(&eth->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(&eth->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
+
diff --git a/target/linux/mediatek/patches-4.4/0068-net-mediatek-remove-incorrect-dma_mask-assignment.patch b/target/linux/mediatek/patches-4.4/0068-net-mediatek-remove-incorrect-dma_mask-assignment.patch
deleted file mode 100644 (file)
index f2edef0..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-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);
diff --git a/target/linux/mediatek/patches-4.4/0069-net-mediatek-check-device_reset-return-code.patch b/target/linux/mediatek/patches-4.4/0069-net-mediatek-check-device_reset-return-code.patch
deleted file mode 100644 (file)
index fb2d579..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0069-net-mediatek-do-not-set-the-QID-field-in-the-TX-DMA-.patch b/target/linux/mediatek/patches-4.4/0069-net-mediatek-do-not-set-the-QID-field-in-the-TX-DMA-.patch
new file mode 100644 (file)
index 0000000..a596bb9
--- /dev/null
@@ -0,0 +1,37 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0070-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch b/target/linux/mediatek/patches-4.4/0070-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch
new file mode 100644 (file)
index 0000000..b2ceb87
--- /dev/null
@@ -0,0 +1,46 @@
+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 {
+                <&ethsys CLK_ETHSYS_GP2>,
+                <&ethsys 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 = <&ethsys MT2701_ETHSYS_ETH_RST>;
+       reset-names = "eth";
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0070-net-mediatek-watchdog_timeo-was-not-set.patch b/target/linux/mediatek/patches-4.4/0070-net-mediatek-watchdog_timeo-was-not-set.patch
deleted file mode 100644 (file)
index 9ab1622..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-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 &
diff --git a/target/linux/mediatek/patches-4.4/0071-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch b/target/linux/mediatek/patches-4.4/0071-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch
deleted file mode 100644 (file)
index d726876..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-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)
diff --git a/target/linux/mediatek/patches-4.4/0071-pwm-add-pwm-mediatek.patch b/target/linux/mediatek/patches-4.4/0071-pwm-add-pwm-mediatek.patch
new file mode 100644 (file)
index 0000000..9d7a27e
--- /dev/null
@@ -0,0 +1,351 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0072-mtd-backport-v4.7-0day-patches-from-Boris.patch b/target/linux/mediatek/patches-4.4/0072-mtd-backport-v4.7-0day-patches-from-Boris.patch
new file mode 100644 (file)
index 0000000..e89c372
--- /dev/null
@@ -0,0 +1,5621 @@
+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, &section, &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, &section,
++                                      &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, &section,
++                                      &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, &section, &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
+
diff --git a/target/linux/mediatek/patches-4.4/0072-net-mediatek-remove-superflous-reset-call.patch b/target/linux/mediatek/patches-4.4/0072-net-mediatek-remove-superflous-reset-call.patch
deleted file mode 100644 (file)
index 4eacc35..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0073-net-mediatek-fix-stop-and-wakeup-of-queue.patch b/target/linux/mediatek/patches-4.4/0073-net-mediatek-fix-stop-and-wakeup-of-queue.patch
deleted file mode 100644 (file)
index 3955ffd..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-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;
- }
diff --git a/target/linux/mediatek/patches-4.4/0073-of-mtd-prepare-helper-reading-NAND-ECC-algo-from-DT.patch b/target/linux/mediatek/patches-4.4/0073-of-mtd-prepare-helper-reading-NAND-ECC-algo-from-DT.patch
new file mode 100644 (file)
index 0000000..99eae20
--- /dev/null
@@ -0,0 +1,98 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0074-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch b/target/linux/mediatek/patches-4.4/0074-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch
new file mode 100644 (file)
index 0000000..27572c1
--- /dev/null
@@ -0,0 +1,185 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0074-net-mediatek-fix-mtk_pending_work.patch b/target/linux/mediatek/patches-4.4/0074-net-mediatek-fix-mtk_pending_work.patch
deleted file mode 100644 (file)
index abbb0df..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-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();
- }
diff --git a/target/linux/mediatek/patches-4.4/0075-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch b/target/linux/mediatek/patches-4.4/0075-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch
new file mode 100644 (file)
index 0000000..a9c776b
--- /dev/null
@@ -0,0 +1,2080 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0075-net-mediatek-fix-TX-locking.patch b/target/linux/mediatek/patches-4.4/0075-net-mediatek-fix-TX-locking.patch
deleted file mode 100644 (file)
index 4ace1fd..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-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(&eth->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(&eth->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(&eth->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 = &eth->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(&eth->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(&eth->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(&eth->page_lock, flags);
-       return NETDEV_TX_OK;
- drop:
-+      spin_unlock_irqrestore(&eth->page_lock, flags);
-       stats->tx_dropped++;
-       dev_kfree_skb(skb);
-       return NETDEV_TX_OK;
diff --git a/target/linux/mediatek/patches-4.4/0076-mtd-nand-add-power-domains-to-the-mediatek-driver.patch b/target/linux/mediatek/patches-4.4/0076-mtd-nand-add-power-domains-to-the-mediatek-driver.patch
new file mode 100644 (file)
index 0000000..d0bc250
--- /dev/null
@@ -0,0 +1,72 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0076-net-mediatek-move-the-pending_work-struct-to-the-dev.patch b/target/linux/mediatek/patches-4.4/0076-net-mediatek-move-the-pending_work-struct-to-the-dev.patch
deleted file mode 100644 (file)
index 4af1f0d..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-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(&eth->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(&eth->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(&eth->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 */
diff --git a/target/linux/mediatek/patches-4.4/0077-net-mediatek-do-not-set-the-QID-field-in-the-TX-DMA-.patch b/target/linux/mediatek/patches-4.4/0077-net-mediatek-do-not-set-the-QID-field-in-the-TX-DMA-.patch
deleted file mode 100644 (file)
index 2705f80..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0077-net-next-mediatek-use-mdiobus_free-in-favour-of-kfre.patch b/target/linux/mediatek/patches-4.4/0077-net-next-mediatek-use-mdiobus_free-in-favour-of-kfre.patch
new file mode 100644 (file)
index 0000000..a77ccee
--- /dev/null
@@ -0,0 +1,41 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0078-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch b/target/linux/mediatek/patches-4.4/0078-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch
deleted file mode 100644 (file)
index 7d17fc0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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 {
-                <&ethsys CLK_ETHSYS_GP2>,
-                <&ethsys 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 = <&ethsys MT2701_ETHSYS_ETH_RST>;
-       reset-names = "eth";
diff --git a/target/linux/mediatek/patches-4.4/0078-net-next-mediatek-fix-gigabit-and-flow-control-adver.patch b/target/linux/mediatek/patches-4.4/0078-net-next-mediatek-fix-gigabit-and-flow-control-adver.patch
new file mode 100644 (file)
index 0000000..244eb67
--- /dev/null
@@ -0,0 +1,76 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0079-net-next-mediatek-add-fixed-phy-support.patch b/target/linux/mediatek/patches-4.4/0079-net-next-mediatek-add-fixed-phy-support.patch
new file mode 100644 (file)
index 0000000..6ebc1d2
--- /dev/null
@@ -0,0 +1,43 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0079-net-next-mediatek-fix-BQL-support.patch b/target/linux/mediatek/patches-4.4/0079-net-next-mediatek-fix-BQL-support.patch
deleted file mode 100644 (file)
index 7b5b421..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-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 */
diff --git a/target/linux/mediatek/patches-4.4/0080-net-next-mediatek-fix-gigabit-and-flow-control-adver.patch b/target/linux/mediatek/patches-4.4/0080-net-next-mediatek-fix-gigabit-and-flow-control-adver.patch
deleted file mode 100644 (file)
index ef4dae2..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-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);
diff --git a/target/linux/mediatek/patches-4.4/0080-net-next-mediatek-properly-handle-RGMII-modes.patch b/target/linux/mediatek/patches-4.4/0080-net-next-mediatek-properly-handle-RGMII-modes.patch
new file mode 100644 (file)
index 0000000..a7dea77
--- /dev/null
@@ -0,0 +1,36 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0081-net-next-mediatek-add-fixed-phy-support.patch b/target/linux/mediatek/patches-4.4/0081-net-next-mediatek-add-fixed-phy-support.patch
deleted file mode 100644 (file)
index 0fe6186..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0081-net-next-mediatek-fix-DQL-support.patch b/target/linux/mediatek/patches-4.4/0081-net-next-mediatek-fix-DQL-support.patch
new file mode 100644 (file)
index 0000000..4d6a689
--- /dev/null
@@ -0,0 +1,97 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0082-net-next-mediatek-add-RX-delay-support.patch b/target/linux/mediatek/patches-4.4/0082-net-next-mediatek-add-RX-delay-support.patch
deleted file mode 100644 (file)
index 747cc7e..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-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;
diff --git a/target/linux/mediatek/patches-4.4/0082-net-next-mediatek-add-missing-return-code-check.patch b/target/linux/mediatek/patches-4.4/0082-net-next-mediatek-add-missing-return-code-check.patch
new file mode 100644 (file)
index 0000000..529290a
--- /dev/null
@@ -0,0 +1,31 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0083-net-next-mediatek-add-missing-return-code-check.patch b/target/linux/mediatek/patches-4.4/0083-net-next-mediatek-add-missing-return-code-check.patch
deleted file mode 100644 (file)
index 78780b8..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-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);
diff --git a/target/linux/mediatek/patches-4.4/0083-net-next-mediatek-fix-missing-free-of-scratch-memory.patch b/target/linux/mediatek/patches-4.4/0083-net-next-mediatek-fix-missing-free-of-scratch-memory.patch
new file mode 100644 (file)
index 0000000..b55950e
--- /dev/null
@@ -0,0 +1,99 @@
+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,
++                                             &eth->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
+
diff --git a/target/linux/mediatek/patches-4.4/0084-net-next-mediatek-fix-missing-free-of-scratch-memory.patch b/target/linux/mediatek/patches-4.4/0084-net-next-mediatek-fix-missing-free-of-scratch-memory.patch
deleted file mode 100644 (file)
index 15d113e..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-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,
-+                                             &eth->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;
diff --git a/target/linux/mediatek/patches-4.4/0084-net-next-mediatek-invalid-buffer-lookup-in-mtk_tx_ma.patch b/target/linux/mediatek/patches-4.4/0084-net-next-mediatek-invalid-buffer-lookup-in-mtk_tx_ma.patch
new file mode 100644 (file)
index 0000000..31e2a1f
--- /dev/null
@@ -0,0 +1,32 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0085-net-next-mediatek-dropped-rx-packets-are-not-being-c.patch b/target/linux/mediatek/patches-4.4/0085-net-next-mediatek-dropped-rx-packets-are-not-being-c.patch
new file mode 100644 (file)
index 0000000..6eeaa67
--- /dev/null
@@ -0,0 +1,39 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0085-net-next-mediatek-invalid-buffer-lookup-in-mtk_tx_ma.patch b/target/linux/mediatek/patches-4.4/0085-net-next-mediatek-invalid-buffer-lookup-in-mtk_tx_ma.patch
deleted file mode 100644 (file)
index 79740c4..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-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);
diff --git a/target/linux/mediatek/patches-4.4/0086-net-next-mediatek-add-next-data-pointer-coherency-pr.patch b/target/linux/mediatek/patches-4.4/0086-net-next-mediatek-add-next-data-pointer-coherency-pr.patch
new file mode 100644 (file)
index 0000000..0d2fc46
--- /dev/null
@@ -0,0 +1,46 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0086-net-next-mediatek-dropped-rx-packets-are-not-being-c.patch b/target/linux/mediatek/patches-4.4/0086-net-next-mediatek-dropped-rx-packets-are-not-being-c.patch
deleted file mode 100644 (file)
index ef49d0f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-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);
diff --git a/target/linux/mediatek/patches-4.4/0087-net-next-mediatek-add-IRQ-locking.patch b/target/linux/mediatek/patches-4.4/0087-net-next-mediatek-add-IRQ-locking.patch
deleted file mode 100644 (file)
index 4930726..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-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(&eth->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(&eth->irq_lock, flags);
- }
- static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
- {
-+      unsigned long flags;
-       u32 val;
-+      spin_lock_irqsave(&eth->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(&eth->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(&eth->page_lock);
-+      spin_lock_init(&eth->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];
diff --git a/target/linux/mediatek/patches-4.4/0087-net-next-mediatek-disable-all-interrupts-during-prob.patch b/target/linux/mediatek/patches-4.4/0087-net-next-mediatek-disable-all-interrupts-during-prob.patch
new file mode 100644 (file)
index 0000000..68b578a
--- /dev/null
@@ -0,0 +1,31 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0088-net-next-mediatek-add-support-for-IRQ-grouping.patch b/target/linux/mediatek/patches-4.4/0088-net-next-mediatek-add-support-for-IRQ-grouping.patch
deleted file mode 100644 (file)
index 698f7de..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-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 = &eth->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 = &eth->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(&eth->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(&eth->rx_napi)))
-                       __napi_schedule(&eth->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(&eth->tx_napi)))
-+                      __napi_schedule(&eth->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(&eth->tx_napi);
-               napi_enable(&eth->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(&eth->tx_napi);
-       napi_disable(&eth->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(&eth->dummy_dev);
--      netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_poll,
-+      netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx,
-+                     MTK_NAPI_WEIGHT);
-+      netif_napi_add(&eth->dummy_dev, &eth->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(&eth->tx_napi);
-       netif_napi_del(&eth->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
diff --git a/target/linux/mediatek/patches-4.4/0088-net-next-mediatek-fix-threshold-value.patch b/target/linux/mediatek/patches-4.4/0088-net-next-mediatek-fix-threshold-value.patch
new file mode 100644 (file)
index 0000000..2a8fc33
--- /dev/null
@@ -0,0 +1,35 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0089-net-mediatek-add-gsw-mt7530-driver.patch b/target/linux/mediatek/patches-4.4/0089-net-mediatek-add-gsw-mt7530-driver.patch
deleted file mode 100644 (file)
index 8ddfb8d..0000000
+++ /dev/null
@@ -1,2359 +0,0 @@
-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(&eth->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 */
diff --git a/target/linux/mediatek/patches-4.4/0089-net-next-mediatek-increase-watchdog_timeo.patch b/target/linux/mediatek/patches-4.4/0089-net-next-mediatek-increase-watchdog_timeo.patch
new file mode 100644 (file)
index 0000000..9632556
--- /dev/null
@@ -0,0 +1,31 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0090-net-mediatek-v4.4-backports.patch b/target/linux/mediatek/patches-4.4/0090-net-mediatek-v4.4-backports.patch
deleted file mode 100644 (file)
index 8dcba36..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-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(&eth->rx_napi)))
-+              if (likely(napi_schedule_prep(&eth->rx_napi))) {
-+                      mtk_irq_disable(eth, MTK_RX_DONE_INT);
-                       __napi_schedule(&eth->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(&eth->tx_napi)))
-+              if (likely(napi_schedule_prep(&eth->tx_napi))) {
-+                      mtk_irq_disable(eth, MTK_TX_DONE_INT);
-                       __napi_schedule(&eth->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);
diff --git a/target/linux/mediatek/patches-4.4/0090-net-next-mediatek-fix-off-by-one-in-the-TX-ring-allo.patch b/target/linux/mediatek/patches-4.4/0090-net-next-mediatek-fix-off-by-one-in-the-TX-ring-allo.patch
new file mode 100644 (file)
index 0000000..7a58bfb
--- /dev/null
@@ -0,0 +1,41 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0091-net-next-mediatek-WIP.patch b/target/linux/mediatek/patches-4.4/0091-net-next-mediatek-WIP.patch
deleted file mode 100644 (file)
index 4bee845..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-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(&eth->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(&eth->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(&eth->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(&eth->rx_napi))) {
--                      mtk_irq_disable(eth, MTK_RX_DONE_INT);
--                      __napi_schedule(&eth->rx_napi);
--              }
-+      if (likely(napi_schedule_prep(&eth->rx_napi))) {
-+              __napi_schedule(&eth->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(&eth->tx_napi))) {
--                      mtk_irq_disable(eth, MTK_TX_DONE_INT);
--                      __napi_schedule(&eth->tx_napi);
--              }
-+      if (likely(napi_schedule_prep(&eth->tx_napi))) {
-+              __napi_schedule(&eth->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)
diff --git a/target/linux/mediatek/patches-4.4/0091-net-next-mediatek-only-wake-the-queue-if-it-is-stopp.patch b/target/linux/mediatek/patches-4.4/0091-net-next-mediatek-only-wake-the-queue-if-it-is-stopp.patch
new file mode 100644 (file)
index 0000000..c292ff9
--- /dev/null
@@ -0,0 +1,53 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0092-net-next-mediatek-remove-superfluous-queue-wake-up-c.patch b/target/linux/mediatek/patches-4.4/0092-net-next-mediatek-remove-superfluous-queue-wake-up-c.patch
new file mode 100644 (file)
index 0000000..c330da9
--- /dev/null
@@ -0,0 +1,41 @@
+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(&eth->page_lock, flags);
+       return NETDEV_TX_OK;
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0093-net-next-mediatek-remove-superfluous-register-reads.patch b/target/linux/mediatek/patches-4.4/0093-net-next-mediatek-remove-superfluous-register-reads.patch
new file mode 100644 (file)
index 0000000..a83d713
--- /dev/null
@@ -0,0 +1,42 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0094-net-next-mediatek-don-t-use-intermediate-variables-t.patch b/target/linux/mediatek/patches-4.4/0094-net-next-mediatek-don-t-use-intermediate-variables-t.patch
new file mode 100644 (file)
index 0000000..c45d7fe
--- /dev/null
@@ -0,0 +1,87 @@
+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 = &eth->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
+
diff --git a/target/linux/mediatek/patches-4.4/0095-net-next-mediatek-add-IRQ-locking.patch b/target/linux/mediatek/patches-4.4/0095-net-next-mediatek-add-IRQ-locking.patch
new file mode 100644 (file)
index 0000000..ff21c09
--- /dev/null
@@ -0,0 +1,71 @@
+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(&eth->irq_lock, flags);
+       val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+       mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
++      spin_unlock_irqrestore(&eth->irq_lock, flags);
+ }
+ static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
+ {
++      unsigned long flags;
+       u32 val;
++      spin_lock_irqsave(&eth->irq_lock, flags);
+       val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+       mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
++      spin_unlock_irqrestore(&eth->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(&eth->page_lock);
++      spin_lock_init(&eth->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
+
diff --git a/target/linux/mediatek/patches-4.4/0096-net-next-mediatek-add-support-for-IRQ-grouping.patch b/target/linux/mediatek/patches-4.4/0096-net-next-mediatek-add-support-for-IRQ-grouping.patch
new file mode 100644 (file)
index 0000000..01fe771
--- /dev/null
@@ -0,0 +1,368 @@
+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 = &eth->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(&eth->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(&eth->rx_napi))) {
++              __napi_schedule(&eth->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(&eth->rx_napi)))
+-                      __napi_schedule(&eth->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(&eth->tx_napi))) {
++              __napi_schedule(&eth->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(&eth->tx_napi);
+               napi_enable(&eth->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(&eth->tx_napi);
+       napi_disable(&eth->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(&eth->dummy_dev);
+-      netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_poll,
++      netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx,
++                     MTK_NAPI_WEIGHT);
++      netif_napi_add(&eth->dummy_dev, &eth->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(&eth->tx_napi);
+       netif_napi_del(&eth->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
+
diff --git a/target/linux/mediatek/patches-4.4/0097-net-next-mediatek-change-my-email-address.patch b/target/linux/mediatek/patches-4.4/0097-net-next-mediatek-change-my-email-address.patch
new file mode 100644 (file)
index 0000000..612fccb
--- /dev/null
@@ -0,0 +1,35 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0098-net-next-mediatek-only-trigger-the-tx-watchdog-reset.patch b/target/linux/mediatek/patches-4.4/0098-net-next-mediatek-only-trigger-the-tx-watchdog-reset.patch
new file mode 100644 (file)
index 0000000..bbcbc67
--- /dev/null
@@ -0,0 +1,64 @@
+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(&eth->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(&eth->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
+
diff --git a/target/linux/mediatek/patches-4.4/0099-MAINTAINERS-change-my-email-address.patch b/target/linux/mediatek/patches-4.4/0099-MAINTAINERS-change-my-email-address.patch
new file mode 100644 (file)
index 0000000..abe7f5b
--- /dev/null
@@ -0,0 +1,28 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0100-MAINTAINERS-add-Sean-as-mediatek-ethernet-maintainer.patch b/target/linux/mediatek/patches-4.4/0100-MAINTAINERS-add-Sean-as-mediatek-ethernet-maintainer.patch
new file mode 100644 (file)
index 0000000..d4c0b71
--- /dev/null
@@ -0,0 +1,30 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0100-block2mtd.patch b/target/linux/mediatek/patches-4.4/0100-block2mtd.patch
deleted file mode 100644 (file)
index 395884b..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
---- 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;
-       }
diff --git a/target/linux/mediatek/patches-4.4/0101-net-mediatek-add-gsw-mt7530-driver.patch b/target/linux/mediatek/patches-4.4/0101-net-mediatek-add-gsw-mt7530-driver.patch
new file mode 100644 (file)
index 0000000..79b5f90
--- /dev/null
@@ -0,0 +1,2381 @@
+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(&eth->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
+
diff --git a/target/linux/mediatek/patches-4.4/0102-net-mediatek-v4.4-backports.patch b/target/linux/mediatek/patches-4.4/0102-net-mediatek-v4.4-backports.patch
new file mode 100644 (file)
index 0000000..a6b423e
--- /dev/null
@@ -0,0 +1,51 @@
+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
+
diff --git a/target/linux/mediatek/patches-4.4/0201-block2mtd.patch b/target/linux/mediatek/patches-4.4/0201-block2mtd.patch
new file mode 100644 (file)
index 0000000..395884b
--- /dev/null
@@ -0,0 +1,32 @@
+--- 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;
+       }