diff options
author | John Crispin <john@phrozen.org> | 2017-02-16 09:53:03 +0100 |
---|---|---|
committer | John Crispin <john@phrozen.org> | 2017-02-16 09:53:30 +0100 |
commit | 53f5d59fa17049d94a3992d1067ded1fa90f61f8 (patch) | |
tree | 5596104b74bfe2a96f1abdb2303314a40c74322b /target/linux/mediatek/patches-4.9 | |
parent | f885edef5a3bfcddcfe85732ee65c1d475c4a8f6 (diff) | |
download | upstream-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')
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 = ð_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; + } |