aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mediatek/patches-4.9
diff options
context:
space:
mode:
authorJohn Crispin <john@phrozen.org>2017-02-16 09:53:03 +0100
committerJohn Crispin <john@phrozen.org>2017-02-16 09:53:30 +0100
commit53f5d59fa17049d94a3992d1067ded1fa90f61f8 (patch)
tree5596104b74bfe2a96f1abdb2303314a40c74322b /target/linux/mediatek/patches-4.9
parentf885edef5a3bfcddcfe85732ee65c1d475c4a8f6 (diff)
downloadupstream-53f5d59fa17049d94a3992d1067ded1fa90f61f8.tar.gz
upstream-53f5d59fa17049d94a3992d1067ded1fa90f61f8.tar.bz2
upstream-53f5d59fa17049d94a3992d1067ded1fa90f61f8.zip
mediatek: bump to v4.9
Signed-off-by: John Crispin <john@phrozen.org>
Diffstat (limited to 'target/linux/mediatek/patches-4.9')
-rw-r--r--target/linux/mediatek/patches-4.9/0000-pinctrl-esw.patch12
-rw-r--r--target/linux/mediatek/patches-4.9/0001-NET-multi-phy-support.patch53
-rw-r--r--target/linux/mediatek/patches-4.9/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch44
-rw-r--r--target/linux/mediatek/patches-4.9/0009-clk-mediatek-Add-MT2701-clock-support.patch1431
-rw-r--r--target/linux/mediatek/patches-4.9/0011-reset-mediatek-mt2701-reset-driver.patch36
-rw-r--r--target/linux/mediatek/patches-4.9/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch29
-rw-r--r--target/linux/mediatek/patches-4.9/0017-clk-add-hifsys-reset.patch31
-rw-r--r--target/linux/mediatek/patches-4.9/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch154
-rw-r--r--target/linux/mediatek/patches-4.9/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch698
-rw-r--r--target/linux/mediatek/patches-4.9/0026-scpsys-various-fixes.patch22
-rw-r--r--target/linux/mediatek/patches-4.9/0052-clk-dont-disable-unused-clocks.patch21
-rw-r--r--target/linux/mediatek/patches-4.9/0053-clk-mediatek-enable-critical-clocks.patch69
-rw-r--r--target/linux/mediatek/patches-4.9/0054-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch287
-rw-r--r--target/linux/mediatek/patches-4.9/0055-cpufreq-mediatek-add-driver.patch433
-rw-r--r--target/linux/mediatek/patches-4.9/0071-pwm-add-pwm-mediatek.patch304
-rw-r--r--target/linux/mediatek/patches-4.9/0101-net-mediatek-add-gsw-mt7530-driver.patch2322
-rw-r--r--target/linux/mediatek/patches-4.9/0103-nand_fixes.patch22
-rw-r--r--target/linux/mediatek/patches-4.9/0200-devicetree.patch11
-rw-r--r--target/linux/mediatek/patches-4.9/0201-block2mtd.patch32
19 files changed, 6011 insertions, 0 deletions
diff --git a/target/linux/mediatek/patches-4.9/0000-pinctrl-esw.patch b/target/linux/mediatek/patches-4.9/0000-pinctrl-esw.patch
new file mode 100644
index 0000000000..2fedae701b
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0000-pinctrl-esw.patch
@@ -0,0 +1,12 @@
+--- a/include/dt-bindings/pinctrl/mt7623-pinfunc.h
++++ b/include/dt-bindings/pinctrl/mt7623-pinfunc.h
+@@ -505,6 +505,9 @@
+ #define MT7623_PIN_272_G2_RXD3_FUNC_GPIO272 (MTK_PIN_NO(272) | 0)
+ #define MT7623_PIN_272_G2_RXD3_FUNC_G2_RXD3 (MTK_PIN_NO(272) | 1)
+
++#define MT7623_PIN_273_ESW_INT_FUNC_GPIO273 (MTK_PIN_NO(273) | 0)
++#define MT7623_PIN_273_ESW_INT_FUNC_ESW_INT (MTK_PIN_NO(273) | 1)
++
+ #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)
+
diff --git a/target/linux/mediatek/patches-4.9/0001-NET-multi-phy-support.patch b/target/linux/mediatek/patches-4.9/0001-NET-multi-phy-support.patch
new file mode 100644
index 0000000000..b60eac0efc
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0001-NET-multi-phy-support.patch
@@ -0,0 +1,53 @@
+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 001/102] NET: multi phy support
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/phy/phy.c | 9 ++++++---
+ include/linux/phy.h | 1 +
+ 2 files changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -890,7 +890,8 @@ void phy_state_machine(struct work_struc
+ /* If the link is down, give up on negotiation for now */
+ if (!phydev->link) {
+ phydev->state = PHY_NOLINK;
+- netif_carrier_off(phydev->attached_dev);
++ if (!phydev->no_auto_carrier_off)
++ netif_carrier_off(phydev->attached_dev);
+ phydev->adjust_link(phydev->attached_dev);
+ break;
+ }
+@@ -973,7 +974,8 @@ void phy_state_machine(struct work_struc
+ netif_carrier_on(phydev->attached_dev);
+ } else {
+ phydev->state = PHY_NOLINK;
+- netif_carrier_off(phydev->attached_dev);
++ if (!phydev->no_auto_carrier_off)
++ netif_carrier_off(phydev->attached_dev);
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+@@ -985,7 +987,8 @@ void phy_state_machine(struct work_struc
+ case PHY_HALTED:
+ if (phydev->link) {
+ phydev->link = 0;
+- netif_carrier_off(phydev->attached_dev);
++ if (!phydev->no_auto_carrier_off)
++ netif_carrier_off(phydev->attached_dev);
+ phydev->adjust_link(phydev->attached_dev);
+ do_suspend = true;
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -377,6 +377,7 @@ struct phy_device {
+ bool is_pseudo_fixed_link;
+ bool has_fixups;
+ bool suspended;
++ bool no_auto_carrier_off;
+
+ enum phy_state state;
+
diff --git a/target/linux/mediatek/patches-4.9/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch b/target/linux/mediatek/patches-4.9/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch
new file mode 100644
index 0000000000..132d6c89c8
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch
@@ -0,0 +1,44 @@
+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 004/102] soc: mediatek: Add MT2701 power dt-bindings
+
+Add power dt-bindings for MT2701.
+
+Signed-off-by: Shunli Wang <shunli.wang@mediatek.com>
+Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
+---
+ include/dt-bindings/power/mt2701-power.h | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+ create mode 100644 include/dt-bindings/power/mt2701-power.h
+
+--- /dev/null
++++ b/include/dt-bindings/power/mt2701-power.h
+@@ -0,0 +1,27 @@
++/*
++ * Copyright (C) 2015 MediaTek Inc.
++ *
++ * 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 _DT_BINDINGS_POWER_MT2701_POWER_H
++#define _DT_BINDINGS_POWER_MT2701_POWER_H
++
++#define MT2701_POWER_DOMAIN_CONN 0
++#define MT2701_POWER_DOMAIN_DISP 1
++#define MT2701_POWER_DOMAIN_MFG 2
++#define MT2701_POWER_DOMAIN_VDEC 3
++#define MT2701_POWER_DOMAIN_ISP 4
++#define MT2701_POWER_DOMAIN_BDP 5
++#define MT2701_POWER_DOMAIN_ETH 6
++#define MT2701_POWER_DOMAIN_HIF 7
++#define MT2701_POWER_DOMAIN_IFR_MSC 8
++
++#endif /* _DT_BINDINGS_POWER_MT2701_POWER_H */
diff --git a/target/linux/mediatek/patches-4.9/0009-clk-mediatek-Add-MT2701-clock-support.patch b/target/linux/mediatek/patches-4.9/0009-clk-mediatek-Add-MT2701-clock-support.patch
new file mode 100644
index 0000000000..552b46e565
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0009-clk-mediatek-Add-MT2701-clock-support.patch
@@ -0,0 +1,1431 @@
+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 009/102] clk: mediatek: Add MT2701 clock support
+
+Add MT2701 clock support, include topckgen, apmixedsys,
+infracfg, pericfg and subsystem clocks.
+
+Signed-off-by: Shunli Wang <shunli.wang@mediatek.com>
+Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
+---
+ drivers/clk/mediatek/Kconfig | 8 +
+ drivers/clk/mediatek/Makefile | 1 +
+ drivers/clk/mediatek/clk-gate.c | 56 ++
+ drivers/clk/mediatek/clk-gate.h | 2 +
+ drivers/clk/mediatek/clk-mt2701.c | 1210 +++++++++++++++++++++++++++++++++++++
+ drivers/clk/mediatek/clk-mtk.c | 25 +
+ drivers/clk/mediatek/clk-mtk.h | 35 +-
+ 7 files changed, 1334 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/clk/mediatek/clk-mt2701.c
+
+--- a/drivers/clk/mediatek/Kconfig
++++ b/drivers/clk/mediatek/Kconfig
+@@ -6,6 +6,14 @@
+ ---help---
+ Mediatek SoCs' clock support.
+
++config COMMON_CLK_MT2701
++ bool "Clock driver for Mediatek MT2701 and MT7623"
++ depends on COMMON_CLK
++ select COMMON_CLK_MEDIATEK
++ default ARCH_MEDIATEK
++ ---help---
++ This driver supports Mediatek MT2701 and MT7623 clocks.
++
+ config COMMON_CLK_MT8135
+ bool "Clock driver for Mediatek MT8135"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+--- a/drivers/clk/mediatek/Makefile
++++ b/drivers/clk/mediatek/Makefile
+@@ -1,4 +1,5 @@
+ obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
+ obj-$(CONFIG_RESET_CONTROLLER) += reset.o
++obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
+ obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
+ obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
+--- a/drivers/clk/mediatek/clk-gate.c
++++ b/drivers/clk/mediatek/clk-gate.c
+@@ -61,6 +61,26 @@
+ regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
+ }
+
++static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
++{
++ struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
++ u32 val;
++
++ regmap_read(cg->regmap, cg->sta_ofs, &val);
++ val |= BIT(cg->bit);
++ regmap_write(cg->regmap, cg->sta_ofs, val);
++}
++
++static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
++{
++ struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
++ u32 val;
++
++ regmap_read(cg->regmap, cg->sta_ofs, &val);
++ val &= ~(BIT(cg->bit));
++ regmap_write(cg->regmap, cg->sta_ofs, val);
++}
++
+ static int mtk_cg_enable(struct clk_hw *hw)
+ {
+ mtk_cg_clr_bit(hw);
+@@ -85,6 +105,30 @@
+ mtk_cg_clr_bit(hw);
+ }
+
++static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
++{
++ mtk_cg_clr_bit_no_setclr(hw);
++
++ return 0;
++}
++
++static void mtk_cg_disable_no_setclr(struct clk_hw *hw)
++{
++ mtk_cg_set_bit_no_setclr(hw);
++}
++
++static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw)
++{
++ mtk_cg_set_bit_no_setclr(hw);
++
++ return 0;
++}
++
++static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
++{
++ mtk_cg_clr_bit_no_setclr(hw);
++}
++
+ const struct clk_ops mtk_clk_gate_ops_setclr = {
+ .is_enabled = mtk_cg_bit_is_cleared,
+ .enable = mtk_cg_enable,
+@@ -97,6 +141,18 @@
+ .disable = mtk_cg_disable_inv,
+ };
+
++const struct clk_ops mtk_clk_gate_ops_no_setclr = {
++ .is_enabled = mtk_cg_bit_is_cleared,
++ .enable = mtk_cg_enable_no_setclr,
++ .disable = mtk_cg_disable_no_setclr,
++};
++
++const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
++ .is_enabled = mtk_cg_bit_is_set,
++ .enable = mtk_cg_enable_inv_no_setclr,
++ .disable = mtk_cg_disable_inv_no_setclr,
++};
++
+ struct clk *mtk_clk_register_gate(
+ const char *name,
+ const char *parent_name,
+--- a/drivers/clk/mediatek/clk-gate.h
++++ b/drivers/clk/mediatek/clk-gate.h
+@@ -36,6 +36,8 @@
+
+ extern const struct clk_ops mtk_clk_gate_ops_setclr;
+ extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
++extern const struct clk_ops mtk_clk_gate_ops_no_setclr;
++extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv;
+
+ struct clk *mtk_clk_register_gate(
+ const char *name,
+--- /dev/null
++++ b/drivers/clk/mediatek/clk-mt2701.c
+@@ -0,0 +1,1210 @@
++/*
++ * Copyright (c) 2014 MediaTek Inc.
++ * Author: Shunli Wang <shunli.wang@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/of.h>
++#include <linux/of_address.h>
++
++#include "clk-mtk.h"
++#include "clk-gate.h"
++
++#include <dt-bindings/clock/mt2701-clk.h>
++
++static DEFINE_SPINLOCK(lock);
++
++static const struct mtk_fixed_clk top_fixed_clks[] __initconst = {
++ FIXED_CLK(CLK_TOP_DPI, "dpi_ck", "clk26m", 108 * MHZ),
++ FIXED_CLK(CLK_TOP_DMPLL, "dmpll_ck", "clk26m", 400 * MHZ),
++ FIXED_CLK(CLK_TOP_VENCPLL, "vencpll_ck", "clk26m", 295750000),
++ FIXED_CLK(CLK_TOP_HDMI_0_PIX340M, "hdmi_0_pix340m", "clk26m", 340 * MHZ),
++ FIXED_CLK(CLK_TOP_HDMI_0_DEEP340M, "hdmi_0_deep340m", "clk26m", 340 * MHZ),
++ FIXED_CLK(CLK_TOP_HDMI_0_PLL340M, "hdmi_0_pll340m", "clk26m", 340 * MHZ),
++ FIXED_CLK(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_dig_cts", "clk26m", 300 * MHZ),
++ FIXED_CLK(CLK_TOP_HADDS2_FB, "hadds2_fbclk", "clk26m", 27 * MHZ),
++ FIXED_CLK(CLK_TOP_WBG_DIG_416M, "wbg_dig_ck_416m", "clk26m", 416 * MHZ),
++};
++
++static const struct mtk_fixed_factor top_fixed_divs[] __initconst = {
++ FACTOR(CLK_TOP_SYSPLL, "syspll_ck", "mainpll", 1, 1),
++ FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2),
++ FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3),
++ FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5),
++ FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1, 7),
++ FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1, 2),
++ FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1, 4),
++ FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1, 8),
++ FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1, 16),
++ FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1, 2),
++ FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1, 4),
++ FACTOR(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "syspll_d3", 1, 8),
++ FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1, 2),
++ FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1, 4),
++ FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1, 2),
++ FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1, 4),
++
++ FACTOR(CLK_TOP_UNIVPLL, "univpll_ck", "univpll", 1, 1),
++ FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
++ FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
++ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
++ FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
++ FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll", 1, 26),
++ FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univpll", 1, 52),
++ FACTOR(CLK_TOP_UNIVPLL_D108, "univpll_d108", "univpll", 1, 108),
++ FACTOR(CLK_TOP_USB_PHY48M, "USB_PHY48M_CK", "univpll", 1, 26),
++ FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1, 2),
++ FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1, 4),
++ FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_d2", 1, 8),
++ FACTOR(CLK_TOP_8BDAC, "8bdac_ck", "univpll_d2", 1, 1),
++ FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_d3", 1, 2),
++ FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_d3", 1, 4),
++ FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_d3", 1, 8),
++ FACTOR(CLK_TOP_UNIVPLL2_D16, "univpll2_d16", "univpll_d3", 1, 16),
++ FACTOR(CLK_TOP_UNIVPLL2_D32, "univpll2_d32", "univpll_d3", 1, 32),
++ FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1, 2),
++ FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1, 4),
++ FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univpll_d5", 1, 8),
++
++ FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
++ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
++ FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
++ FACTOR(CLK_TOP_MSDCPLL_D8, "msdcpll_d8", "msdcpll", 1, 8),
++
++ FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
++ FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
++
++ FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "dmpll_ck", 1, 2),
++ FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "dmpll_ck", 1, 4),
++ FACTOR(CLK_TOP_DMPLL_X2, "dmpll_x2", "dmpll_ck", 1, 1),
++
++ FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll", 1, 1),
++ FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll", 1, 2),
++ FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll", 1, 4),
++
++ FACTOR(CLK_TOP_VDECPLL, "vdecpll_ck", "vdecpll", 1, 1),
++ FACTOR(CLK_TOP_TVD2PLL, "tvd2pll_ck", "tvd2pll", 1, 1),
++ FACTOR(CLK_TOP_TVD2PLL_D2, "tvd2pll_d2", "tvd2pll", 1, 2),
++
++ FACTOR(CLK_TOP_MIPIPLL, "mipipll", "dpi_ck", 1, 1),
++ FACTOR(CLK_TOP_MIPIPLL_D2, "mipipll_d2", "dpi_ck", 1, 2),
++ FACTOR(CLK_TOP_MIPIPLL_D4, "mipipll_d4", "dpi_ck", 1, 4),
++
++ FACTOR(CLK_TOP_HDMIPLL, "hdmipll_ck", "hdmitx_dig_cts", 1, 1),
++ FACTOR(CLK_TOP_HDMIPLL_D2, "hdmipll_d2", "hdmitx_dig_cts", 1, 2),
++ FACTOR(CLK_TOP_HDMIPLL_D3, "hdmipll_d3", "hdmitx_dig_cts", 1, 3),
++
++ FACTOR(CLK_TOP_ARMPLL_1P3G, "armpll_1p3g_ck", "armpll", 1, 1),
++
++ FACTOR(CLK_TOP_AUDPLL, "audpll", "audpll_sel", 1, 1),
++ FACTOR(CLK_TOP_AUDPLL_D4, "audpll_d4", "audpll_sel", 1, 4),
++ FACTOR(CLK_TOP_AUDPLL_D8, "audpll_d8", "audpll_sel", 1, 8),
++ FACTOR(CLK_TOP_AUDPLL_D16, "audpll_d16", "audpll_sel", 1, 16),
++ FACTOR(CLK_TOP_AUDPLL_D24, "audpll_d24", "audpll_sel", 1, 24),
++
++ FACTOR(CLK_TOP_AUD1PLL_98M, "aud1pll_98m_ck", "aud1pll", 1, 3),
++ FACTOR(CLK_TOP_AUD2PLL_90M, "aud2pll_90m_ck", "aud2pll", 1, 3),
++ FACTOR(CLK_TOP_HADDS2PLL_98M, "hadds2pll_98m", "hadds2pll", 1, 3),
++ FACTOR(CLK_TOP_HADDS2PLL_294M, "hadds2pll_294m", "hadds2pll", 1, 1),
++ FACTOR(CLK_TOP_ETHPLL_500M, "ethpll_500m_ck", "ethpll", 1, 1),
++ FACTOR(CLK_TOP_CLK26M_D8, "clk26m_d8", "clk26m", 1, 8),
++ FACTOR(CLK_TOP_32K_INTERNAL, "32k_internal", "clk26m", 1, 793),
++ FACTOR(CLK_TOP_32K_EXTERNAL, "32k_external", "rtc32k", 1, 1),
++};
++
++static const char * const axi_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d2",
++ "syspll_d5",
++ "syspll1_d4",
++ "univpll_d5",
++ "univpll2_d2",
++ "mmpll_d2",
++ "dmpll_d2"
++};
++
++static const char * const mem_parents[] __initconst = {
++ "clk26m",
++ "dmpll_ck"
++};
++
++static const char * const ddrphycfg_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d8"
++};
++
++static const char * const mm_parents[] __initconst = {
++ "clk26m",
++ "vencpll_ck",
++ "syspll1_d2",
++ "syspll1_d4",
++ "univpll_d5",
++ "univpll1_d2",
++ "univpll2_d2",
++ "dmpll_ck"
++};
++
++static const char * const pwm_parents[] __initconst = {
++ "clk26m",
++ "univpll2_d4",
++ "univpll3_d2",
++ "univpll1_d4",
++};
++
++static const char * const vdec_parents[] __initconst = {
++ "clk26m",
++ "vdecpll_ck",
++ "syspll_d5",
++ "syspll1_d4",
++ "univpll_d5",
++ "univpll2_d2",
++ "vencpll_ck",
++ "msdcpll_d2",
++ "mmpll_d2"
++};
++
++static const char * const mfg_parents[] __initconst = {
++ "clk26m",
++ "mmpll_ck",
++ "dmpll_x2_ck",
++ "msdcpll_ck",
++ "clk26m",
++ "syspll_d3",
++ "univpll_d3",
++ "univpll1_d2"
++};
++
++static const char * const camtg_parents[] __initconst = {
++ "clk26m",
++ "univpll_d26",
++ "univpll2_d2",
++ "syspll3_d2",
++ "syspll3_d4",
++ "msdcpll_d2",
++ "mmpll_d2"
++};
++
++static const char * const uart_parents[] __initconst = {
++ "clk26m",
++ "univpll2_d8"
++};
++
++static const char * const spi_parents[] __initconst = {
++ "clk26m",
++ "syspll3_d2",
++ "syspll4_d2",
++ "univpll2_d4",
++ "univpll1_d8"
++};
++
++static const char * const usb20_parents[] __initconst = {
++ "clk26m",
++ "univpll1_d8",
++ "univpll3_d4"
++};
++
++static const char * const msdc30_parents[] __initconst = {
++ "clk26m",
++ "msdcpll_d2",
++ "syspll2_d2",
++ "syspll1_d4",
++ "univpll1_d4",
++ "univpll2_d4"
++};
++
++static const char * const audio_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d16"
++};
++
++static const char * const aud_intbus_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d4",
++ "syspll3_d2",
++ "syspll4_d2",
++ "univpll3_d2",
++ "univpll2_d4"
++};
++
++static const char * const pmicspi_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d8",
++ "syspll2_d4",
++ "syspll4_d2",
++ "syspll3_d4",
++ "syspll2_d8",
++ "syspll1_d16",
++ "univpll3_d4",
++ "univpll_d26",
++ "dmpll_d2",
++ "dmpll_d4"
++};
++
++static const char * const scp_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d8",
++ "dmpll_d2",
++ "dmpll_d4"
++};
++
++static const char * const dpi0_parents[] __initconst = {
++ "clk26m",
++ "mipipll",
++ "mipipll_d2",
++ "mipipll_d4",
++ "clk26m",
++ "tvdpll_ck",
++ "tvdpll_d2",
++ "tvdpll_d4"
++};
++
++static const char * const dpi1_parents[] __initconst = {
++ "clk26m",
++ "tvdpll_ck",
++ "tvdpll_d2",
++ "tvdpll_d4"
++};
++
++static const char * const tve_parents[] __initconst = {
++ "clk26m",
++ "mipipll",
++ "mipipll_d2",
++ "mipipll_d4",
++ "clk26m",
++ "tvdpll_ck",
++ "tvdpll_d2",
++ "tvdpll_d4"
++};
++
++static const char * const hdmi_parents[] __initconst = {
++ "clk26m",
++ "hdmipll_ck",
++ "hdmipll_d2",
++ "hdmipll_d3"
++};
++
++static const char * const apll_parents[] __initconst = {
++ "clk26m",
++ "audpll",
++ "audpll_d4",
++ "audpll_d8",
++ "audpll_d16",
++ "audpll_d24",
++ "clk26m",
++ "clk26m"
++};
++
++static const char * const rtc_parents[] __initconst = {
++ "32k_internal",
++ "32k_external",
++ "clk26m",
++ "univpll3_d8"
++};
++
++static const char * const nfi2x_parents[] __initconst = {
++ "clk26m",
++ "syspll2_d2",
++ "syspll_d7",
++ "univpll3_d2",
++ "syspll2_d4",
++ "univpll3_d4",
++ "syspll4_d4",
++ "clk26m"
++};
++
++static const char * const emmc_hclk_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d2",
++ "syspll1_d4",
++ "syspll2_d2"
++};
++
++static const char * const flash_parents[] __initconst = {
++ "clk26m_d8",
++ "clk26m",
++ "syspll2_d8",
++ "syspll3_d4",
++ "univpll3_d4",
++ "syspll4_d2",
++ "syspll2_d4",
++ "univpll2_d4"
++};
++
++static const char * const di_parents[] __initconst = {
++ "clk26m",
++ "tvd2pll_ck",
++ "tvd2pll_d2",
++ "clk26m"
++};
++
++static const char * const nr_osd_parents[] __initconst = {
++ "clk26m",
++ "vencpll_ck",
++ "syspll1_d2",
++ "syspll1_d4",
++ "univpll_d5",
++ "univpll1_d2",
++ "univpll2_d2",
++ "dmpll_ck"
++};
++
++static const char * const hdmirx_bist_parents[] __initconst = {
++ "clk26m",
++ "syspll_d3",
++ "clk26m",
++ "syspll1_d16",
++ "syspll4_d2",
++ "syspll1_d4",
++ "vencpll_ck",
++ "clk26m"
++};
++
++static const char * const intdir_parents[] __initconst = {
++ "clk26m",
++ "mmpll_ck",
++ "syspll_d2",
++ "univpll_d2"
++};
++
++static const char * const asm_parents[] __initconst = {
++ "clk26m",
++ "univpll2_d4",
++ "univpll2_d2",
++ "syspll_d5"
++};
++
++static const char * const ms_card_parents[] __initconst = {
++ "clk26m",
++ "univpll3_d8",
++ "syspll4_d4"
++};
++
++static const char * const ethif_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d2",
++ "syspll_d5",
++ "syspll1_d4",
++ "univpll_d5",
++ "univpll1_d2",
++ "dmpll_ck",
++ "dmpll_d2"
++};
++
++static const char * const hdmirx_parents[] __initconst = {
++ "clk26m",
++ "univpll_d52"
++};
++
++static const char * const cmsys_parents[] __initconst = {
++ "clk26m",
++ "syspll1_d2",
++ "univpll1_d2",
++ "univpll_d5",
++ "syspll_d5",
++ "syspll2_d2",
++ "syspll1_d4",
++ "syspll3_d2",
++ "syspll2_d4",
++ "syspll1_d8",
++ "clk26m",
++ "clk26m",
++ "clk26m",
++ "clk26m",
++ "clk26m"
++};
++
++static const char * const clk_8bdac_parents[] __initconst = {
++ "clkrtc_int",
++ "8bdac_ck_pre",
++ "clk26m",
++ "clk26m"
++};
++
++static const char * const aud2dvd_parents[] __initconst = {
++ "a1sys_hp_ck",
++ "a2sys_hp_ck"
++};
++
++static const char * const padmclk_parents[] __initconst = {
++ "clk26m",
++ "univpll_d26",
++ "univpll_d52",
++ "univpll_d108",
++ "univpll2_d8",
++ "univpll2_d16",
++ "univpll2_d32"
++};
++
++static const char * const aud_mux_parents[] __initconst = {
++ "clk26m",
++ "aud1pll_98m_ck",
++ "aud2pll_90m_ck",
++ "hadds2pll_98m",
++ "audio_ext1_ck",
++ "audio_ext2_ck"
++};
++
++static const char * const aud_src_parents[] __initconst = {
++ "aud_mux1_sel",
++ "aud_mux2_sel"
++};
++
++static const char * const cpu_parents[] __initconst = {
++ "clk26m",
++ "armpll",
++ "mainpll",
++ "mmpll"
++};
++
++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),
++ MUX_GATE(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1, 15),
++ MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, 0x0040, 16, 1, 23),
++ MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 3, 31),
++
++ MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7),
++ MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15),
++ MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 16, 3, 23),
++ MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0050, 24, 3, 31),
++ MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 0, 1, 7),
++
++ MUX_GATE(CLK_TOP_SPI0_SEL, "spi0_sel", spi_parents, 0x0060, 8, 3, 15),
++ MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 16, 2, 23),
++ MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents, 0x0060, 24, 3, 31),
++
++ MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents, 0x0070, 0, 3, 7),
++ MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents, 0x0070, 8, 3, 15),
++ MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", msdc30_parents, 0x0070, 16, 1, 23),
++ MUX_GATE(CLK_TOP_AUDINTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, 0x0070, 24, 3, 31),
++
++ MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0080, 0, 4, 7),
++ MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x0080, 8, 2, 15),
++ MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x0080, 16, 3, 23),
++ MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, 0x0080, 24, 2, 31),
++
++ MUX_GATE(CLK_TOP_TVE_SEL, "tve_sel", tve_parents, 0x0090, 0, 3, 7),
++ MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x0090, 8, 2, 15),
++ MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, 0x0090, 16, 3, 23),
++
++ MUX_GATE(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00A0, 0, 2, 7),
++ MUX_GATE(CLK_TOP_NFI2X_SEL, "nfi2x_sel", nfi2x_parents, 0x00A0, 8, 3, 15),
++ MUX_GATE(CLK_TOP_EMMC_HCLK_SEL, "emmc_hclk_sel", emmc_hclk_parents, 0x00A0, 24, 2, 31),
++
++ MUX_GATE(CLK_TOP_FLASH_SEL, "flash_sel", flash_parents, 0x00B0, 0, 3, 7),
++ MUX_GATE(CLK_TOP_DI_SEL, "di_sel", di_parents, 0x00B0, 8, 2, 15),
++ MUX_GATE(CLK_TOP_NR_SEL, "nr_sel", nr_osd_parents, 0x00B0, 16, 3, 23),
++ MUX_GATE(CLK_TOP_OSD_SEL, "osd_sel", nr_osd_parents, 0x00B0, 24, 3, 31),
++
++ MUX_GATE(CLK_TOP_HDMIRX_BIST_SEL, "hdmirx_bist_sel", hdmirx_bist_parents, 0x00C0, 0, 3, 7),
++ MUX_GATE(CLK_TOP_INTDIR_SEL, "intdir_sel", intdir_parents, 0x00C0, 8, 2, 15),
++ MUX_GATE(CLK_TOP_ASM_I_SEL, "asm_i_sel", asm_parents, 0x00C0, 16, 2, 23),
++ MUX_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel", asm_parents, 0x00C0, 24, 3, 31),
++
++ MUX_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel", asm_parents, 0x00D0, 0, 2, 7),
++ MUX_GATE(CLK_TOP_MS_CARD_SEL, "ms_card_sel", ms_card_parents, 0x00D0, 16, 2, 23),
++ MUX_GATE(CLK_TOP_ETHIF_SEL, "ethif_sel", ethif_parents, 0x00D0, 24, 3, 31),
++
++ MUX_GATE(CLK_TOP_HDMIRX26_24_SEL, "hdmirx26_24_sel", hdmirx_parents, 0x00E0, 0, 1, 7),
++ MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents, 0x00E0, 8, 3, 15),
++ MUX_GATE(CLK_TOP_CMSYS_SEL, "cmsys_sel", cmsys_parents, 0x00E0, 16, 4, 23),
++
++ MUX_GATE(CLK_TOP_SPI1_SEL, "spi2_sel", spi_parents, 0x00E0, 24, 3, 31),
++ MUX_GATE(CLK_TOP_SPI2_SEL, "spi1_sel", spi_parents, 0x00F0, 0, 3, 7),
++ MUX_GATE(CLK_TOP_8BDAC_SEL, "8bdac_sel", clk_8bdac_parents, 0x00F0, 8, 2, 15),
++ MUX_GATE(CLK_TOP_AUD2DVD_SEL, "aud2dvd_sel", aud2dvd_parents, 0x00F0, 16, 1, 23),
++
++ MUX(CLK_TOP_PADMCLK_SEL, "padmclk_sel", padmclk_parents, 0x0100, 0, 3),
++
++ MUX(CLK_TOP_AUD_MUX1_SEL, "aud_mux1_sel", aud_mux_parents, 0x012c, 0, 3),
++ MUX(CLK_TOP_AUD_MUX2_SEL, "aud_mux2_sel", aud_mux_parents, 0x012c, 3, 3),
++ MUX(CLK_TOP_AUDPLL_MUX_SEL, "audpll_sel", aud_mux_parents, 0x012c, 6, 3),
++ MUX_GATE(CLK_TOP_AUD_K1_SRC_SEL, "aud_k1_src_sel", aud_src_parents, 0x012c, 15, 1, 23),
++ MUX_GATE(CLK_TOP_AUD_K2_SRC_SEL, "aud_k2_src_sel", aud_src_parents, 0x012c, 16, 1, 24),
++ MUX_GATE(CLK_TOP_AUD_K3_SRC_SEL, "aud_k3_src_sel", aud_src_parents, 0x012c, 17, 1, 25),
++ MUX_GATE(CLK_TOP_AUD_K4_SRC_SEL, "aud_k4_src_sel", aud_src_parents, 0x012c, 18, 1, 26),
++ MUX_GATE(CLK_TOP_AUD_K5_SRC_SEL, "aud_k5_src_sel", aud_src_parents, 0x012c, 19, 1, 27),
++ MUX_GATE(CLK_TOP_AUD_K6_SRC_SEL, "aud_k6_src_sel", aud_src_parents, 0x012c, 20, 1, 28),
++};
++
++static const struct mtk_clk_divider top_adj_divs[] __initconst = {
++ DIV_ADJ(CLK_TOP_AUD_EXTCK1_DIV, "audio_ext1_ck", "aud_ext_ck1", 0x0120, 0, 8),
++ DIV_ADJ(CLK_TOP_AUD_EXTCK2_DIV, "audio_ext2_ck", "aud_ext_ck2", 0x0120, 8, 8),
++ DIV_ADJ(CLK_TOP_AUD_MUX1_DIV, "aud_mux1_div", "aud_mux1_sel", 0x0120, 16, 8),
++ DIV_ADJ(CLK_TOP_AUD_MUX2_DIV, "aud_mux2_div", "aud_mux2_sel", 0x0120, 24, 8),
++ DIV_ADJ(CLK_TOP_AUD_K1_SRC_DIV, "aud_k1_src_div", "aud_k1_src_sel", 0x0124, 0, 8),
++ DIV_ADJ(CLK_TOP_AUD_K2_SRC_DIV, "aud_k2_src_div", "aud_k2_src_sel", 0x0124, 8, 8),
++ DIV_ADJ(CLK_TOP_AUD_K3_SRC_DIV, "aud_k3_src_div", "aud_k3_src_sel", 0x0124, 16, 8),
++ DIV_ADJ(CLK_TOP_AUD_K4_SRC_DIV, "aud_k4_src_div", "aud_k4_src_sel", 0x0124, 24, 8),
++ DIV_ADJ(CLK_TOP_AUD_K5_SRC_DIV, "aud_k5_src_div", "aud_k5_src_sel", 0x0128, 0, 8),
++ DIV_ADJ(CLK_TOP_AUD_K6_SRC_DIV, "aud_k6_src_div", "aud_k6_src_sel", 0x0128, 8, 8),
++};
++
++static const struct mtk_gate_regs top_aud_cg_regs __initconst = {
++ .sta_ofs = 0x012C,
++};
++
++#define GATE_TOP_AUD(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &top_aud_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_no_setclr, \
++ }
++
++static const struct mtk_gate top_clks[] __initconst = {
++ GATE_TOP_AUD(CLK_TOP_AUD_48K_TIMING, "a1sys_hp_ck", "aud_mux1_div", 21),
++ GATE_TOP_AUD(CLK_TOP_AUD_44K_TIMING, "a2sys_hp_ck", "aud_mux2_div", 22),
++ GATE_TOP_AUD(CLK_TOP_AUD_I2S1_MCLK, "aud_i2s1_mclk", "aud_k1_src_div", 23),
++ GATE_TOP_AUD(CLK_TOP_AUD_I2S2_MCLK, "aud_i2s2_mclk", "aud_k2_src_div", 24),
++ GATE_TOP_AUD(CLK_TOP_AUD_I2S3_MCLK, "aud_i2s3_mclk", "aud_k3_src_div", 25),
++ GATE_TOP_AUD(CLK_TOP_AUD_I2S4_MCLK, "aud_i2s4_mclk", "aud_k4_src_div", 26),
++ GATE_TOP_AUD(CLK_TOP_AUD_I2S5_MCLK, "aud_i2s5_mclk", "aud_k5_src_div", 27),
++ GATE_TOP_AUD(CLK_TOP_AUD_I2S6_MCLK, "aud_i2s6_mclk", "aud_k6_src_div", 28),
++};
++
++static void __init mtk_topckgen_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ void __iomem *base;
++ int r;
++
++ base = of_iomap(node, 0);
++ if (!base) {
++ pr_err("%s(): ioremap failed\n", __func__);
++ return;
++ }
++
++ clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
++
++ mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
++ clk_data);
++
++ mtk_clk_register_factors(top_fixed_divs, ARRAY_SIZE(top_fixed_divs),
++ clk_data);
++
++ mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes),
++ base, &lock, clk_data);
++
++ mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs),
++ base, &lock, clk_data);
++
++ mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt2701-topckgen", mtk_topckgen_init);
++
++static const struct mtk_gate_regs infra_cg_regs __initconst = {
++ .set_ofs = 0x0040,
++ .clr_ofs = 0x0044,
++ .sta_ofs = 0x0048,
++};
++
++#define GATE_ICG(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &infra_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr, \
++ }
++
++static const struct mtk_gate infra_clks[] __initconst = {
++ GATE_ICG(CLK_INFRA_DBG, "dbgclk", "axi_sel", 0),
++ GATE_ICG(CLK_INFRA_SMI, "smi_ck", "mm_sel", 1),
++ GATE_ICG(CLK_INFRA_QAXI_CM4, "cm4_ck", "axi_sel", 2),
++ GATE_ICG(CLK_INFRA_AUD_SPLIN_B, "audio_splin_bck", "hadds2_294m_ck", 4),
++ GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "clk_null", 5),
++ GATE_ICG(CLK_INFRA_EFUSE, "efuse_ck", "clk26m", 6),
++ GATE_ICG(CLK_INFRA_L2C_SRAM, "l2c_sram_ck", "mm_sel", 7),
++ GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8),
++ GATE_ICG(CLK_INFRA_CONNMCU, "connsys_bus", "wbg_dig_ck_416m", 12),
++ GATE_ICG(CLK_INFRA_TRNG, "trng_ck", "axi_sel", 13),
++ GATE_ICG(CLK_INFRA_RAMBUFIF, "rambufif_ck", "mem_sel", 14),
++ GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "mem_sel", 15),
++ GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16),
++ GATE_ICG(CLK_INFRA_CEC, "cec_ck", "rtc_sel", 18),
++ GATE_ICG(CLK_INFRA_IRRX, "irrx_ck", "axi_sel", 19),
++ GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22),
++ GATE_ICG(CLK_INFRA_PMICWRAP, "pmicwrap_ck", "axi_sel", 23),
++ GATE_ICG(CLK_INFRA_DDCCI, "ddcci_ck", "axi_sel", 24),
++};
++
++static const struct mtk_fixed_factor infra_fixed_divs[] __initconst = {
++ FACTOR(CLK_INFRA_CLK_13M, "clk13m", "clk26m", 1, 2),
++};
++
++static void __init mtk_infrasys_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ int r;
++
++ clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
++
++ mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
++ clk_data);
++ mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt2701-infracfg", mtk_infrasys_init);
++
++static const struct mtk_gate_regs peri0_cg_regs __initconst = {
++ .set_ofs = 0x0008,
++ .clr_ofs = 0x0010,
++ .sta_ofs = 0x0018,
++};
++
++static const struct mtk_gate_regs peri1_cg_regs __initconst = {
++ .set_ofs = 0x000c,
++ .clr_ofs = 0x0014,
++ .sta_ofs = 0x001c,
++};
++
++#define GATE_PERI0(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &peri0_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr, \
++ }
++
++#define GATE_PERI1(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &peri1_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr, \
++ }
++
++static const struct mtk_gate peri_clks[] __initconst = {
++ GATE_PERI1(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 31),
++ GATE_PERI1(CLK_PERI_ETH, "eth_ck", "clk26m", 30),
++ GATE_PERI1(CLK_PERI_SPI0, "spi0_ck", "spi0_sel", 29),
++ GATE_PERI1(CLK_PERI_AUXADC, "auxadc_ck", "clk26m", 28),
++ GATE_PERI0(CLK_PERI_I2C3, "i2c3_ck", "clk26m", 27),
++ GATE_PERI0(CLK_PERI_I2C2, "i2c2_ck", "axi_sel", 26),
++ GATE_PERI0(CLK_PERI_I2C1, "i2c1_ck", "axi_sel", 25),
++ GATE_PERI0(CLK_PERI_I2C0, "i2c0_ck", "axi_sel", 24),
++ GATE_PERI0(CLK_PERI_BTIF, "bitif_ck", "axi_sel", 23),
++ GATE_PERI0(CLK_PERI_UART3, "uart3_ck", "axi_sel", 22),
++ GATE_PERI0(CLK_PERI_UART2, "uart2_ck", "axi_sel", 21),
++ GATE_PERI0(CLK_PERI_UART1, "uart1_ck", "axi_sel", 20),
++ GATE_PERI0(CLK_PERI_UART0, "uart0_ck", "axi_sel", 19),
++ GATE_PERI0(CLK_PERI_NLI, "nli_ck", "axi_sel", 18),
++ GATE_PERI0(CLK_PERI_MSDC50_3, "msdc50_3_ck", "emmc_hclk_sel", 17),
++ GATE_PERI0(CLK_PERI_MSDC30_3, "msdc30_3_ck", "msdc30_3_sel", 16),
++ GATE_PERI0(CLK_PERI_MSDC30_2, "msdc30_2_ck", "msdc30_2_sel", 15),
++ GATE_PERI0(CLK_PERI_MSDC30_1, "msdc30_1_ck", "msdc30_1_sel", 14),
++ GATE_PERI0(CLK_PERI_MSDC30_0, "msdc30_0_ck", "msdc30_0_sel", 13),
++ GATE_PERI0(CLK_PERI_AP_DMA, "ap_dma_ck", "axi_sel", 12),
++ GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11),
++ GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10),
++ GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9),
++ GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8),
++ GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7),
++ GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6),
++ GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5),
++ GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4),
++ GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3),
++ GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2),
++ GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1),
++ GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "nfi2x_sel", 0),
++
++ GATE_PERI1(CLK_PERI_FCI, "fci_ck", "ms_card", 11),
++ GATE_PERI1(CLK_PERI_SPI2, "spi2_ck", "spi2_sel", 10),
++ GATE_PERI1(CLK_PERI_SPI1, "spi1_ck", "spi1_sel", 9),
++ GATE_PERI1(CLK_PERI_HOST89_DVD, "host89_dvd_ck", "aud2dvd_sel", 8),
++ GATE_PERI1(CLK_PERI_HOST89_SPI, "host89_spi_ck", "spi0_sel", 7),
++ GATE_PERI1(CLK_PERI_HOST89_INT, "host89_int_ck", "axi_sel", 6),
++ GATE_PERI1(CLK_PERI_FLASH, "flash_ck", "nfi2x_sel", 5),
++ GATE_PERI1(CLK_PERI_NFI_PAD, "nfi_pad_ck", "nfi_sel", 4),
++ GATE_PERI1(CLK_PERI_NFI_ECC, "nfi_ecc_ck", "nfi_sel", 3),
++ GATE_PERI1(CLK_PERI_GCPU, "gcpu_ck", "axi_sel", 2),
++ GATE_PERI1(CLK_PERI_USB_SLV, "usbslv_ck", "axi_sel", 1),
++ GATE_PERI1(CLK_PERI_USB1_MCU, "usb1_mcu_ck", "axi_sel", 0),
++};
++
++static const char * const uart_ck_sel_parents[] __initconst = {
++ "clk26m",
++ "uart_sel",
++};
++
++static const struct mtk_composite peri_muxs[] __initconst = {
++ MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1),
++ MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1),
++ MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1),
++ MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1),
++};
++
++static void __init mtk_pericfg_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ void __iomem *base;
++ int r;
++
++ base = of_iomap(node, 0);
++ if (!base) {
++ pr_err("%s(): ioremap failed\n", __func__);
++ return;
++ }
++
++ clk_data = mtk_alloc_clk_data(CLK_PERI_NR);
++
++ mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks),
++ clk_data);
++
++ mtk_clk_register_composites(peri_muxs, ARRAY_SIZE(peri_muxs), base,
++ &lock, 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt2701-pericfg", mtk_pericfg_init);
++
++static const struct mtk_gate_regs disp0_cg_regs __initconst = {
++ .set_ofs = 0x0104,
++ .clr_ofs = 0x0108,
++ .sta_ofs = 0x0100,
++};
++
++static const struct mtk_gate_regs disp1_cg_regs __initconst = {
++ .set_ofs = 0x0114,
++ .clr_ofs = 0x0118,
++ .sta_ofs = 0x0110,
++};
++
++#define GATE_DISP0(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &disp0_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr, \
++ }
++
++#define GATE_DISP1(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &disp1_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr, \
++ }
++
++static const struct mtk_gate mm_clks[] __initconst = {
++ GATE_DISP0(CLK_MM_SMI_COMMON, "mm_smi_comm", "mm_sel", 0),
++ GATE_DISP0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1),
++ GATE_DISP0(CLK_MM_CMDQ, "mm_cmdq", "mm_sel", 2),
++ GATE_DISP0(CLK_MM_MUTEX, "mm_mutex", "mm_sel", 3),
++ GATE_DISP0(CLK_MM_DISP_COLOR, "mm_disp_color", "mm_sel", 4),
++ GATE_DISP0(CLK_MM_DISP_BLS, "mm_disp_bls", "mm_sel", 5),
++ GATE_DISP0(CLK_MM_DISP_WDMA, "mm_disp_wdma", "mm_sel", 6),
++ GATE_DISP0(CLK_MM_DISP_RDMA, "mm_disp_rdma", "mm_sel", 7),
++ GATE_DISP0(CLK_MM_DISP_OVL, "mm_disp_ovl", "mm_sel", 8),
++ GATE_DISP0(CLK_MM_MDP_TDSHP, "mm_mdp_tdshp", "mm_sel", 9),
++ GATE_DISP0(CLK_MM_MDP_WROT, "mm_mdp_wrot", "mm_sel", 10),
++ GATE_DISP0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11),
++ GATE_DISP0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 12),
++ GATE_DISP0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 13),
++ GATE_DISP0(CLK_MM_MDP_RDMA, "mm_mdp_rdma", "mm_sel", 14),
++ GATE_DISP0(CLK_MM_MDP_BLS_26M, "mm_mdp_bls_26m", "clk26m", 15),
++ GATE_DISP0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 16),
++ GATE_DISP0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 17),
++ GATE_DISP0(CLK_MM_MUTEX_32K, "mm_mutex_32k", "rtc_sel", 18),
++ GATE_DISP0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 19),
++ GATE_DISP0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 20),
++ GATE_DISP1(CLK_MM_DSI_ENGINE, "mm_dsi_eng", "mm_sel", 0),
++ GATE_DISP1(CLK_MM_DSI_DIG, "mm_dsi_dig", "dsio_lntc_dsiclk", 1),
++ GATE_DISP1(CLK_MM_DPI_DIGL, "mm_dpi_digl", "dpi0_sel", 2),
++ GATE_DISP1(CLK_MM_DPI_ENGINE, "mm_dpi_eng", "mm_sel", 3),
++ GATE_DISP1(CLK_MM_DPI1_DIGL, "mm_dpi1_digl", "dpi1_sel", 4),
++ GATE_DISP1(CLK_MM_DPI1_ENGINE, "mm_dpi1_eng", "mm_sel", 5),
++ GATE_DISP1(CLK_MM_TVE_OUTPUT, "mm_tve_output", "tve_sel", 6),
++ GATE_DISP1(CLK_MM_TVE_INPUT, "mm_tve_input", "dpi0_sel", 7),
++ GATE_DISP1(CLK_MM_HDMI_PIXEL, "mm_hdmi_pixel", "dpi1_sel", 8),
++ GATE_DISP1(CLK_MM_HDMI_PLL, "mm_hdmi_pll", "hdmi_sel", 9),
++ GATE_DISP1(CLK_MM_HDMI_AUDIO, "mm_hdmi_audio", "apll_sel", 10),
++ GATE_DISP1(CLK_MM_HDMI_SPDIF, "mm_hdmi_spdif", "apll_sel", 11),
++ GATE_DISP1(CLK_MM_TVE_FMM, "mm_tve_fmm", "mm_sel", 14),
++};
++
++static void __init mtk_mmsys_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ int r;
++
++ clk_data = mtk_alloc_clk_data(CLK_MM_NR);
++
++ mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_mmsys, "mediatek,mt2701-mmsys", mtk_mmsys_init);
++
++static const struct mtk_gate_regs img_cg_regs __initconst = {
++ .set_ofs = 0x0004,
++ .clr_ofs = 0x0008,
++ .sta_ofs = 0x0000,
++};
++
++#define GATE_IMG(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &img_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr, \
++ }
++
++static const struct mtk_gate img_clks[] __initconst = {
++ GATE_IMG(CLK_IMG_SMI_COMM, "img_smi_comm", "mm_sel", 0),
++ GATE_IMG(CLK_IMG_RESZ, "img_resz", "mm_sel", 1),
++ GATE_IMG(CLK_IMG_JPGDEC, "img_jpgdec", "mm_sel", 5),
++ GATE_IMG(CLK_IMG_VENC_LT, "img_venc_lt", "mm_sel", 8),
++ GATE_IMG(CLK_IMG_VENC, "img_venc", "mm_sel", 9),
++};
++
++static void __init mtk_imgsys_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ int r;
++
++ clk_data = mtk_alloc_clk_data(CLK_IMG_NR);
++
++ mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_imgsys, "mediatek,mt2701-imgsys", mtk_imgsys_init);
++
++static const struct mtk_gate_regs vdec0_cg_regs __initconst = {
++ .set_ofs = 0x0000,
++ .clr_ofs = 0x0004,
++ .sta_ofs = 0x0000,
++};
++
++static const struct mtk_gate_regs vdec1_cg_regs __initconst = {
++ .set_ofs = 0x0008,
++ .clr_ofs = 0x000c,
++ .sta_ofs = 0x0008,
++};
++
++#define GATE_VDEC0(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &vdec0_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr_inv, \
++ }
++
++#define GATE_VDEC1(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &vdec1_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr_inv, \
++ }
++
++static const struct mtk_gate vdec_clks[] __initconst = {
++ GATE_VDEC0(CLK_VDEC_CKGEN, "vdec_cken", "vdec_sel", 0),
++ GATE_VDEC1(CLK_VDEC_LARB, "vdec_larb_cken", "mm_sel", 0),
++};
++
++static void __init mtk_vdecsys_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ int r;
++
++ clk_data = mtk_alloc_clk_data(CLK_VDEC_NR);
++
++ mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_vdecsys, "mediatek,mt2701-vdecsys", mtk_vdecsys_init);
++
++static const struct mtk_gate_regs hif_cg_regs __initconst = {
++ .sta_ofs = 0x0008,
++};
++
++#define GATE_HIF(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &hif_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
++ }
++
++static const struct mtk_gate hif_clks[] __initconst = {
++ GATE_HIF(CLK_HIFSYS_USB0PHY, "usb0_phy_clk", "ethpll_500m_ck", 21),
++ GATE_HIF(CLK_HIFSYS_USB1PHY, "usb1_phy_clk", "ethpll_500m_ck", 22),
++ GATE_HIF(CLK_HIFSYS_PCIE0, "pcie0_clk", "ethpll_500m_ck", 24),
++ GATE_HIF(CLK_HIFSYS_PCIE1, "pcie1_clk", "ethpll_500m_ck", 25),
++ GATE_HIF(CLK_HIFSYS_PCIE2, "pcie2_clk", "ethpll_500m_ck", 26),
++};
++
++static void __init mtk_hifsys_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ int r;
++
++ clk_data = mtk_alloc_clk_data(CLK_HIFSYS_NR);
++
++ mtk_clk_register_gates(node, hif_clks, ARRAY_SIZE(hif_clks),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_hifsys, "mediatek,mt2701-hifsys", mtk_hifsys_init);
++
++static const struct mtk_gate_regs eth_cg_regs __initconst = {
++ .sta_ofs = 0x0030,
++};
++
++#define GATE_eth(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &eth_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
++ }
++
++static const struct mtk_gate eth_clks[] __initconst = {
++ GATE_HIF(CLK_ETHSYS_HSDMA, "hsdma_clk", "ethif_sel", 5),
++ GATE_HIF(CLK_ETHSYS_ESW, "esw_clk", "ethpll_500m_ck", 6),
++ GATE_HIF(CLK_ETHSYS_GP2, "gp2_clk", "trgpll", 7),
++ GATE_HIF(CLK_ETHSYS_GP1, "gp1_clk", "ethpll_500m_ck", 8),
++ GATE_HIF(CLK_ETHSYS_PCM, "pcm_clk", "ethif_sel", 11),
++ GATE_HIF(CLK_ETHSYS_GDMA, "gdma_clk", "ethif_sel", 14),
++ GATE_HIF(CLK_ETHSYS_I2S, "i2s_clk", "ethif_sel", 17),
++ GATE_HIF(CLK_ETHSYS_CRYPTO, "crypto_clk", "ethif_sel", 29),
++};
++
++static void __init mtk_ethsys_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ int r;
++
++ clk_data = mtk_alloc_clk_data(CLK_ETHSYS_NR);
++
++ mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_ethsys, "mediatek,mt2701-ethsys", mtk_ethsys_init);
++
++static const struct mtk_gate_regs bdp0_cg_regs __initconst = {
++ .set_ofs = 0x0104,
++ .clr_ofs = 0x0108,
++ .sta_ofs = 0x0100,
++};
++
++static const struct mtk_gate_regs bdp1_cg_regs __initconst = {
++ .set_ofs = 0x0114,
++ .clr_ofs = 0x0118,
++ .sta_ofs = 0x0110,
++};
++
++#define GATE_BDP0(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &bdp0_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr_inv, \
++ }
++
++#define GATE_BDP1(_id, _name, _parent, _shift) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .regs = &bdp1_cg_regs, \
++ .shift = _shift, \
++ .ops = &mtk_clk_gate_ops_setclr_inv, \
++ }
++
++static const struct mtk_gate bdp_clks[] __initconst = {
++ GATE_BDP0(CLK_BDP_BRG_BA, "brg_baclk", "mm_sel", 0),
++ GATE_BDP0(CLK_BDP_BRG_DRAM, "brg_dram", "mm_sel", 1),
++ GATE_BDP0(CLK_BDP_LARB_DRAM, "larb_dram", "mm_sel", 2),
++ GATE_BDP0(CLK_BDP_WR_VDI_PXL, "wr_vdi_pxl", "hdmi_0_deep340m", 3),
++ GATE_BDP0(CLK_BDP_WR_VDI_DRAM, "wr_vdi_dram", "mm_sel", 4),
++ GATE_BDP0(CLK_BDP_WR_B, "wr_bclk", "mm_sel", 5),
++ GATE_BDP0(CLK_BDP_DGI_IN, "dgi_in", "dpi1_sel", 6),
++ GATE_BDP0(CLK_BDP_DGI_OUT, "dgi_out", "dpi_sel", 7),
++ GATE_BDP0(CLK_BDP_FMT_MAST_27, "fmt_mast_27", "dpi1_sel", 8),
++ GATE_BDP0(CLK_BDP_FMT_B, "fmt_bclk", "mm_sel", 9),
++ GATE_BDP0(CLK_BDP_OSD_B, "osd_bclk", "mm_sel", 10),
++ GATE_BDP0(CLK_BDP_OSD_DRAM, "osd_dram", "mm_sel", 11),
++ GATE_BDP0(CLK_BDP_OSD_AGENT, "osd_agent", "osd_sel", 12),
++ GATE_BDP0(CLK_BDP_OSD_PXL, "osd_pxl", "dpi1_sel", 13),
++ GATE_BDP0(CLK_BDP_RLE_B, "rle_bclk", "mm_sel", 14),
++ GATE_BDP0(CLK_BDP_RLE_AGENT, "rle_agent", "mm_sel", 15),
++ GATE_BDP0(CLK_BDP_RLE_DRAM, "rle_dram", "mm_sel", 16),
++ GATE_BDP0(CLK_BDP_F27M, "f27m", "di_sel", 17),
++ GATE_BDP0(CLK_BDP_F27M_VDOUT, "f27m_vdout", "di_sel", 18),
++ GATE_BDP0(CLK_BDP_F27_74_74, "f27_74_74", "di_sel", 19),
++ GATE_BDP0(CLK_BDP_F2FS, "f2fs", "di_sel", 20),
++ GATE_BDP0(CLK_BDP_F2FS74_148, "f2fs74_148", "di_sel", 21),
++ GATE_BDP0(CLK_BDP_FB, "fbclk", "mm_sel", 22),
++ GATE_BDP0(CLK_BDP_VDO_DRAM, "vdo_dram", "mm_sel", 23),
++ GATE_BDP0(CLK_BDP_VDO_2FS, "vdo_2fs", "di_sel", 24),
++ GATE_BDP0(CLK_BDP_VDO_B, "vdo_bclk", "mm_sel", 25),
++ GATE_BDP0(CLK_BDP_WR_DI_PXL, "wr_di_pxl", "di_sel", 26),
++ GATE_BDP0(CLK_BDP_WR_DI_DRAM, "wr_di_dram", "mm_sel", 27),
++ GATE_BDP0(CLK_BDP_WR_DI_B, "wr_di_bclk", "mm_sel", 28),
++ GATE_BDP0(CLK_BDP_NR_PXL, "nr_pxl", "nr_sel", 29),
++ GATE_BDP0(CLK_BDP_NR_DRAM, "nr_dram", "mm_sel", 30),
++ GATE_BDP0(CLK_BDP_NR_B, "nr_bclk", "mm_sel", 31),
++ GATE_BDP1(CLK_BDP_RX_F, "rx_fclk", "hadds2_fbclk", 0),
++ GATE_BDP1(CLK_BDP_RX_X, "rx_xclk", "clk26m", 1),
++ GATE_BDP1(CLK_BDP_RXPDT, "rxpdtclk", "hdmi_0_pix340m", 2),
++ GATE_BDP1(CLK_BDP_RX_CSCL_N, "rx_cscl_n", "clk26m", 3),
++ GATE_BDP1(CLK_BDP_RX_CSCL, "rx_cscl", "clk26m", 4),
++ GATE_BDP1(CLK_BDP_RX_DDCSCL_N, "rx_ddcscl_n", "hdmi_scl_rx", 5),
++ GATE_BDP1(CLK_BDP_RX_DDCSCL, "rx_ddcscl", "hdmi_scl_rx", 6),
++ GATE_BDP1(CLK_BDP_RX_VCO, "rx_vcoclk", "hadds2pll_294m", 7),
++ GATE_BDP1(CLK_BDP_RX_DP, "rx_dpclk", "hdmi_0_pll340m", 8),
++ GATE_BDP1(CLK_BDP_RX_P, "rx_pclk", "hdmi_0_pll340m", 9),
++ GATE_BDP1(CLK_BDP_RX_M, "rx_mclk", "hadds2pll_294m", 10),
++ GATE_BDP1(CLK_BDP_RX_PLL, "rx_pllclk", "hdmi_0_pix340m", 11),
++ GATE_BDP1(CLK_BDP_BRG_RT_B, "brg_rt_bclk", "mm_sel", 12),
++ GATE_BDP1(CLK_BDP_BRG_RT_DRAM, "brg_rt_dram", "mm_sel", 13),
++ GATE_BDP1(CLK_BDP_LARBRT_DRAM, "larbrt_dram", "mm_sel", 14),
++ GATE_BDP1(CLK_BDP_TMDS_SYN, "tmds_syn", "hdmi_0_pll340m", 15),
++ GATE_BDP1(CLK_BDP_HDMI_MON, "hdmi_mon", "hdmi_0_mon", 16),
++};
++
++static void __init mtk_bdpsys_init(struct device_node *node)
++{
++ struct clk_onecell_data *clk_data;
++ int r;
++
++ clk_data = mtk_alloc_clk_data(CLK_BDP_NR);
++
++ mtk_clk_register_gates(node, bdp_clks, ARRAY_SIZE(bdp_clks),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_bdpsys, "mediatek,mt2701-bdpsys", mtk_bdpsys_init);
++
++#define MT8590_PLL_FMAX (2000 * MHZ)
++#define CON0_MT8590_RST_BAR BIT(27)
++
++#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, \
++ _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \
++ .id = _id, \
++ .name = _name, \
++ .reg = _reg, \
++ .pwr_reg = _pwr_reg, \
++ .en_mask = _en_mask, \
++ .flags = _flags, \
++ .rst_bar_mask = CON0_MT8590_RST_BAR, \
++ .fmax = MT8590_PLL_FMAX, \
++ .pcwbits = _pcwbits, \
++ .pd_reg = _pd_reg, \
++ .pd_shift = _pd_shift, \
++ .tuner_reg = _tuner_reg, \
++ .pcw_reg = _pcw_reg, \
++ .pcw_shift = _pcw_shift, \
++ }
++
++static const struct mtk_pll_data apmixed_plls[] = {
++ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x200, 0x20c, 0x80000001, 0,
++ 21, 0x204, 24, 0x0, 0x204, 0),
++ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x210, 0x21c, 0xf0000001,
++ HAVE_RST_BAR, 21, 0x210, 4, 0x0, 0x214, 0),
++ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x220, 0x22c, 0xf3000001,
++ HAVE_RST_BAR, 7, 0x220, 4, 0x0, 0x224, 14),
++ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x230, 0x23c, 0x00000001, 0,
++ 21, 0x230, 4, 0x0, 0x234, 0),
++ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x240, 0x24c, 0x00000001, 0,
++ 21, 0x240, 4, 0x0, 0x244, 0),
++ PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x250, 0x25c, 0x00000001, 0,
++ 21, 0x250, 4, 0x0, 0x254, 0),
++ PLL(CLK_APMIXED_AUD1PLL, "aud1pll", 0x270, 0x27c, 0x00000001, 0,
++ 31, 0x270, 4, 0x0, 0x274, 0),
++ PLL(CLK_APMIXED_TRGPLL, "trgpll", 0x280, 0x28c, 0x00000001, 0,
++ 31, 0x280, 4, 0x0, 0x284, 0),
++ PLL(CLK_APMIXED_ETHPLL, "ethpll", 0x290, 0x29c, 0x00000001, 0,
++ 31, 0x290, 4, 0x0, 0x294, 0),
++ PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x2a0, 0x2ac, 0x00000001, 0,
++ 31, 0x2a0, 4, 0x0, 0x2a4, 0),
++ PLL(CLK_APMIXED_HADDS2PLL, "hadds2pll", 0x2b0, 0x2bc, 0x00000001, 0,
++ 31, 0x2b0, 4, 0x0, 0x2b4, 0),
++ PLL(CLK_APMIXED_AUD2PLL, "aud2pll", 0x2c0, 0x2cc, 0x00000001, 0,
++ 31, 0x2c0, 4, 0x0, 0x2c4, 0),
++ PLL(CLK_APMIXED_TVD2PLL, "tvd2pll", 0x2d0, 0x2dc, 0x00000001, 0,
++ 21, 0x2d0, 4, 0x0, 0x2d4, 0),
++};
++
++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));
++ if (!clk_data)
++ return;
++
++ mtk_clk_register_plls(node, apmixed_plls, ARRAY_SIZE(apmixed_plls),
++ 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",
++ __func__, r);
++}
++CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt2701-apmixedsys",
++ mtk_apmixedsys_init);
+--- a/drivers/clk/mediatek/clk-mtk.c
++++ b/drivers/clk/mediatek/clk-mtk.c
+@@ -244,3 +244,28 @@
+ clk_data->clks[mc->id] = clk;
+ }
+ }
++
++void __init mtk_clk_register_dividers(const struct mtk_clk_divider *mcds,
++ int num, void __iomem *base, spinlock_t *lock,
++ struct clk_onecell_data *clk_data)
++{
++ struct clk *clk;
++ int i;
++
++ for (i = 0; i < num; i++) {
++ const struct mtk_clk_divider *mcd = &mcds[i];
++
++ clk = clk_register_divider(NULL, mcd->name, mcd->parent_name,
++ mcd->flags, base + mcd->div_reg, mcd->div_shift,
++ mcd->div_width, mcd->clk_divider_flags, lock);
++
++ if (IS_ERR(clk)) {
++ pr_err("Failed to register clk %s: %ld\n",
++ mcd->name, PTR_ERR(clk));
++ continue;
++ }
++
++ if (clk_data)
++ clk_data->clks[mcd->id] = clk;
++ }
++}
+--- a/drivers/clk/mediatek/clk-mtk.h
++++ b/drivers/clk/mediatek/clk-mtk.h
+@@ -121,7 +121,8 @@
+ .flags = CLK_SET_RATE_PARENT, \
+ }
+
+-#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \
++#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, \
++ _div_width, _div_shift) { \
+ .id = _id, \
+ .parent = _parent, \
+ .name = _name, \
+@@ -156,8 +157,36 @@
+ const struct clk_ops *ops;
+ };
+
+-int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks,
+- int num, struct clk_onecell_data *clk_data);
++int mtk_clk_register_gates(struct device_node *node,
++ const struct mtk_gate *clks, int num,
++ struct clk_onecell_data *clk_data);
++
++struct mtk_clk_divider {
++ int id;
++ const char *name;
++ const char *parent_name;
++ unsigned long flags;
++
++ uint32_t div_reg;
++ unsigned char div_shift;
++ unsigned char div_width;
++ unsigned char clk_divider_flags;
++ const struct clk_div_table *clk_div_table;
++};
++
++#define DIV_ADJ(_id, _name, _parent, _reg, _shift, _width) { \
++ .id = _id, \
++ .name = _name, \
++ .parent_name = _parent, \
++ .flags = CLK_SET_RATE_PARENT, \
++ .div_reg = _reg, \
++ .div_shift = _shift, \
++ .div_width = _width, \
++}
++
++void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds,
++ int num, void __iomem *base, spinlock_t *lock,
++ struct clk_onecell_data *clk_data);
+
+ struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
+
diff --git a/target/linux/mediatek/patches-4.9/0011-reset-mediatek-mt2701-reset-driver.patch b/target/linux/mediatek/patches-4.9/0011-reset-mediatek-mt2701-reset-driver.patch
new file mode 100644
index 0000000000..18d4fbf252
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0011-reset-mediatek-mt2701-reset-driver.patch
@@ -0,0 +1,36 @@
+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 011/102] reset: mediatek: mt2701 reset driver
+
+In infrasys and perifsys, there are many reset
+control bits for kinds of modules. These bits are
+used as actual reset controllers to be registered
+into kernel's generic reset controller framework.
+
+Signed-off-by: Shunli Wang <shunli.wang@mediatek.com>
+Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
+---
+ drivers/clk/mediatek/clk-mt2701.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- 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
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
++
++ mtk_register_reset_controller(node, 2, 0x30);
+ }
+ CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt2701-infracfg", mtk_infrasys_init);
+
+@@ -782,6 +784,8 @@ static void __init mtk_pericfg_init(stru
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
++
++ mtk_register_reset_controller(node, 2, 0x0);
+ }
+ CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt2701-pericfg", mtk_pericfg_init);
+
diff --git a/target/linux/mediatek/patches-4.9/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch b/target/linux/mediatek/patches-4.9/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch
new file mode 100644
index 0000000000..696dea6179
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch
@@ -0,0 +1,29 @@
+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 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.
+Add config options so we can build for one SoC only. Add MT2701.
+
+Signed-off-by: Erin Lo <erin.lo@mediatek.com>
+Acked-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ arch/arm/mach-mediatek/Kconfig | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm/mach-mediatek/Kconfig
++++ b/arch/arm/mach-mediatek/Kconfig
+@@ -14,6 +14,10 @@
+ bool "MediaTek MT2701 SoCs support"
+ default ARCH_MEDIATEK
+
++config MACH_MT2701
++ bool "MediaTek MT2701 SoCs support"
++ default ARCH_MEDIATEK
++
+ config MACH_MT6589
+ bool "MediaTek MT6589 SoCs support"
+ default ARCH_MEDIATEK
diff --git a/target/linux/mediatek/patches-4.9/0017-clk-add-hifsys-reset.patch b/target/linux/mediatek/patches-4.9/0017-clk-add-hifsys-reset.patch
new file mode 100644
index 0000000000..60940f3239
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0017-clk-add-hifsys-reset.patch
@@ -0,0 +1,31 @@
+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 017/102] clk: add hifsys reset
+
+Hi,
+
+small patch to add hifsys reset bits. Maybe you could add it to the next
+version of your patch series. i have teste scpsys and clk on mt7623 today
+and it works well.
+
+thanks,
+ John
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/clk/mediatek/clk-mt2701.c | 2 ++
+ include/dt-bindings/reset-controller/mt2701-resets.h | 9 +++++++++
+ 2 files changed, 11 insertions(+)
+
+--- a/drivers/clk/mediatek/clk-mt2701.c
++++ b/drivers/clk/mediatek/clk-mt2701.c
+@@ -1000,6 +1000,8 @@
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
++
++ mtk_register_reset_controller(node, 1, 0x34);
+ }
+ CLK_OF_DECLARE(mtk_hifsys, "mediatek,mt2701-hifsys", mtk_hifsys_init);
+
diff --git a/target/linux/mediatek/patches-4.9/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch b/target/linux/mediatek/patches-4.9/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch
new file mode 100644
index 0000000000..d6fe977793
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch
@@ -0,0 +1,154 @@
+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 024/102] dt-bindings: add MediaTek PCIe binding documentation
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ .../devicetree/bindings/pci/mediatek-pcie.txt | 140 ++++++++++++++++++++
+ 1 file changed, 140 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pci/mediatek-pcie.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt
+@@ -0,0 +1,140 @@
++Mediatek PCIe controller
++
++Required properties:
++- compatible: Should be one of:
++ - "mediatek,mt2701-pcie"
++ - "mediatek,mt7623-pcie"
++- device_type: Must be "pci"
++- reg: A list of physical base address and length for each set of controller
++ registers. A list of register ranges to use. Must contain an
++ entry for each entry in the reg-names property.
++- reg-names: Must include the following entries:
++ "pcie": PCIe registers
++ "pcie phy0": PCIe PHY0 registers
++ "pcie phy1": PCIe PHY0 registers
++ "pcie phy2": PCIe PHY0 registers
++- interrupts: A list of interrupt outputs of the controller. Must contain an
++ entry for each entry in the interrupt-names property.
++- interrupt-names: Must include the following entries:
++ "pcie0": The interrupt that is asserted for port0
++ "pcie1": The interrupt that is asserted for port1
++ "pcie2": The interrupt that is asserted for port2
++- bus-range: Range of bus numbers associated with this controller
++- #address-cells: Address representation for root ports (must be 3)
++- #size-cells: Size representation for root ports (must be 2)
++- ranges: Describes the translation of addresses for root ports and standard
++ PCI regions. The entries must be 6 cells each.
++ Please refer to the standard PCI bus binding document for a more detailed
++ explanation.
++- #interrupt-cells: Size representation for interrupts (must be 1)
++- clocks: Must contain an entry for each entry in clock-names.
++ See ../clocks/clock-bindings.txt for details.
++- clock-names: Must include the following entries:
++ - pcie0
++ - pcie1
++ - pcie2
++- resets: Must contain an entry for each entry in reset-names.
++ See ../reset/reset.txt for details.
++- reset-names: Must include the following entries:
++ - pcie0
++ - pcie1
++ - pcie2
++- mediatek,hifsys: Must contain a phandle to the HIFSYS syscon range.
++Root ports are defined as subnodes of the PCIe controller node.
++
++Required properties:
++- device_type: Must be "pci"
++- assigned-addresses: Address and size of the port configuration registers
++- reg: PCI bus address of the root port
++- #address-cells: Must be 3
++- #size-cells: Must be 2
++- ranges: Sub-ranges distributed from the PCIe controller node. An empty
++ property is sufficient.
++
++Example:
++
++SoC DTSI:
++
++ hifsys: clock-controller@1a000000 {
++ compatible = "mediatek,mt7623-hifsys",
++ "mediatek,mt2701-hifsys",
++ "syscon";
++ reg = <0 0x1a000000 0 0x1000>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++ pcie-controller@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", "pice1", "pcie2";
++
++ bus-range = <0x00 0xff>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++
++ mediatek,hifsys = <&hifsys>;
++
++ 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;
++
++ status = "disabled";
++ };
++
++ pcie@2,0{
++ device_type = "pci";
++ reg = <0x1000 0 0 0 0>;
++
++ #address-cells = <3>;
++ #size-cells = <2>;
++ ranges;
++
++ status = "disabled";
++ };
++
++ pcie@3,0{
++ device_type = "pci";
++ reg = <0x1800 0 0 0 0>;
++
++ #address-cells = <3>;
++ #size-cells = <2>;
++ ranges;
++
++ status = "disabled";
++ };
++ };
++
++Board DTS:
++
++ pcie-controller {
++ status = "okay";
++
++ pci@1,0 {
++ status = "okay";
++ };
++ };
diff --git a/target/linux/mediatek/patches-4.9/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch b/target/linux/mediatek/patches-4.9/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch
new file mode 100644
index 0000000000..71081fbcdb
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch
@@ -0,0 +1,698 @@
+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 025/102] PCI: mediatek: add support for PCIe found on
+ MT7623/MT2701
+
+Add PCIe controller support on MediaTek MT2701/MT7623. The driver supports
+a single Root complex (RC) with 3 Root Ports. The SoCs supports a Gen2
+1-lan Link on each port.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ arch/arm/mach-mediatek/Kconfig | 1 +
+ drivers/pci/host/Kconfig | 11 +
+ drivers/pci/host/Makefile | 1 +
+ drivers/pci/host/pcie-mediatek.c | 641 ++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 654 insertions(+)
+ create mode 100644 drivers/pci/host/pcie-mediatek.c
+
+--- a/arch/arm/mach-mediatek/Kconfig
++++ b/arch/arm/mach-mediatek/Kconfig
+@@ -29,6 +29,7 @@
+ config MACH_MT7623
+ bool "MediaTek MT7623 SoCs support"
+ default ARCH_MEDIATEK
++ select MIGHT_HAVE_PCI
+
+ config MACH_MT8127
+ bool "MediaTek MT8127 SoCs support"
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -301,4 +301,15 @@
+ To compile this driver as a module, choose M here: the
+ module will be called vmd.
+
++config PCIE_MTK
++ bool "Mediatek PCIe Controller"
++ depends on MACH_MT2701 || MACH_MT7623
++ depends on OF
++ depends on PCI
++ help
++ Say Y here if you want to enable PCI controller support on Mediatek MT7623.
++ MT7623 PCIe supports single Root complex (RC) with 3 Root Ports.
++ Each port supports a Gen2 1-lan Link.
++ PCIe include one Host/PCI bridge and 3 PCIe MAC.
++
+ endmenu
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -33,3 +33,4 @@
+ obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
+ obj-$(CONFIG_VMD) += vmd.o
++obj-$(CONFIG_PCIE_MTK) += pcie-mediatek.o
+--- /dev/null
++++ b/drivers/pci/host/pcie-mediatek.c
+@@ -0,0 +1,641 @@
++/*
++ * Mediatek MT2701/MT7623 SoC PCIE support
++ *
++ * Copyright (C) 2015 Mediatek
++ * Copyright (C) 2015 Ziv Huang <ziv.huang@mediatek.com>
++ * Copyright (C) 2015 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.
++ */
++
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/delay.h>
++#include <asm/irq.h>
++#include <asm/mach/pci.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_pci.h>
++#include <linux/of_platform.h>
++#include <linux/of_irq.h>
++#include <linux/reset.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/pm_runtime.h>
++#include <linux/clk.h>
++#include <linux/regmap.h>
++#include <linux/mfd/syscon.h>
++
++#define MEMORY_BASE 0x80000000
++
++/* PCIE Registers */
++#define PCICFG 0x00
++#define PCIINT 0x08
++#define PCIENA 0x0c
++#define CFGADDR 0x20
++#define CFGDATA 0x24
++#define MEMBASE 0x28
++#define IOBASE 0x2c
++
++/* per Port Registers */
++#define BAR0SETUP 0x10
++#define IMBASEBAR0 0x18
++#define PCIE_CLASS 0x34
++#define PCIE_SISTAT 0x50
++
++#define MTK_PCIE_HIGH_PERF BIT(14)
++#define PCIEP0_BASE 0x2000
++#define PCIEP1_BASE 0x3000
++#define PCIEP2_BASE 0x4000
++
++#define PHY_P0_CTL 0x9000
++#define PHY_P1_CTL 0xa000
++#define PHY_P2_CTL 0x4000
++
++#define RSTCTL_PCIE0_RST BIT(24)
++#define RSTCTL_PCIE1_RST BIT(25)
++#define RSTCTL_PCIE2_RST BIT(26)
++
++#define HIFSYS_SYSCFG1 0x14
++#define HIFSYS_SYSCFG1_PHY2_MASK (0x3 << 20)
++
++#define MTK_PHY_CLK 0xb00
++#define MTK_PHY_CLKDRV_OFFSET BIT(2)
++#define MTK_PHY_CLKDRV_OFFSET_MASK 0xe
++#define MTK_PHY_PLL 0xb04
++#define MTK_PHY_CLKDRV_AMP BIT(30)
++#define MTK_PHY_CLKDRV_AMP_MASK 0xe0000000
++#define MTK_PHY_REFCLK_SEL 0xc00
++#define MTK_PHY_XTAL_EXT_EN (BIT(17) | BIT(12))
++#define MTK_PHY_XTAL_EXT_EN_MASK 0x33000
++#define MTK_PHY_PLL_BC 0xc08
++#define MTK_PHY_PLL_BC_PE2H 0xc0
++#define MTK_PHY_PLL_BC_PE2H_MASK 0x380000
++#define MTK_PHY_PLL_IC 0xc0c
++#define MTK_PHY_PLL_IC_BR_PE2H BIT(28)
++#define MTK_PHY_PLL_IC_BR_PE2H_MASK 0x30000000
++#define MTK_PHY_PLL_IC_PE2H BIT(12)
++#define MTK_PHY_PLL_IC_PE2H_MASK 0xf000
++#define MTK_PHY_PLL_IR 0xc10
++#define MTK_PHY_PLL_IR_PE2H BIT(17)
++#define MTK_PHY_PLL_IR_PE2H_MASK 0xf0000
++#define MTK_PHY_PLL_BP 0xc14
++#define MTK_PHY_PLL_BP_PE2H (BIT(19) | BIT(17))
++#define MTK_PHY_PLL_BP_PE2H_MASK 0xf0000
++#define MTK_PHY_SSC_DELTA1 0xc3c
++#define MTK_PHY_SSC_DELTA1_PE2H (0x3c << 16)
++#define MTK_PHY_SSC_DELTA1_PE2H_MASK 0xffff0000
++#define MTK_PHY_SSC_DELTA 0xc48
++#define MTK_PHY_SSC_DELTA_PE2H 0x36
++#define MTK_PHY_SSC_DELTA_PE2H_MASK 0xffff
++
++#define MAX_PORT_NUM 3
++
++struct mtk_pcie_port {
++ int id;
++ int enable;
++ int irq;
++ u32 link;
++ void __iomem *phy_base;
++ struct reset_control *rstc;
++};
++
++#define mtk_foreach_port(pcie, p) \
++ for ((p) = pcie->port; \
++ (p) != &pcie->port[MAX_PORT_NUM]; (p)++)
++
++struct mtk_pcie {
++ struct device *dev;
++ void __iomem *pcie_base;
++ struct regmap *hifsys;
++
++ struct resource io;
++ struct resource pio;
++ struct resource mem;
++ struct resource prefetch;
++ struct resource busn;
++
++ u32 io_bus_addr;
++ u32 mem_bus_addr;
++
++ struct clk *clk;
++
++ struct mtk_pcie_port port[MAX_PORT_NUM];
++ int pcie_card_link;
++};
++
++static struct mtk_pcie_port_data {
++ u32 base;
++ u32 perst_n;
++ u32 interrupt_en;
++} mtk_pcie_port_data[MAX_PORT_NUM] = {
++ { PCIEP0_BASE, BIT(1), BIT(20) },
++ { PCIEP1_BASE, BIT(2), BIT(21) },
++ { PCIEP2_BASE, BIT(3), BIT(22) },
++};
++
++static const struct mtk_phy_init {
++ uint32_t reg;
++ uint32_t mask;
++ uint32_t val;
++} mtk_phy_init[] = {
++ { MTK_PHY_REFCLK_SEL, MTK_PHY_XTAL_EXT_EN_MASK, MTK_PHY_XTAL_EXT_EN },
++ { MTK_PHY_PLL, MTK_PHY_CLKDRV_AMP_MASK, MTK_PHY_CLKDRV_AMP },
++ { MTK_PHY_CLK, MTK_PHY_CLKDRV_OFFSET_MASK, MTK_PHY_CLKDRV_OFFSET },
++ { MTK_PHY_SSC_DELTA1, MTK_PHY_SSC_DELTA1_PE2H_MASK, MTK_PHY_SSC_DELTA1_PE2H },
++ { MTK_PHY_SSC_DELTA, MTK_PHY_SSC_DELTA_PE2H_MASK, MTK_PHY_SSC_DELTA_PE2H },
++ { MTK_PHY_PLL_IC, MTK_PHY_PLL_IC_BR_PE2H_MASK, MTK_PHY_PLL_IC_BR_PE2H },
++ { MTK_PHY_PLL_BC, MTK_PHY_PLL_BC_PE2H_MASK, MTK_PHY_PLL_BC_PE2H },
++ { MTK_PHY_PLL_IR, MTK_PHY_PLL_IR_PE2H_MASK, MTK_PHY_PLL_IR_PE2H },
++ { MTK_PHY_PLL_IC, MTK_PHY_PLL_IC_PE2H_MASK, MTK_PHY_PLL_IC_PE2H },
++ { MTK_PHY_PLL_BP, MTK_PHY_PLL_BP_PE2H_MASK, MTK_PHY_PLL_BP_PE2H },
++};
++
++static struct mtk_pcie *sys_to_pcie(struct pci_sys_data *sys)
++{
++ return sys->private_data;
++}
++
++static void pcie_w32(struct mtk_pcie *pcie, u32 val, unsigned reg)
++{
++ iowrite32(val, pcie->pcie_base + reg);
++}
++
++static u32 pcie_r32(struct mtk_pcie *pcie, unsigned reg)
++{
++ return ioread32(pcie->pcie_base + reg);
++}
++
++static void pcie_m32(struct mtk_pcie *pcie, u32 mask, u32 val, unsigned reg)
++{
++ u32 v = pcie_r32(pcie, reg);
++
++ v &= mask;
++ v |= val;
++ pcie_w32(pcie, v, reg);
++}
++
++static int pcie_config_read(struct pci_bus *bus, unsigned int devfn, int where,
++ int size, u32 *val)
++{
++ struct mtk_pcie *pcie = sys_to_pcie(bus->sysdata);
++ unsigned int slot = PCI_SLOT(devfn);
++ u8 func = PCI_FUNC(devfn);
++ u32 address;
++ u32 data;
++ u32 num = 0;
++
++ if (bus)
++ num = bus->number;
++
++ address = (((where & 0xf00) >> 8) << 24) |
++ (num << 16) |
++ (slot << 11) |
++ (func << 8) |
++ (where & 0xfc);
++
++ pcie_w32(pcie, address, CFGADDR);
++ data = pcie_r32(pcie, CFGDATA);
++
++ switch (size) {
++ case 1:
++ *val = (data >> ((where & 3) << 3)) & 0xff;
++ break;
++ case 2:
++ *val = (data >> ((where & 3) << 3)) & 0xffff;
++ break;
++ case 4:
++ *val = data;
++ break;
++ }
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++static int pcie_config_write(struct pci_bus *bus, unsigned int devfn, int where,
++ int size, u32 val)
++{
++ struct mtk_pcie *pcie = sys_to_pcie(bus->sysdata);
++ unsigned int slot = PCI_SLOT(devfn);
++ u8 func = PCI_FUNC(devfn);
++ u32 address;
++ u32 data;
++ u32 num = 0;
++
++ if (bus)
++ num = bus->number;
++
++ address = (((where & 0xf00) >> 8) << 24) |
++ (num << 16) | (slot << 11) | (func << 8) | (where & 0xfc);
++ pcie_w32(pcie, address, CFGADDR);
++ data = pcie_r32(pcie, CFGDATA);
++
++ switch (size) {
++ case 1:
++ data = (data & ~(0xff << ((where & 3) << 3))) |
++ (val << ((where & 3) << 3));
++ break;
++ case 2:
++ data = (data & ~(0xffff << ((where & 3) << 3))) |
++ (val << ((where & 3) << 3));
++ break;
++ case 4:
++ data = val;
++ break;
++ }
++ pcie_w32(pcie, data, CFGDATA);
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++static struct pci_ops mtk_pcie_ops = {
++ .read = pcie_config_read,
++ .write = pcie_config_write,
++};
++
++static int __init mtk_pcie_setup(int nr, struct pci_sys_data *sys)
++{
++ struct mtk_pcie *pcie = sys_to_pcie(sys);
++
++ request_resource(&ioport_resource, &pcie->pio);
++ request_resource(&iomem_resource, &pcie->mem);
++
++ pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
++ pci_add_resource_offset(&sys->resources, &pcie->pio, sys->io_offset);
++ pci_add_resource(&sys->resources, &pcie->busn);
++
++ return 1;
++}
++
++static struct pci_bus * __init mtk_pcie_scan_bus(int nr,
++ struct pci_sys_data *sys)
++{
++ struct mtk_pcie *pcie = sys_to_pcie(sys);
++ struct pci_bus *bus;
++
++ bus = pci_create_root_bus(pcie->dev, sys->busnr, &mtk_pcie_ops, sys,
++ &sys->resources);
++ if (!bus)
++ return NULL;
++
++ pci_scan_child_bus(bus);
++
++ return bus;
++}
++
++static int __init mtk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
++{
++ struct mtk_pcie *pcie = sys_to_pcie(dev->bus->sysdata);
++ struct mtk_pcie_port *port;
++ int irq = -1;
++
++ mtk_foreach_port(pcie, port)
++ if (port->id == slot)
++ irq = port->irq;
++
++ return irq;
++}
++
++static void mtk_pcie_configure_phy(struct mtk_pcie *pcie,
++ struct mtk_pcie_port *port)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(mtk_phy_init); i++) {
++ void __iomem *phy_addr = port->phy_base + mtk_phy_init[i].reg;
++ u32 val = ioread32(phy_addr);
++
++ val &= ~mtk_phy_init[i].mask;
++ val |= mtk_phy_init[i].val;
++ iowrite32(val, phy_addr);
++ }
++ usleep_range(5000, 6000);
++}
++
++static void mtk_pcie_configure_rc(struct mtk_pcie *pcie,
++ struct mtk_pcie_port *port,
++ struct pci_bus *bus)
++{
++ u32 val = 0;
++
++ pcie_config_write(bus,
++ port->id << 3,
++ PCI_BASE_ADDRESS_0, 4, MEMORY_BASE);
++
++ pcie_config_read(bus,
++ port->id << 3, PCI_BASE_ADDRESS_0, 4, &val);
++
++ /* Configure RC Credit */
++ pcie_config_read(bus, port->id << 3, 0x73c, 4, &val);
++ val &= ~(0x9fff) << 16;
++ val |= 0x806c << 16;
++ pcie_config_write(bus, port->id << 3, 0x73c, 4, val);
++
++ /* Configure RC FTS number */
++ pcie_config_read(bus, port->id << 3, 0x70c, 4, &val);
++ val &= ~(0xff3) << 8;
++ val |= 0x50 << 8;
++ pcie_config_write(bus, port->id << 3, 0x70c, 4, val);
++}
++
++static int mtk_pcie_preinit(struct mtk_pcie *pcie)
++{
++ struct mtk_pcie_port *port;
++ u32 val = 0;
++ struct pci_bus bus;
++ struct pci_sys_data sys;
++
++ memset(&bus, 0, sizeof(bus));
++ memset(&sys, 0, sizeof(sys));
++ bus.sysdata = (void *)&sys;
++ sys.private_data = (void *)pcie;
++
++ pcibios_min_io = 0;
++ pcibios_min_mem = 0;
++
++ /* The PHY on Port 2 is shared with USB */
++ if (pcie->port[2].enable)
++ regmap_update_bits(pcie->hifsys, HIFSYS_SYSCFG1,
++ HIFSYS_SYSCFG1_PHY2_MASK, 0x0);
++
++ /* PCIe RC Reset */
++ mtk_foreach_port(pcie, port)
++ if (port->enable)
++ reset_control_assert(port->rstc);
++ usleep_range(1000, 2000);
++ mtk_foreach_port(pcie, port)
++ if (port->enable)
++ reset_control_deassert(port->rstc);
++ usleep_range(1000, 2000);
++
++ /* Configure PCIe PHY */
++ mtk_foreach_port(pcie, port)
++ if (port->enable)
++ mtk_pcie_configure_phy(pcie, port);
++
++ /* PCIe EP reset */
++ val = 0;
++ mtk_foreach_port(pcie, port)
++ if (port->enable)
++ val |= mtk_pcie_port_data[port->id].perst_n;
++ pcie_w32(pcie, pcie_r32(pcie, PCICFG) | val, PCICFG);
++ usleep_range(1000, 2000);
++ pcie_w32(pcie, pcie_r32(pcie, PCICFG) & ~val, PCICFG);
++ usleep_range(1000, 2000);
++ msleep(100);
++
++ /* check the link status */
++ val = 0;
++ mtk_foreach_port(pcie, port) {
++ if (port->enable) {
++ u32 base = mtk_pcie_port_data[port->id].base;
++
++ if ((pcie_r32(pcie, base + PCIE_SISTAT) & 0x1))
++ port->link = 1;
++ else
++ reset_control_assert(port->rstc);
++ }
++ }
++
++ mtk_foreach_port(pcie, port)
++ if (port->link)
++ pcie->pcie_card_link++;
++
++ if (!pcie->pcie_card_link)
++ return -ENODEV;
++
++ pcie_w32(pcie, pcie->mem_bus_addr, MEMBASE);
++ pcie_w32(pcie, pcie->io_bus_addr, IOBASE);
++
++ mtk_foreach_port(pcie, port) {
++ if (port->link) {
++ u32 base = mtk_pcie_port_data[port->id].base;
++ u32 inte = mtk_pcie_port_data[port->id].interrupt_en;
++
++ pcie_m32(pcie, 0, inte, PCIENA);
++ pcie_w32(pcie, 0x7fff0001, base + BAR0SETUP);
++ pcie_w32(pcie, MEMORY_BASE, base + IMBASEBAR0);
++ pcie_w32(pcie, 0x06040001, base + PCIE_CLASS);
++ }
++ }
++
++ mtk_foreach_port(pcie, port)
++ if (port->link)
++ mtk_pcie_configure_rc(pcie, port, &bus);
++
++ return 0;
++}
++
++static int mtk_pcie_parse_dt(struct mtk_pcie *pcie)
++{
++ struct device_node *np = pcie->dev->of_node, *port;
++ struct of_pci_range_parser parser;
++ struct of_pci_range range;
++ struct resource res;
++ int err;
++
++ pcie->hifsys = syscon_regmap_lookup_by_phandle(np, "mediatek,hifsys");
++ if (IS_ERR(pcie->hifsys)) {
++ dev_err(pcie->dev, "missing \"mediatek,hifsys\" phandle\n");
++ return PTR_ERR(pcie->hifsys);
++ }
++
++ if (of_pci_range_parser_init(&parser, np)) {
++ dev_err(pcie->dev, "missing \"ranges\" property\n");
++ return -EINVAL;
++ }
++
++ for_each_of_pci_range(&parser, &range) {
++ err = of_pci_range_to_resource(&range, np, &res);
++ if (err < 0) {
++ dev_err(pcie->dev, "failed to read resource range\n");
++ return err;
++ }
++
++ switch (res.flags & IORESOURCE_TYPE_BITS) {
++ case IORESOURCE_IO:
++ memcpy(&pcie->pio, &res, sizeof(res));
++ pcie->pio.start = (resource_size_t)range.pci_addr;
++ pcie->pio.end = (resource_size_t)
++ (range.pci_addr + range.size - 1);
++ pcie->io_bus_addr = (resource_size_t)range.cpu_addr;
++ break;
++
++ case IORESOURCE_MEM:
++ if (res.flags & IORESOURCE_PREFETCH) {
++ memcpy(&pcie->prefetch, &res, sizeof(res));
++ pcie->prefetch.name = "prefetchable";
++ pcie->prefetch.start =
++ (resource_size_t)range.pci_addr;
++ pcie->prefetch.end = (resource_size_t)
++ (range.pci_addr + range.size - 1);
++ } else {
++ memcpy(&pcie->mem, &res, sizeof(res));
++ pcie->mem.name = "non-prefetchable";
++ pcie->mem.start = (resource_size_t)
++ range.pci_addr;
++ pcie->prefetch.end = (resource_size_t)
++ (range.pci_addr + range.size - 1);
++ pcie->mem_bus_addr = (resource_size_t)
++ range.cpu_addr;
++ }
++ break;
++ }
++ }
++
++ err = of_pci_parse_bus_range(np, &pcie->busn);
++ if (err < 0) {
++ dev_err(pcie->dev, "failed to parse ranges property: %d\n",
++ err);
++ pcie->busn.name = np->name;
++ pcie->busn.start = 0;
++ pcie->busn.end = 0xff;
++ pcie->busn.flags = IORESOURCE_BUS;
++ }
++
++ /* parse root ports */
++ for_each_child_of_node(np, port) {
++ unsigned int index;
++ char rst[] = "pcie0";
++
++ err = of_pci_get_devfn(port);
++ if (err < 0) {
++ dev_err(pcie->dev, "failed to parse address: %d\n",
++ err);
++ return err;
++ }
++
++ index = PCI_SLOT(err);
++ if (index > MAX_PORT_NUM) {
++ dev_err(pcie->dev, "invalid port number: %d\n", index);
++ continue;
++ }
++ index--;
++ pcie->port[index].id = index;
++
++ if (!of_device_is_available(port))
++ continue;
++
++ rst[4] += index;
++ pcie->port[index].rstc = devm_reset_control_get(pcie->dev,
++ rst);
++ if (!IS_ERR(pcie->port[index].rstc))
++ pcie->port[index].enable = 1;
++ }
++ return 0;
++}
++
++static int mtk_pcie_get_resources(struct mtk_pcie *pcie)
++{
++ struct platform_device *pdev = to_platform_device(pcie->dev);
++ struct mtk_pcie_port *port;
++ struct resource *res;
++
++ pcie->clk = devm_clk_get(&pdev->dev, "pcie");
++ if (IS_ERR(pcie->clk)) {
++ dev_err(&pdev->dev, "Failed to get pcie clk\n");
++ return PTR_ERR(pcie->clk);
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ pcie->pcie_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(pcie->pcie_base)) {
++ dev_err(&pdev->dev, "Failed to get pcie range\n");
++ return PTR_ERR(pcie->pcie_base);
++ }
++
++ mtk_foreach_port(pcie, port) {
++ if (!port->enable)
++ continue;
++ res = platform_get_resource(pdev, IORESOURCE_MEM, port->id + 1);
++ port->phy_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(port->phy_base)) {
++ dev_err(&pdev->dev, "Failed to get pcie phy%d range %p\n",
++ port->id, port->phy_base);
++ return PTR_ERR(port->phy_base);
++ }
++ port->irq = platform_get_irq(pdev, port->id);
++ }
++
++ return clk_prepare_enable(pcie->clk);
++}
++
++static int mtk_pcie_probe(struct platform_device *pdev)
++{
++ struct mtk_pcie *pcie;
++ struct hw_pci hw;
++ int ret;
++
++ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
++ if (!pcie)
++ return -ENOMEM;
++
++ pcie->dev = &pdev->dev;
++ ret = mtk_pcie_parse_dt(pcie);
++ if (ret < 0)
++ return ret;
++
++ pm_runtime_enable(&pdev->dev);
++ pm_runtime_get_sync(&pdev->dev);
++
++ ret = mtk_pcie_get_resources(pcie);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "failed to request resources: %d\n", ret);
++ goto err_out;
++ }
++
++ ret = mtk_pcie_preinit(pcie);
++ if (ret)
++ return ret;
++
++ memset(&hw, 0, sizeof(hw));
++ hw.nr_controllers = 1;
++ hw.private_data = (void **)&pcie;
++ hw.setup = mtk_pcie_setup;
++ hw.map_irq = mtk_pcie_map_irq;
++ hw.scan = mtk_pcie_scan_bus;
++
++ pci_common_init_dev(pcie->dev, &hw);
++ platform_set_drvdata(pdev, pcie);
++
++ return 0;
++
++err_out:
++ clk_disable_unprepare(pcie->clk);
++ pm_runtime_put_sync(&pdev->dev);
++ pm_runtime_disable(&pdev->dev);
++
++ return ret;
++}
++
++static const struct of_device_id mtk_pcie_ids[] = {
++ { .compatible = "mediatek,mt2701-pcie" },
++ { .compatible = "mediatek,mt7623-pcie" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, mtk_pcie_ids);
++
++static struct platform_driver mtk_pcie_driver = {
++ .probe = mtk_pcie_probe,
++ .driver = {
++ .name = "mediatek-pcie",
++ .owner = THIS_MODULE,
++ .of_match_table = of_match_ptr(mtk_pcie_ids),
++ },
++};
++
++static int __init mtk_pcie_init(void)
++{
++ return platform_driver_register(&mtk_pcie_driver);
++}
++
++module_init(mtk_pcie_init);
diff --git a/target/linux/mediatek/patches-4.9/0026-scpsys-various-fixes.patch b/target/linux/mediatek/patches-4.9/0026-scpsys-various-fixes.patch
new file mode 100644
index 0000000000..b5047f76b8
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0026-scpsys-various-fixes.patch
@@ -0,0 +1,22 @@
+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 026/102] scpsys: various fixes
+
+---
+ drivers/clk/mediatek/clk-mt2701.c | 2 ++
+ drivers/soc/mediatek/mtk-scpsys-mt2701.c | 8 --------
+ include/dt-bindings/power/mt2701-power.h | 4 ++--
+ 3 files changed, 4 insertions(+), 10 deletions(-)
+
+--- a/drivers/clk/mediatek/clk-mt2701.c
++++ b/drivers/clk/mediatek/clk-mt2701.c
+@@ -1043,6 +1043,8 @@
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
++
++ mtk_register_reset_controller(node, 1, 0x34);
+ }
+ CLK_OF_DECLARE(mtk_ethsys, "mediatek,mt2701-ethsys", mtk_ethsys_init);
+
diff --git a/target/linux/mediatek/patches-4.9/0052-clk-dont-disable-unused-clocks.patch b/target/linux/mediatek/patches-4.9/0052-clk-dont-disable-unused-clocks.patch
new file mode 100644
index 0000000000..87e4a54ed3
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0052-clk-dont-disable-unused-clocks.patch
@@ -0,0 +1,21 @@
+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(-)
+
+--- 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.9/0053-clk-mediatek-enable-critical-clocks.patch b/target/linux/mediatek/patches-4.9/0053-clk-mediatek-enable-critical-clocks.patch
new file mode 100644
index 0000000000..39939001f1
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0053-clk-mediatek-enable-critical-clocks.patch
@@ -0,0 +1,69 @@
+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(-)
+
+--- 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.9/0054-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch b/target/linux/mediatek/patches-4.9/0054-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch
new file mode 100644
index 0000000000..2ff6990d10
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0054-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch
@@ -0,0 +1,287 @@
+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
+
+--- 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_mtk_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_mtk_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_mtk_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 @@
+ "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 @@
+ 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>
+
+@@ -525,6 +526,25 @@
+ "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),
+@@ -948,6 +968,9 @@
+ 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
+@@ -221,7 +221,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
+@@ -193,7 +193,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.9/0055-cpufreq-mediatek-add-driver.patch b/target/linux/mediatek/patches-4.9/0055-cpufreq-mediatek-add-driver.patch
new file mode 100644
index 0000000000..8b476361ff
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0055-cpufreq-mediatek-add-driver.patch
@@ -0,0 +1,433 @@
+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
+
+--- 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.9/0071-pwm-add-pwm-mediatek.patch b/target/linux/mediatek/patches-4.9/0071-pwm-add-pwm-mediatek.patch
new file mode 100644
index 0000000000..75796ab0fa
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0071-pwm-add-pwm-mediatek.patch
@@ -0,0 +1,304 @@
+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
+
+--- a/arch/arm/boot/dts/mt7623-evb.dts
++++ b/arch/arm/boot/dts/mt7623-evb.dts
+@@ -26,8 +26,25 @@
+ memory {
+ reg = <0 0x80000000 0 0x40000000>;
+ };
++/*
++ pwm_pins: pwm {
++ pins_pwm1 {
++ pinmux = <MT7623_PIN_204_PWM1_FUNC_PWM1>;
++ };
++
++ pins_pwm2 {
++ pinmux = <MT7623_PIN_205_PWM2_FUNC_PWM2>;
++ };
++ };*/
++
+ };
+
+ &uart2 {
+ status = "okay";
+ };
++
++/*&pwm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_pins>;
++ status = "okay";
++};*/
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -282,6 +282,15 @@
+ 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
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -25,6 +25,7 @@
+ obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o
+ obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o
+ obj-$(CONFIG_PWM_MESON) += pwm-meson.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_OMAP_DMTIMER) += pwm-omap-dmtimer.o
+--- /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");
diff --git a/target/linux/mediatek/patches-4.9/0101-net-mediatek-add-gsw-mt7530-driver.patch b/target/linux/mediatek/patches-4.9/0101-net-mediatek-add-gsw-mt7530-driver.patch
new file mode 100644
index 0000000000..a90f49055e
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0101-net-mediatek-add-gsw-mt7530-driver.patch
@@ -0,0 +1,2322 @@
+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
+
+--- 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@nbd.name>
++ * 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@nbd.name>
++ * 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 && mdiobus_get_phy(eth->mii_bus, 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
+@@ -25,6 +25,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)");
+@@ -74,14 +77,14 @@
+ 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");
+ return -1;
+ }
+
+-static u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
++u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
+ u32 phy_register, u32 write_data)
+ {
+ if (mtk_mdio_busy_wait(eth))
+@@ -100,7 +103,7 @@
+ return 0;
+ }
+
+-static u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg)
++u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg)
+ {
+ u32 d;
+
+@@ -155,7 +158,7 @@
+
+ val = (speed == SPEED_1000) ?
+ RCK_CTRL_RGMII_1000 : RCK_CTRL_RGMII_10_100;
+- mtk_w32(eth, val, TRGMII_RCK_CTRL);
++ mtk_w32(eth, val, _TRGMII_RCK_CTRL);
+
+ val = (speed == SPEED_1000) ?
+ TCK_CTRL_RGMII_1000 : TCK_CTRL_RGMII_10_100;
+@@ -1833,15 +1836,6 @@
+ }
+ regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
+
+- /* 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));
+
+@@ -1851,6 +1845,8 @@
+ /* Enable RX VLan Offloading */
+ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+
++ mtk_gsw_init(eth);
++
+ /* disable delay and normal interrupt */
+ mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ mtk_w32(eth, 0, MTK_PDMA_DELAY_INT);
+@@ -1879,6 +1875,8 @@
+ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+ }
+
++ mt7623_gsw_config(eth);
++
+ return 0;
+ }
+
+@@ -2379,6 +2377,9 @@
+ if (!eth)
+ return -ENOMEM;
+
++ eth->switch_np = of_parse_phandle(pdev->dev.of_node,
++ "mediatek,switch", 0);
++
+ eth->dev = &pdev->dev;
+ eth->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(eth->base))
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -314,7 +314,7 @@
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_LINK)
+
+ /* TRGMII RXC control register */
+-#define TRGMII_RCK_CTRL 0x10300
++#define _TRGMII_RCK_CTRL 0x10300
+ #define DQSI0(x) ((x << 0) & GENMASK(6, 0))
+ #define DQSI1(x) ((x << 8) & GENMASK(14, 8))
+ #define RXCTL_DMWTLAT(x) ((x << 16) & GENMASK(18, 16))
+@@ -554,6 +554,9 @@
+ struct mii_bus *mii_bus;
+ struct work_struct pending_work;
+ unsigned long state;
++
++ struct device_node *switch_np;
++ void *sw_priv;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
+@@ -586,4 +589,6 @@
+ 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.9/0103-nand_fixes.patch b/target/linux/mediatek/patches-4.9/0103-nand_fixes.patch
new file mode 100644
index 0000000000..92f34c5fc2
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0103-nand_fixes.patch
@@ -0,0 +1,22 @@
+--- a/drivers/mtd/nand/mtk_nand.c
++++ b/drivers/mtd/nand/mtk_nand.c
+@@ -1017,8 +1017,8 @@ static int mtk_nfc_ooblayout_free(struct
+ if (section >= eccsteps)
+ return -ERANGE;
+
+- oob_region->length = fdm->reg_size - fdm->ecc_size;
+- oob_region->offset = section * fdm->reg_size + fdm->ecc_size;
++ oob_region->length = fdm->reg_size - 1;
++ oob_region->offset = section * fdm->reg_size + 1;
+
+ return 0;
+ }
+@@ -1058,7 +1058,7 @@ static void mtk_nfc_set_fdm(struct mtk_n
+ fdm->reg_size = NFI_FDM_MAX_SIZE;
+
+ /* bad block mark storage */
+- fdm->ecc_size = 1;
++ fdm->ecc_size = fdm->reg_size;
+ }
+
+ static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl,
diff --git a/target/linux/mediatek/patches-4.9/0200-devicetree.patch b/target/linux/mediatek/patches-4.9/0200-devicetree.patch
new file mode 100644
index 0000000000..eb743c7247
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0200-devicetree.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -775,6 +775,8 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
+ mt6589-aquaris5.dtb \
+ mt6592-evb.dtb \
+ mt7623-evb.dtb \
++ mt7623-eMMC.dtb \
++ mt7623-NAND.dtb \
+ mt8127-moose.dtb \
+ mt8135-evbp1.dtb
+ dtb-$(CONFIG_ARCH_ZX) += zx296702-ad1.dtb
diff --git a/target/linux/mediatek/patches-4.9/0201-block2mtd.patch b/target/linux/mediatek/patches-4.9/0201-block2mtd.patch
new file mode 100644
index 0000000000..395884b730
--- /dev/null
+++ b/target/linux/mediatek/patches-4.9/0201-block2mtd.patch
@@ -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;
+ }