summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-11-05 15:47:39 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2014-11-05 15:47:39 +0100
commit7b4cc54579f12cc6c9586e8c21e729dd220e7f45 (patch)
tree85adc78e0eb782f805113b2b48dd07be6555e532 /drivers
parent254b64520b9a729da496cd8bf637d080de7af5a1 (diff)
parentc202b7c8d9e66082853ac1b131ddcedf53e9ca99 (diff)
downloadbarebox-7b4cc54579f12cc6c9586e8c21e729dd220e7f45.tar.gz
barebox-7b4cc54579f12cc6c9586e8c21e729dd220e7f45.tar.xz
Merge branch 'for-next/tegra'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/clk/tegra/clk-pll.c287
-rw-r--r--drivers/clk/tegra/clk-tegra124.c45
-rw-r--r--drivers/clk/tegra/clk-tegra20.c10
-rw-r--r--drivers/clk/tegra/clk-tegra30.c43
-rw-r--r--drivers/clk/tegra/clk.h13
-rw-r--r--drivers/i2c/busses/i2c-tegra.c2
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/rtl8169.c578
-rw-r--r--drivers/of/Kconfig1
-rw-r--r--drivers/of/address.c68
-rw-r--r--drivers/pci/Kconfig7
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/bus.c3
-rw-r--r--drivers/pci/pci-tegra.c1305
-rw-r--r--drivers/pci/pci.c276
-rw-r--r--drivers/phy/Kconfig18
-rw-r--r--drivers/phy/Makefile5
-rw-r--r--drivers/phy/phy-core.c318
-rw-r--r--drivers/pinctrl/Kconfig8
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-tegra-xusb.c519
-rw-r--r--drivers/pinctrl/pinctrl-tegra20.c8
-rw-r--r--drivers/pinctrl/pinctrl-tegra30.c8
-rw-r--r--drivers/serial/serial_ns16550.c12
27 files changed, 3454 insertions, 93 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index e126f62c8f..5984ccca2c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -30,5 +30,6 @@ source "drivers/reset/Kconfig"
source "drivers/pci/Kconfig"
source "drivers/rtc/Kconfig"
source "drivers/firmware/Kconfig"
+source "drivers/phy/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index cf42190b7b..7ef5e90d80 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_RESET_CONTROLLER) += reset/
obj-$(CONFIG_PCI) += pci/
obj-y += rtc/
obj-$(CONFIG_FIRMWARE) += firmware/
+obj-$(CONFIG_GENERIC_PHY) += phy/
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index c18c67f705..e677effba2 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -17,12 +17,15 @@
*/
#include <common.h>
+#include <clock.h>
#include <io.h>
#include <malloc.h>
#include <asm-generic/div64.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <mach/iomap.h>
+
#include "clk.h"
#define PLL_BASE_BYPASS BIT(31)
@@ -60,6 +63,7 @@
#define PLLDU_LFCON_SET_DIVN 600
#define PLLE_BASE_DIVCML_SHIFT 24
+#define PLLE_BASE_DIVCML_MASK 0xf
#define PLLE_BASE_DIVCML_WIDTH 4
#define PLLE_BASE_DIVP_SHIFT 16
#define PLLE_BASE_DIVP_WIDTH 7
@@ -78,8 +82,45 @@
PLLE_MISC_SETUP_EX_MASK)
#define PLLE_MISC_SETUP_VALUE (7 << PLLE_MISC_SETUP_BASE_SHIFT)
-#define PLLE_SS_CTRL 0x68
-#define PLLE_SS_DISABLE (7 << 10)
+#define XUSBIO_PLL_CFG0 0x51c
+#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL (1 << 0)
+#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL (1 << 2)
+#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET (1 << 6)
+#define XUSBIO_PLL_CFG0_SEQ_ENABLE (1 << 24)
+#define XUSBIO_PLL_CFG0_SEQ_START_STATE (1 << 25)
+
+#define PLLE_SS_CTRL 0x68
+#define PLLE_SS_CNTL_BYPASS_SS (7 << 10)
+#define PLLE_SS_CNTL_INTERP_RESET (1 << 11)
+#define PLLE_SS_CNTL_SSC_BYP (1 << 12)
+#define PLLE_SS_CNTL_CENTER (1 << 14)
+#define PLLE_SS_CNTL_INVERT (1 << 15)
+#define PLLE_SS_MAX_MASK 0x1ff
+#define PLLE_SS_MAX_VAL 0x25
+#define PLLE_SS_INC_MASK (0xff << 16)
+#define PLLE_SS_INC_VAL (0x1 << 16)
+#define PLLE_SS_INCINTRV_MASK (0x3f << 24)
+#define PLLE_SS_INCINTRV_VAL (0x20 << 24)
+#define PLLE_SS_COEFFICIENTS_MASK \
+ (PLLE_SS_MAX_MASK | PLLE_SS_INC_MASK | PLLE_SS_INCINTRV_MASK)
+#define PLLE_SS_COEFFICIENTS_VAL \
+ (PLLE_SS_MAX_VAL | PLLE_SS_INC_VAL | PLLE_SS_INCINTRV_VAL)
+
+#define PLLE_MISC_VREG_CTRL_SHIFT 2
+#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT)
+#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4
+#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT)
+#define PLLE_MISC_PLLE_PTS (1 << 8)
+#define PLLE_MISC_IDDQ_SW_VALUE (1 << 13)
+#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14)
+
+#define PLLE_AUX_PLLP_SEL (1 << 2)
+#define PLLE_AUX_USE_LOCKDET (1 << 3)
+#define PLLE_AUX_ENABLE_SWCTL (1 << 4)
+#define PLLE_AUX_SS_SWCTL (1 << 6)
+#define PLLE_AUX_SEQ_ENABLE (1 << 24)
+#define PLLE_AUX_SEQ_START_STATE (1 << 25)
+#define PLLE_AUX_PLLRE_SEL (1 << 28)
#define PMC_SATA_PWRGT 0x1ac
#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5)
@@ -99,10 +140,19 @@
#define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \
mask(p->divp_width))
+#define divm_shift(p) (p)->divm_shift
+#define divn_shift(p) (p)->divn_shift
+#define divp_shift(p) (p)->divp_shift
+
+#define divm_mask_shifted(p) (divm_mask(p) << divm_shift(p))
+#define divn_mask_shifted(p) (divn_mask(p) << divn_shift(p))
+#define divp_mask_shifted(p) (divp_mask(p) << divp_shift(p))
+
#define divm_max(p) (divm_mask(p))
#define divn_max(p) (divn_mask(p))
#define divp_max(p) (1 << (divp_mask(p)))
+
#define to_clk_pll(_hw) container_of(_hw, struct tegra_clk_pll, hw)
static int clk_pll_is_enabled(struct clk *hw)
@@ -393,6 +443,217 @@ const struct clk_ops tegra_clk_pll_ops = {
.set_rate = clk_pll_set_rate,
};
+static unsigned long clk_plle_recalc_rate(struct clk *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val = pll_readl_base(pll);
+ u32 divn = 0, divm = 0, divp = 0;
+ u64 rate = parent_rate;
+
+ divp = (val >> 16) & 0x3f;
+ divn = (val >> 8) & (0xff);
+ divm = (val >> 0) & (0xff);
+ divm *= divp;
+
+ rate *= divn;
+ do_div(rate, divm);
+ return rate;
+}
+
+static int clk_plle_training(struct tegra_clk_pll *pll)
+{
+ u32 val;
+
+ /*
+ * PLLE is already disabled, and setup cleared;
+ * create falling edge on PLLE IDDQ input.
+ */
+ val = readl(TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ writel(val, TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+
+ val = readl(TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL;
+ writel(val, TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+
+ val = readl(TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+ val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ writel(val, TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+
+ return wait_on_timeout(100 * MSECOND,
+ (pll_readl_misc(pll) & PLLE_MISC_READY));
+}
+
+static int clk_plle_enable(struct clk *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long input_rate = clk_get_rate(clk_get_parent(hw));
+ struct tegra_clk_pll_freq_table sel;
+ u32 val;
+ int err;
+
+ if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
+ return -EINVAL;
+
+ clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
+ pll_writel_misc(val, pll);
+
+ val = pll_readl_misc(pll);
+ if (!(val & PLLE_MISC_READY)) {
+ err = clk_plle_training(pll);
+ if (err)
+ return err;
+ }
+
+ /* configure dividers */
+ val = pll_readl_base(pll);
+ val &= ~(0x3fffff);
+ val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT);
+ val |= sel.m << 0;
+ val |= sel.n << 8;
+ val |= sel.p << 16;
+ val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+ pll_writel_base(val, pll);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_SETUP_VALUE;
+ val |= PLLE_MISC_LOCK_ENABLE;
+ pll_writel_misc(val, pll);
+
+ val = readl(pll->clk_base + PLLE_SS_CTRL);
+ val |= PLLE_SS_CNTL_BYPASS_SS;
+ writel(val, pll->clk_base + PLLE_SS_CTRL);
+
+ val = pll_readl_base(pll);
+ val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE);
+ pll_writel_base(val, pll);
+
+ clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg,
+ pll->params->lock_bit_idx);
+
+ return 0;
+}
+
+const struct clk_ops tegra_clk_plle_ops = {
+ .recalc_rate = clk_plle_recalc_rate,
+ .is_enabled = clk_pll_is_enabled,
+ .disable = clk_pll_disable,
+ .enable = clk_plle_enable,
+};
+
+static int clk_plle_tegra114_enable(struct clk *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long input_rate = clk_get_rate(clk_get_parent(hw));
+ struct tegra_clk_pll_freq_table sel;
+ u32 val;
+ int ret;
+
+ if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
+ return -EINVAL;
+
+ val = pll_readl_base(pll);
+ val &= ~BIT(29); /* Disable lock override */
+ pll_writel_base(val, pll);
+
+ val = pll_readl(pll->params->aux_reg, pll);
+ val |= PLLE_AUX_ENABLE_SWCTL;
+ val &= ~PLLE_AUX_SEQ_ENABLE;
+ pll_writel(val, pll->params->aux_reg, pll);
+ udelay(1);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_LOCK_ENABLE;
+ val |= PLLE_MISC_IDDQ_SW_CTRL;
+ val &= ~PLLE_MISC_IDDQ_SW_VALUE;
+ val |= PLLE_MISC_PLLE_PTS;
+ val |= PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK;
+ pll_writel_misc(val, pll);
+ udelay(5);
+
+ val = pll_readl(PLLE_SS_CTRL, pll);
+ val |= PLLE_SS_CNTL_BYPASS_SS;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+
+ val = pll_readl_base(pll);
+ val &= ~(divp_mask_shifted(pll) | divn_mask_shifted(pll) |
+ divm_mask_shifted(pll));
+ val &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT);
+ val |= sel.m << divm_shift(pll);
+ val |= sel.n << divn_shift(pll);
+ val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+ pll_writel_base(val, pll);
+ udelay(1);
+
+ clk_pll_enable(hw);
+ ret = clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->misc_reg,
+ pll->params->lock_bit_idx);
+
+ if (ret < 0)
+ return ret;
+
+ val = pll_readl(PLLE_SS_CTRL, pll);
+ val &= ~(PLLE_SS_CNTL_CENTER | PLLE_SS_CNTL_INVERT);
+ val &= ~PLLE_SS_COEFFICIENTS_MASK;
+ val |= PLLE_SS_COEFFICIENTS_VAL;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ val &= ~(PLLE_SS_CNTL_SSC_BYP | PLLE_SS_CNTL_BYPASS_SS);
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ udelay(1);
+ val &= ~PLLE_SS_CNTL_INTERP_RESET;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ udelay(1);
+
+ /* Enable hw control of xusb brick pll */
+ val = pll_readl_misc(pll);
+ val &= ~PLLE_MISC_IDDQ_SW_CTRL;
+ pll_writel_misc(val, pll);
+
+ val = pll_readl(pll->params->aux_reg, pll);
+ val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SEQ_START_STATE);
+ val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+ pll_writel(val, pll->params->aux_reg, pll);
+ udelay(1);
+ val |= PLLE_AUX_SEQ_ENABLE;
+ pll_writel(val, pll->params->aux_reg, pll);
+
+ val = pll_readl(XUSBIO_PLL_CFG0, pll);
+ val |= (XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET |
+ XUSBIO_PLL_CFG0_SEQ_START_STATE);
+ val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL |
+ XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL);
+ pll_writel(val, XUSBIO_PLL_CFG0, pll);
+ udelay(1);
+ val |= XUSBIO_PLL_CFG0_SEQ_ENABLE;
+ pll_writel(val, XUSBIO_PLL_CFG0, pll);
+
+ return ret;
+}
+
+static void clk_plle_tegra114_disable(struct clk *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE;
+ pll_writel_misc(val, pll);
+ udelay(1);
+}
+
+const struct clk_ops tegra_clk_plle_tegra114_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+ .is_enabled = clk_pll_is_enabled,
+ .disable = clk_plle_tegra114_disable,
+ .enable = clk_plle_tegra114_enable,
+};
+
static struct clk *_tegra_clk_register_pll(const char *name,
const char *parent_name, void __iomem *clk_base,
unsigned long flags, unsigned long fixed_rate,
@@ -447,3 +708,25 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
flags, fixed_rate, pll_params, pll_flags, freq_table,
&tegra_clk_pll_ops);
}
+
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+ void __iomem *clk_base,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u8 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table)
+{
+ return _tegra_clk_register_pll(name, parent_name, clk_base,
+ flags, fixed_rate, pll_params, pll_flags, freq_table,
+ &tegra_clk_plle_ops);
+}
+
+struct clk *tegra_clk_register_plle_tegra114(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u8 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table)
+{
+ return _tegra_clk_register_pll(name, parent_name, clk_base,
+ flags, fixed_rate, pll_params, pll_flags, freq_table,
+ &tegra_clk_plle_tegra114_ops);
+}
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index 514b22a784..7a2f7c081f 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -62,6 +62,15 @@ static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
{ 0, 0, 0, 0, 0, 0 },
};
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ {336000000, 100000000, 100, 21, 16, 11},
+ {312000000, 100000000, 200, 26, 24, 13},
+ {13000000, 100000000, 200, 1, 26, 13},
+ {12000000, 100000000, 200, 1, 24, 13},
+ {0, 0, 0, 0, 0, 0},
+};
+
static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
{12000000, 408000000, 408, 12, 0, 8},
{13000000, 408000000, 408, 13, 0, 8},
@@ -114,6 +123,21 @@ static struct tegra_clk_pll_params pll_c_params = {
.lock_delay = 300,
};
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 75000000,
+ .vco_min = 1600000000,
+ .vco_max = 2400000000U,
+ .base_reg = CRC_PLLE_BASE,
+ .misc_reg = CRC_PLLE_MISC,
+ .aux_reg = CRC_PLLE_AUX,
+ .lock_bit_idx = CRC_PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = CRC_PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
static struct tegra_clk_pll_params pll_p_params = {
.input_min = 2000000,
.input_max = 31000000,
@@ -220,6 +244,11 @@ static void tegra124_pll_init(void)
clks[TEGRA124_CLK_PLL_U] = tegra_clk_register_pll("pll_u", "pll_ref",
car_base, 0, 0, &pll_u_params, TEGRA_PLLU |
TEGRA_PLL_HAS_CPCON, pll_u_freq_table);
+
+ /* PLLE */
+ clks[TEGRA124_CLK_PLL_E] = tegra_clk_register_plle_tegra114("pll_e",
+ "pll_ref", car_base, 0, 100000000, &pll_e_params,
+ TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK, pll_e_freq_table);
}
static const char *mux_pllpcm_clkm[] = {"pll_p", "pll_c2", "pll_c", "pll_c3",
@@ -244,6 +273,12 @@ static void tegra124_periph_init(void)
mux_pllpcm_clkm, ARRAY_SIZE(mux_pllpcm_clkm), car_base,
CRC_CLK_SOURCE_UARTD, TEGRA124_CLK_UARTD,
TEGRA_PERIPH_ON_APB);
+ clks[TEGRA124_CLK_PCIE] = clk_gate("pcie", "clk_m",
+ car_base + CRC_CLK_OUT_ENB_U, 6, 0, 0);
+ clks[TEGRA124_CLK_AFI] = clk_gate("afi", "clk_m",
+ car_base + CRC_CLK_OUT_ENB_U, 8, 0, 0);
+ clks[TEGRA124_CLK_CML0] = clk_gate("cml0", "pll_e",
+ car_base + CRC_PLLE_AUX, 0, 0, 0);
/* peripheral clocks with a divider */
clks[TEGRA124_CLK_MSELECT] = tegra_clk_register_periph("mselect",
@@ -286,11 +321,11 @@ static struct tegra_clk_init_table init_table[] = {
{TEGRA124_CLK_PLL_P_OUT2, TEGRA124_CLK_CLK_MAX, 48000000, 1},
{TEGRA124_CLK_PLL_P_OUT3, TEGRA124_CLK_CLK_MAX, 102000000, 1},
{TEGRA124_CLK_PLL_P_OUT4, TEGRA124_CLK_CLK_MAX, 204000000, 1},
- {TEGRA124_CLK_MSELECT, TEGRA124_CLK_PLL_P, 204000000, 1},
- {TEGRA124_CLK_UARTA, TEGRA124_CLK_PLL_P, 0, 1},
- {TEGRA124_CLK_UARTB, TEGRA124_CLK_PLL_P, 0, 1},
- {TEGRA124_CLK_UARTC, TEGRA124_CLK_PLL_P, 0, 1},
- {TEGRA124_CLK_UARTD, TEGRA124_CLK_PLL_P, 0, 1},
+ {TEGRA124_CLK_MSELECT, TEGRA124_CLK_PLL_P, 102000000, 1},
+ {TEGRA124_CLK_UARTA, TEGRA124_CLK_PLL_P, 0, 0},
+ {TEGRA124_CLK_UARTB, TEGRA124_CLK_PLL_P, 0, 0},
+ {TEGRA124_CLK_UARTC, TEGRA124_CLK_PLL_P, 0, 0},
+ {TEGRA124_CLK_UARTD, TEGRA124_CLK_PLL_P, 0, 0},
{TEGRA124_CLK_SDMMC1, TEGRA124_CLK_PLL_P, 48000000, 0},
{TEGRA124_CLK_SDMMC2, TEGRA124_CLK_PLL_P, 48000000, 0},
{TEGRA124_CLK_SDMMC3, TEGRA124_CLK_PLL_P, 48000000, 0},
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index 5b4365d492..2ff42d8bdb 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -324,11 +324,11 @@ static struct tegra_clk_init_table init_table[] = {
{TEGRA20_CLK_PLL_P_OUT4, TEGRA20_CLK_CLK_MAX, 24000000, 1},
{TEGRA20_CLK_PLL_C, TEGRA20_CLK_CLK_MAX, 600000000, 1},
{TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 120000000, 1},
- {TEGRA20_CLK_UARTA, TEGRA20_CLK_PLL_P, 0, 1},
- {TEGRA20_CLK_UARTB, TEGRA20_CLK_PLL_P, 0, 1},
- {TEGRA20_CLK_UARTC, TEGRA20_CLK_PLL_P, 0, 1},
- {TEGRA20_CLK_UARTD, TEGRA20_CLK_PLL_P, 0, 1},
- {TEGRA20_CLK_UARTE, TEGRA20_CLK_PLL_P, 0, 1},
+ {TEGRA20_CLK_UARTA, TEGRA20_CLK_PLL_P, 0, 0},
+ {TEGRA20_CLK_UARTB, TEGRA20_CLK_PLL_P, 0, 0},
+ {TEGRA20_CLK_UARTC, TEGRA20_CLK_PLL_P, 0, 0},
+ {TEGRA20_CLK_UARTD, TEGRA20_CLK_PLL_P, 0, 0},
+ {TEGRA20_CLK_UARTE, TEGRA20_CLK_PLL_P, 0, 0},
{TEGRA20_CLK_SDMMC1, TEGRA20_CLK_PLL_P, 48000000, 0},
{TEGRA20_CLK_SDMMC2, TEGRA20_CLK_PLL_P, 48000000, 0},
{TEGRA20_CLK_SDMMC3, TEGRA20_CLK_PLL_P, 48000000, 0},
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index ed6d73625c..46fd6dddea 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -130,6 +130,13 @@ static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
{ 0, 0, 0, 0, 0, 0 },
};
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ { 12000000, 100000000, 150, 1, 18, 11},
+ { 216000000, 100000000, 200, 18, 24, 13},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
/* PLL parameters */
static struct tegra_clk_pll_params pll_c_params = {
.input_min = 2000000,
@@ -201,6 +208,19 @@ static struct tegra_clk_pll_params pll_u_params = {
.lock_delay = 1000,
};
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 216000000,
+ .cf_min = 12000000,
+ .cf_max = 12000000,
+ .vco_min = 1200000000,
+ .vco_max = 2400000000U,
+ .base_reg = CRC_PLLE_BASE,
+ .misc_reg = CRC_PLLE_MISC,
+ .lock_enable_bit_idx = CRC_PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
static void tegra30_pll_init(void)
{
/* PLLC */
@@ -251,6 +271,11 @@ static void tegra30_pll_init(void)
clks[TEGRA30_CLK_PLL_U] = tegra_clk_register_pll("pll_u", "pll_ref",
car_base, 0, 0, &pll_u_params, TEGRA_PLLU |
TEGRA_PLL_HAS_CPCON, pll_u_freq_table);
+
+ /* PLLE */
+ clks[TEGRA30_CLK_PLL_E] = tegra_clk_register_plle("pll_e", "pll_ref",
+ car_base, 0, 100000000, &pll_e_params,
+ TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK, pll_e_freq_table);
}
static const char *mux_pllpcm_clkm[] = {"pll_p", "pll_c", "pll_m", "clk_m"};
@@ -278,6 +303,12 @@ static void tegra30_periph_init(void)
mux_pllpcm_clkm, ARRAY_SIZE(mux_pllpcm_clkm), car_base,
CRC_CLK_SOURCE_UARTE, TEGRA30_CLK_UARTE,
TEGRA_PERIPH_ON_APB);
+ clks[TEGRA30_CLK_PCIE] = clk_gate("pcie", "clk_m",
+ car_base + CRC_CLK_OUT_ENB_U, 6, 0, 0);
+ clks[TEGRA30_CLK_AFI] = clk_gate("afi", "clk_m",
+ car_base + CRC_CLK_OUT_ENB_U, 8, 0, 0);
+ clks[TEGRA30_CLK_CML0] = clk_gate("cml0", "pll_e",
+ car_base + CRC_PLLE_AUX, 0, 0, 0);
/* peripheral clocks with a divider */
clks[TEGRA30_CLK_MSELECT] = tegra_clk_register_periph("mselect",
@@ -320,12 +351,12 @@ static struct tegra_clk_init_table init_table[] = {
{TEGRA30_CLK_PLL_P_OUT2, TEGRA30_CLK_CLK_MAX, 48000000, 1},
{TEGRA30_CLK_PLL_P_OUT3, TEGRA30_CLK_CLK_MAX, 102000000, 1},
{TEGRA30_CLK_PLL_P_OUT4, TEGRA30_CLK_CLK_MAX, 204000000, 1},
- {TEGRA30_CLK_MSELECT, TEGRA30_CLK_PLL_P, 204000000, 1},
- {TEGRA30_CLK_UARTA, TEGRA30_CLK_PLL_P, 0, 1},
- {TEGRA30_CLK_UARTB, TEGRA30_CLK_PLL_P, 0, 1},
- {TEGRA30_CLK_UARTC, TEGRA30_CLK_PLL_P, 0, 1},
- {TEGRA30_CLK_UARTD, TEGRA30_CLK_PLL_P, 0, 1},
- {TEGRA30_CLK_UARTE, TEGRA30_CLK_PLL_P, 0, 1},
+ {TEGRA30_CLK_MSELECT, TEGRA30_CLK_PLL_P, 102000000, 1},
+ {TEGRA30_CLK_UARTA, TEGRA30_CLK_PLL_P, 0, 0},
+ {TEGRA30_CLK_UARTB, TEGRA30_CLK_PLL_P, 0, 0},
+ {TEGRA30_CLK_UARTC, TEGRA30_CLK_PLL_P, 0, 0},
+ {TEGRA30_CLK_UARTD, TEGRA30_CLK_PLL_P, 0, 0},
+ {TEGRA30_CLK_UARTE, TEGRA30_CLK_PLL_P, 0, 0},
{TEGRA30_CLK_SDMMC1, TEGRA30_CLK_PLL_P, 48000000, 0},
{TEGRA30_CLK_SDMMC2, TEGRA30_CLK_PLL_P, 48000000, 0},
{TEGRA30_CLK_SDMMC3, TEGRA30_CLK_PLL_P, 48000000, 0},
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index d5d0730602..10d03573fd 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -63,6 +63,7 @@ struct tegra_clk_pll_params {
u32 base_reg;
u32 misc_reg;
+ u32 aux_reg;
u32 lock_reg;
u8 lock_bit_idx;
u8 lock_enable_bit_idx;
@@ -101,6 +102,18 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
struct tegra_clk_pll_params *pll_params, u8 pll_flags,
struct tegra_clk_pll_freq_table *freq_table);
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+ void __iomem *clk_base,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u8 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table);
+
+struct clk *tegra_clk_register_plle_tegra114(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u8 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table);
+
/* struct tegra_clk_pll_out - PLL output divider */
struct tegra_clk_pll_out {
struct clk hw;
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index c52da18b57..f793cbeabf 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -696,4 +696,4 @@ static struct driver_d tegra_i2c_driver = {
.probe = tegra_i2c_probe,
.of_compatible = DRV_OF_COMPAT(tegra_i2c_compatible),
};
-device_platform_driver(tegra_i2c_driver);
+register_driver_macro(fs, platform, tegra_i2c_driver);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index c99fcc8316..24b984462d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -156,6 +156,14 @@ config DRIVER_NET_RTL8139
This is a driver for the Fast Ethernet PCI network cards based on
the RTL 8139 chips.
+config DRIVER_NET_RTL8169
+ bool "RealTek RTL-8169 PCI Ethernet driver"
+ depends on PCI
+ select PHYLIB
+ help
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the RTL 8169 chips.
+
config DRIVER_NET_SMC911X
bool "smc911x ethernet driver"
select PHYLIB
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 1b85778f9f..3e66b3135c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o
obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o
obj-$(CONFIG_DRIVER_NET_ORION) += orion-gbe.o
obj-$(CONFIG_DRIVER_NET_RTL8139) += rtl8139.o
+obj-$(CONFIG_DRIVER_NET_RTL8169) += rtl8169.o
obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o
obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o
obj-$(CONFIG_DRIVER_NET_TAP) += tap.o
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c
new file mode 100644
index 0000000000..5702900e49
--- /dev/null
+++ b/drivers/net/rtl8169.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <asm/mmu.h>
+#include <common.h>
+#include <init.h>
+#include <net.h>
+#include <malloc.h>
+#include <linux/pci.h>
+
+#define NUM_TX_DESC 1
+#define NUM_RX_DESC 4
+#define PKT_BUF_SIZE 1536
+#define ETH_ZLEN 60
+
+struct rtl8169_chip_info {
+ const char *name;
+ u8 version;
+ u32 RxConfigMask;
+};
+
+#define BD_STAT_OWN 0x80000000
+#define BD_STAT_EOR 0x40000000
+#define BD_STAT_FS 0x20000000
+#define BD_STAT_LS 0x10000000
+#define BD_STAT_RX_RES 0x00200000
+struct bufdesc {
+ u32 status;
+ u32 vlan_tag;
+ u32 buf_addr;
+ u32 buf_Haddr;
+};
+
+struct rtl8169_priv {
+ struct eth_device edev;
+ void __iomem *base;
+ struct pci_dev *pci_dev;
+ int chipset;
+
+ struct bufdesc *tx_desc;
+ void *tx_buf;
+ unsigned int cur_tx;
+
+ struct bufdesc *rx_desc;
+ void *rx_buf;
+ unsigned int cur_rx;
+
+ struct mii_bus miibus;
+};
+
+#define MAC0 0x00
+#define MAR0 0x08
+#define TxDescStartAddrLow 0x20
+#define TxDescStartAddrHigh 0x24
+#define TxHDescStartAddrLow 0x28
+#define TxHDescStartAddrHigh 0x2c
+#define FLASH 0x30
+#define ERSR 0x36
+#define ChipCmd 0x37
+#define CmdReset 0x10
+#define CmdRxEnb 0x08
+#define CmdTxEnb 0x04
+#define RxBufEmpty 0x01
+#define TxPoll 0x38
+#define IntrMask 0x3c
+#define IntrStatus 0x3e
+#define SYSErr 0x8000
+#define PCSTimeout 0x4000
+#define SWInt 0x0100
+#define TxDescUnavail 0x80
+#define RxFIFOOver 0x40
+#define RxUnderrun 0x20
+#define RxOverflow 0x10
+#define TxErr 0x08
+#define TxOK 0x04
+#define RxErr 0x02
+#define RxOK 0x01
+#define TxConfig 0x40
+#define TxInterFrameGapShift 24
+#define TxDMAShift 8
+#define RxConfig 0x44
+#define AcceptErr 0x20
+#define AcceptRunt 0x10
+#define AcceptBroadcast 0x08
+#define AcceptMulticast 0x04
+#define AcceptMyPhys 0x02
+#define AcceptAllPhys 0x01
+#define RxCfgFIFOShift 13
+#define RxCfgDMAShift 8
+#define RxMissed 0x4c
+#define Cfg9346 0x50
+#define Cfg9346_Lock 0x00
+#define Cfg9346_Unlock 0xc0
+#define Config0 0x51
+#define Config1 0x52
+#define Config2 0x53
+#define Config3 0x54
+#define Config4 0x55
+#define Config5 0x56
+#define MultiIntr 0x5c
+#define PHYAR 0x60
+#define TBICSR 0x64
+#define TBI_ANAR 0x68
+#define TBI_LPAR 0x6a
+#define PHYstatus 0x6c
+#define RxMaxSize 0xda
+#define CPlusCmd 0xe0
+#define RxDescStartAddrLow 0xe4
+#define RxDescStartAddrHigh 0xe8
+#define EarlyTxThres 0xec
+#define FuncEvent 0xf0
+#define FuncEventMask 0xf4
+#define FuncPresetState 0xf8
+#define FuncForceEvent 0xfc
+
+/* write MMIO register */
+#define RTL_W8(priv, reg, val) writeb(val, ((char *)(priv->base) + reg))
+#define RTL_W16(priv, reg, val) writew(val, ((char *)(priv->base) + reg))
+#define RTL_W32(priv, reg, val) writel(val, ((char *)(priv->base) + reg))
+
+/* read MMIO register */
+#define RTL_R8(priv, reg) readb(((char *)(priv->base) + reg))
+#define RTL_R16(priv, reg) readw(((char *)(priv->base) + reg))
+#define RTL_R32(priv, reg) readl(((char *)(priv->base) + reg))
+
+static const u32 rtl8169_rx_config =
+ (7 << RxCfgFIFOShift) | (6 << RxCfgDMAShift);
+
+static void rtl8169_chip_reset(struct rtl8169_priv *priv)
+{
+ int i;
+
+ /* Soft reset the chip. */
+ RTL_W8(priv, ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ if ((RTL_R8(priv, ChipCmd) & CmdReset) == 0)
+ break;
+ udelay(10);
+ }
+}
+
+static struct rtl8169_chip_info chip_info[] = {
+ {"RTL-8169", 0x00, 0xff7e1880},
+ {"RTL-8169", 0x04, 0xff7e1880},
+ {"RTL-8169", 0x00, 0xff7e1880},
+ {"RTL-8169s/8110s", 0x02, 0xff7e1880},
+ {"RTL-8169s/8110s", 0x04, 0xff7e1880},
+ {"RTL-8169sb/8110sb", 0x10, 0xff7e1880},
+ {"RTL-8169sc/8110sc", 0x18, 0xff7e1880},
+ {"RTL-8168b/8111sb", 0x30, 0xff7e1880},
+ {"RTL-8168b/8111sb", 0x38, 0xff7e1880},
+ {"RTL-8168d/8111d", 0x28, 0xff7e1880},
+ {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880},
+ {"RTL-8168/8111g", 0x4c, 0xff7e1880,},
+ {"RTL-8101e", 0x34, 0xff7e1880},
+ {"RTL-8100e", 0x32, 0xff7e1880},
+};
+
+static void rtl8169_chip_identify(struct rtl8169_priv *priv)
+{
+ u32 val;
+ int i;
+
+ val = RTL_R32(priv, TxConfig);
+ val = ((val & 0x7c000000) + ((val & 0x00800000) << 2)) >> 24;
+
+ for (i = ARRAY_SIZE(chip_info) - 1; i >= 0; i--){
+ if (val == chip_info[i].version) {
+ priv->chipset = i;
+ dev_dbg(&priv->pci_dev->dev, "found %s chipset\n",
+ chip_info[i].name);
+ return;
+ }
+ }
+
+ dev_dbg(&priv->pci_dev->dev,
+ "no matching chip version found, assuming RTL-8169\n");
+ priv->chipset = 0;
+}
+
+static int rtl8169_init_dev(struct eth_device *edev)
+{
+ struct rtl8169_priv *priv = edev->priv;
+
+ rtl8169_chip_reset(priv);
+ rtl8169_chip_identify(priv);
+ pci_set_master(priv->pci_dev);
+
+ return 0;
+}
+
+static void __set_rx_mode(struct rtl8169_priv *priv)
+{
+ u32 mc_filter[2], val;
+
+ /* IFF_ALLMULTI */
+ /* Too many to filter perfectly -- accept all multicasts. */
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+
+ val = AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+ rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
+ chip_info[priv->chipset].RxConfigMask);
+
+ RTL_W32(priv, RxConfig, val);
+ RTL_W32(priv, MAR0 + 0, mc_filter[0]);
+ RTL_W32(priv, MAR0 + 4, mc_filter[1]);
+}
+
+static void rtl8169_init_ring(struct rtl8169_priv *priv)
+{
+ int i;
+
+ priv->cur_rx = priv->cur_tx = 0;
+
+ priv->tx_desc = dma_alloc_coherent(NUM_TX_DESC *
+ sizeof(struct bufdesc));
+ priv->tx_buf = malloc(NUM_TX_DESC * PKT_BUF_SIZE);
+ priv->rx_desc = dma_alloc_coherent(NUM_RX_DESC *
+ sizeof(struct bufdesc));
+ priv->rx_buf = malloc(NUM_RX_DESC * PKT_BUF_SIZE);
+ dma_clean_range((unsigned long)priv->rx_buf,
+ (unsigned long)priv->rx_buf + NUM_RX_DESC * PKT_BUF_SIZE);
+
+ memset(priv->tx_desc, 0, NUM_TX_DESC * sizeof(struct bufdesc));
+ memset(priv->rx_desc, 0, NUM_RX_DESC * sizeof(struct bufdesc));
+
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ if (i == (NUM_RX_DESC - 1))
+ priv->rx_desc[i].status =
+ BD_STAT_OWN | BD_STAT_EOR | PKT_BUF_SIZE;
+ else
+ priv->rx_desc[i].status =
+ BD_STAT_OWN | PKT_BUF_SIZE;
+
+ priv->rx_desc[i].buf_addr =
+ virt_to_phys(priv->rx_buf + i * PKT_BUF_SIZE);
+ }
+
+ dma_flush_range((unsigned long)priv->rx_desc,
+ (unsigned long)priv->rx_desc +
+ NUM_RX_DESC * sizeof(struct bufdesc));
+}
+
+static void rtl8169_hw_start(struct rtl8169_priv *priv)
+{
+ u32 val;
+
+ RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
+
+ /* RTL-8169sb/8110sb or previous version */
+ if (priv->chipset <= 5)
+ RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);
+
+ RTL_W8(priv, EarlyTxThres, 0x3f);
+
+ /* For gigabit rtl8169 */
+ RTL_W16(priv, RxMaxSize, 0x800);
+
+ /* Set Rx Config register */
+ val = rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
+ chip_info[priv->chipset].RxConfigMask);
+ RTL_W32(priv, RxConfig, val);
+
+ /* Set DMA burst size and Interframe Gap Time */
+ RTL_W32(priv, TxConfig, (6 << TxDMAShift) | (3 << TxInterFrameGapShift));
+
+ RTL_W32(priv, TxDescStartAddrLow, virt_to_phys(priv->tx_desc));
+ RTL_W32(priv, TxDescStartAddrHigh, 0);
+ RTL_W32(priv, RxDescStartAddrLow, virt_to_phys(priv->rx_desc));
+ RTL_W32(priv, RxDescStartAddrHigh, 0);
+
+ /* RTL-8169sc/8110sc or later version */
+ if (priv->chipset > 5)
+ RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);
+
+ RTL_W8(priv, Cfg9346, Cfg9346_Lock);
+ udelay(10);
+
+ RTL_W32(priv, RxMissed, 0);
+
+ __set_rx_mode(priv);
+
+ /* no early-rx interrupts */
+ RTL_W16(priv, MultiIntr, RTL_R16(priv, MultiIntr) & 0xf000);
+}
+
+static int rtl8169_eth_open(struct eth_device *edev)
+{
+ struct rtl8169_priv *priv = edev->priv;
+ int ret;
+
+ rtl8169_init_ring(priv);
+ rtl8169_hw_start(priv);
+
+ ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0,
+ PHY_INTERFACE_MODE_NA);
+
+ return ret;
+}
+
+static int rtl8169_phy_write(struct mii_bus *bus, int phy_addr,
+ int reg, u16 val)
+{
+ struct rtl8169_priv *priv = bus->priv;
+ int i;
+
+ if (phy_addr != 0)
+ return -1;
+
+ RTL_W32(priv, PHYAR, 0x80000000 | (reg & 0xff) << 16 | val);
+ mdelay(1);
+
+ for (i = 2000; i > 0; i--) {
+ if (!(RTL_R32(priv, PHYAR) & 0x80000000)) {
+ return 0;
+ } else {
+ udelay(100);
+ }
+ }
+
+ return -1;
+}
+
+static int rtl8169_phy_read(struct mii_bus *bus, int phy_addr, int reg)
+{
+ struct rtl8169_priv *priv = bus->priv;
+ int i, val = 0xffff;
+
+ RTL_W32(priv, PHYAR, 0x0 | (reg & 0xff) << 16);
+ mdelay(10);
+
+ if (phy_addr != 0)
+ return val;
+
+ for (i = 2000; i > 0; i--) {
+ if (RTL_R32(priv, PHYAR) & 0x80000000) {
+ val = (int) (RTL_R32(priv, PHYAR) & 0xffff);
+ break;
+ } else {
+ udelay(100);
+ }
+ }
+ return val;
+}
+
+static int rtl8169_eth_send(struct eth_device *edev, void *packet,
+ int packet_length)
+{
+ struct rtl8169_priv *priv = edev->priv;
+ unsigned int entry;
+
+ entry = priv->cur_tx % NUM_TX_DESC;
+
+ if (packet_length < ETH_ZLEN)
+ memset(priv->tx_buf + entry * PKT_BUF_SIZE, 0, ETH_ZLEN);
+ memcpy(priv->tx_buf + entry * PKT_BUF_SIZE, packet, packet_length);
+ dma_flush_range((unsigned long)priv->tx_buf + entry * PKT_BUF_SIZE,
+ (unsigned long)priv->tx_buf + (entry + 1) * PKT_BUF_SIZE);
+
+ priv->tx_desc[entry].buf_Haddr = 0;
+ priv->tx_desc[entry].buf_addr =
+ virt_to_phys(priv->tx_buf + entry * PKT_BUF_SIZE);
+
+ if (entry != (NUM_TX_DESC - 1)) {
+ priv->tx_desc[entry].status =
+ BD_STAT_OWN | BD_STAT_FS | BD_STAT_LS |
+ ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN);
+ } else {
+ priv->tx_desc[entry].status =
+ BD_STAT_OWN | BD_STAT_EOR | BD_STAT_FS | BD_STAT_LS |
+ ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN);
+ }
+
+ dma_flush_range((unsigned long)&priv->tx_desc[entry],
+ (unsigned long)&priv->tx_desc[entry + 1]);
+
+ RTL_W8(priv, TxPoll, 0x40);
+ do {
+ dma_inv_range((unsigned long)&priv->tx_desc[entry],
+ (unsigned long)&priv->tx_desc[entry + 1]);
+ } while (priv->tx_desc[entry].status & BD_STAT_OWN);
+
+ priv->cur_tx++;
+
+ return 0;
+}
+
+static int rtl8169_eth_rx(struct eth_device *edev)
+{
+ struct rtl8169_priv *priv = edev->priv;
+ unsigned int entry, pkt_size = 0;
+ u8 status;
+
+ entry = priv->cur_rx % NUM_RX_DESC;
+
+ dma_inv_range((unsigned long)&priv->rx_desc[entry],
+ (unsigned long)&priv->rx_desc[entry + 1]);
+
+ if ((priv->rx_desc[entry].status & BD_STAT_OWN) == 0) {
+ if (!(priv->rx_desc[entry].status & BD_STAT_RX_RES)) {
+ pkt_size = (priv->rx_desc[entry].status & 0x1fff) - 4;
+
+ dma_inv_range((unsigned long)priv->rx_buf
+ + entry * PKT_BUF_SIZE,
+ (unsigned long)priv->rx_buf
+ + entry * PKT_BUF_SIZE + pkt_size);
+
+ net_receive(edev, priv->rx_buf + entry * PKT_BUF_SIZE,
+ pkt_size);
+
+ /*
+ * the buffer is going to be reused by HW, make sure to
+ * clean out any potentially modified data
+ */
+ dma_clean_range((unsigned long)priv->rx_buf
+ + entry * PKT_BUF_SIZE,
+ (unsigned long)priv->rx_buf
+ + entry * PKT_BUF_SIZE + pkt_size);
+
+ if (entry == NUM_RX_DESC - 1)
+ priv->rx_desc[entry].status = BD_STAT_OWN |
+ BD_STAT_EOR | PKT_BUF_SIZE;
+ else
+ priv->rx_desc[entry].status =
+ BD_STAT_OWN | PKT_BUF_SIZE;
+ priv->rx_desc[entry].buf_addr =
+ virt_to_phys(priv->rx_buf +
+ entry * PKT_BUF_SIZE);
+
+ dma_flush_range((unsigned long)&priv->rx_desc[entry],
+ (unsigned long)&priv->rx_desc[entry + 1]);
+ } else {
+ dev_err(&edev->dev, "rx error\n");
+ }
+
+ priv->cur_rx++;
+
+ return pkt_size;
+
+ } else {
+ status = RTL_R8(priv, IntrStatus);
+ RTL_W8(priv, IntrStatus, status & ~(TxErr | RxErr | SYSErr));
+ udelay(100); /* wait */
+ }
+
+ return 0;
+}
+
+static int rtl8169_get_ethaddr(struct eth_device *edev, unsigned char *m)
+{
+ struct rtl8169_priv *priv = edev->priv;
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ m[i] = RTL_R8(priv, MAC0 + i);
+ }
+
+ return 0;
+}
+
+static int rtl8169_set_ethaddr(struct eth_device *edev, unsigned char *mac_addr)
+{
+ struct rtl8169_priv *priv = edev->priv;
+ int i;
+
+ RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
+
+ for (i = 0; i < 6; i++) {
+ RTL_W8(priv, (MAC0 + i), mac_addr[i]);
+ RTL_R8(priv, mac_addr[i]);
+ }
+
+ RTL_W8(priv, Cfg9346, Cfg9346_Lock);
+
+ return 0;
+}
+
+static void rtl8169_eth_halt(struct eth_device *edev)
+{
+ struct rtl8169_priv *priv = edev->priv;
+
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8(priv, ChipCmd, 0x00);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16(priv, IntrMask, 0x0000);
+ RTL_W32(priv, RxMissed, 0);
+
+ pci_clear_master(priv->pci_dev);
+}
+
+static int rtl8169_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct device_d *dev = &pdev->dev;
+ struct eth_device *edev;
+ struct rtl8169_priv *priv;
+ int ret;
+
+ /* enable pci device */
+ pci_enable_device(pdev);
+
+ priv = xzalloc(sizeof(struct rtl8169_priv));
+
+ edev = &priv->edev;
+ dev->type_data = edev;
+ edev->priv = priv;
+
+ priv->pci_dev = pdev;
+
+ priv->miibus.read = rtl8169_phy_read;
+ priv->miibus.write = rtl8169_phy_write;
+ priv->miibus.priv = priv;
+ priv->miibus.parent = &edev->dev;
+
+ priv->base = pci_iomap(pdev, pdev->device == 0x8168 ? 2 : 1);
+
+ dev_dbg(dev, "rtl%04x (rev %02x) (base=%p)\n",
+ pdev->device, pdev->revision, priv->base);
+
+ edev->init = rtl8169_init_dev;
+ edev->open = rtl8169_eth_open;
+ edev->send = rtl8169_eth_send;
+ edev->recv = rtl8169_eth_rx;
+ edev->get_ethaddr = rtl8169_get_ethaddr;
+ edev->set_ethaddr = rtl8169_set_ethaddr;
+ edev->halt = rtl8169_eth_halt;
+ edev->parent = dev;
+ ret = eth_register(edev);
+ if (ret)
+ goto eth_err;
+
+ ret = mdiobus_register(&priv->miibus);
+ if (ret)
+ goto mdio_err;
+
+ return 0;
+
+mdio_err:
+ eth_unregister(edev);
+
+eth_err:
+ free(priv);
+
+ return ret;
+}
+static DEFINE_PCI_DEVICE_TABLE(rtl8169_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), },
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), },
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), },
+ { /* sentinel */ }
+};
+
+static struct pci_driver rtl8169_eth_driver = {
+ .name = "rtl8169_eth",
+ .id_table = rtl8169_pci_tbl,
+ .probe = rtl8169_probe,
+};
+
+static int rtl8169_init(void)
+{
+ return pci_register_driver(&rtl8169_eth_driver);
+}
+device_initcall(rtl8169_init);
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 97a1d933ec..97378ab59f 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -30,6 +30,7 @@ config OF_GPIO
config OF_PCI
bool
depends on PCI
+ select OF_ADDRESS_PCI
help
OpenFirmware PCI bus accessors
diff --git a/drivers/of/address.c b/drivers/of/address.c
index b3cbb15453..8018d78bcb 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -179,6 +179,74 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
}
#endif /* CONFIG_OF_ADDRESS_PCI */
+#ifdef CONFIG_OF_PCI
+int of_pci_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node)
+{
+ const int na = 3, ns = 2;
+ int rlen;
+
+ parser->node = node;
+ parser->pna = of_n_addr_cells(node);
+ parser->np = parser->pna + na + ns;
+
+ parser->range = of_get_property(node, "ranges", &rlen);
+ if (parser->range == NULL)
+ return -ENOENT;
+
+ parser->end = parser->range + rlen / sizeof(__be32);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_pci_range_parser_init);
+
+struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
+ struct of_pci_range *range)
+{
+ const int na = 3, ns = 2;
+
+ if (!range)
+ return NULL;
+
+ if (!parser->range || parser->range + parser->np > parser->end)
+ return NULL;
+
+ range->pci_space = parser->range[0];
+ range->flags = of_bus_pci_get_flags(parser->range);
+ range->pci_addr = of_read_number(parser->range + 1, ns);
+ range->cpu_addr = of_translate_address(parser->node,
+ parser->range + na);
+ range->size = of_read_number(parser->range + parser->pna + na, ns);
+
+ parser->range += parser->np;
+
+ /* Now consume following elements while they are contiguous */
+ while (parser->range + parser->np <= parser->end) {
+ u32 flags, pci_space;
+ u64 pci_addr, cpu_addr, size;
+
+ pci_space = be32_to_cpup(parser->range);
+ flags = of_bus_pci_get_flags(parser->range);
+ pci_addr = of_read_number(parser->range + 1, ns);
+ cpu_addr = of_translate_address(parser->node,
+ parser->range + na);
+ size = of_read_number(parser->range + parser->pna + na, ns);
+
+ if (flags != range->flags)
+ break;
+ if (pci_addr != range->pci_addr + range->size ||
+ cpu_addr != range->cpu_addr + range->size)
+ break;
+
+ range->size += size;
+ parser->range += parser->np;
+ }
+
+ return range;
+}
+EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
+#endif /* CONFIG_OF_PCI */
+
/*
* Array of bus specific translators
*/
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index d17a151082..0e9308e97b 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -30,6 +30,13 @@ config PCI_MVEBU
select OF_PCI
select PCI
+config PCI_TEGRA
+ bool "NVDIA Tegra PCIe driver"
+ depends on ARCH_TEGRA
+ select OF_ADDRESS_PCI
+ select OF_PCI
+ select PCI
+
endmenu
endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 442353173c..b93b28abea 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -8,3 +8,4 @@ ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
CPPFLAGS += $(ccflags-y)
obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o pci-mvebu-phy.o
+obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 8215ee5638..866ab08067 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -86,7 +86,8 @@ int pci_register_device(struct pci_dev *pdev)
struct device_d *dev = &pdev->dev;
int ret;
- strcpy(dev->name, "pci");
+ snprintf(dev->name, MAX_DRIVER_NAME, "pci-%04x:%04x.",
+ pdev->vendor, pdev->device);
dev->bus = &pci_bus;
dev->id = DEVICE_ID_DYNAMIC;
diff --git a/drivers/pci/pci-tegra.c b/drivers/pci/pci-tegra.c
new file mode 100644
index 0000000000..f2ade7749d
--- /dev/null
+++ b/drivers/pci/pci-tegra.c
@@ -0,0 +1,1305 @@
+/*
+ * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * based on code
+ * Copyright (c) 2010, CompuLab, Ltd.
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <malloc.h>
+#include <io.h>
+#include <init.h>
+#include <asm/mmu.h>
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <of_address.h>
+#include <of_pci.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+#include <sizes.h>
+#include <mach/tegra-powergate.h>
+#include <regulator.h>
+
+/* register definitions */
+
+#define AFI_AXI_BAR0_SZ 0x00
+#define AFI_AXI_BAR1_SZ 0x04
+#define AFI_AXI_BAR2_SZ 0x08
+#define AFI_AXI_BAR3_SZ 0x0c
+#define AFI_AXI_BAR4_SZ 0x10
+#define AFI_AXI_BAR5_SZ 0x14
+
+#define AFI_AXI_BAR0_START 0x18
+#define AFI_AXI_BAR1_START 0x1c
+#define AFI_AXI_BAR2_START 0x20
+#define AFI_AXI_BAR3_START 0x24
+#define AFI_AXI_BAR4_START 0x28
+#define AFI_AXI_BAR5_START 0x2c
+
+#define AFI_FPCI_BAR0 0x30
+#define AFI_FPCI_BAR1 0x34
+#define AFI_FPCI_BAR2 0x38
+#define AFI_FPCI_BAR3 0x3c
+#define AFI_FPCI_BAR4 0x40
+#define AFI_FPCI_BAR5 0x44
+
+#define AFI_CACHE_BAR0_SZ 0x48
+#define AFI_CACHE_BAR0_ST 0x4c
+#define AFI_CACHE_BAR1_SZ 0x50
+#define AFI_CACHE_BAR1_ST 0x54
+
+#define AFI_MSI_BAR_SZ 0x60
+#define AFI_MSI_FPCI_BAR_ST 0x64
+#define AFI_MSI_AXI_BAR_ST 0x68
+
+#define AFI_MSI_VEC0 0x6c
+#define AFI_MSI_VEC1 0x70
+#define AFI_MSI_VEC2 0x74
+#define AFI_MSI_VEC3 0x78
+#define AFI_MSI_VEC4 0x7c
+#define AFI_MSI_VEC5 0x80
+#define AFI_MSI_VEC6 0x84
+#define AFI_MSI_VEC7 0x88
+
+#define AFI_MSI_EN_VEC0 0x8c
+#define AFI_MSI_EN_VEC1 0x90
+#define AFI_MSI_EN_VEC2 0x94
+#define AFI_MSI_EN_VEC3 0x98
+#define AFI_MSI_EN_VEC4 0x9c
+#define AFI_MSI_EN_VEC5 0xa0
+#define AFI_MSI_EN_VEC6 0xa4
+#define AFI_MSI_EN_VEC7 0xa8
+
+#define AFI_CONFIGURATION 0xac
+#define AFI_CONFIGURATION_EN_FPCI (1 << 0)
+
+#define AFI_FPCI_ERROR_MASKS 0xb0
+
+#define AFI_INTR_MASK 0xb4
+#define AFI_INTR_MASK_INT_MASK (1 << 0)
+#define AFI_INTR_MASK_MSI_MASK (1 << 8)
+
+#define AFI_INTR_CODE 0xb8
+#define AFI_INTR_CODE_MASK 0xf
+#define AFI_INTR_AXI_SLAVE_ERROR 1
+#define AFI_INTR_AXI_DECODE_ERROR 2
+#define AFI_INTR_TARGET_ABORT 3
+#define AFI_INTR_MASTER_ABORT 4
+#define AFI_INTR_INVALID_WRITE 5
+#define AFI_INTR_LEGACY 6
+#define AFI_INTR_FPCI_DECODE_ERROR 7
+
+#define AFI_INTR_SIGNATURE 0xbc
+#define AFI_UPPER_FPCI_ADDRESS 0xc0
+#define AFI_SM_INTR_ENABLE 0xc4
+#define AFI_SM_INTR_INTA_ASSERT (1 << 0)
+#define AFI_SM_INTR_INTB_ASSERT (1 << 1)
+#define AFI_SM_INTR_INTC_ASSERT (1 << 2)
+#define AFI_SM_INTR_INTD_ASSERT (1 << 3)
+#define AFI_SM_INTR_INTA_DEASSERT (1 << 4)
+#define AFI_SM_INTR_INTB_DEASSERT (1 << 5)
+#define AFI_SM_INTR_INTC_DEASSERT (1 << 6)
+#define AFI_SM_INTR_INTD_DEASSERT (1 << 7)
+
+#define AFI_AFI_INTR_ENABLE 0xc8
+#define AFI_INTR_EN_INI_SLVERR (1 << 0)
+#define AFI_INTR_EN_INI_DECERR (1 << 1)
+#define AFI_INTR_EN_TGT_SLVERR (1 << 2)
+#define AFI_INTR_EN_TGT_DECERR (1 << 3)
+#define AFI_INTR_EN_TGT_WRERR (1 << 4)
+#define AFI_INTR_EN_DFPCI_DECERR (1 << 5)
+#define AFI_INTR_EN_AXI_DECERR (1 << 6)
+#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7)
+#define AFI_INTR_EN_PRSNT_SENSE (1 << 8)
+
+#define AFI_PCIE_CONFIG 0x0f8
+#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1))
+#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20)
+
+#define AFI_FUSE 0x104
+#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2)
+
+#define AFI_PEX0_CTRL 0x110
+#define AFI_PEX1_CTRL 0x118
+#define AFI_PEX2_CTRL 0x128
+#define AFI_PEX_CTRL_RST (1 << 0)
+#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1)
+#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
+#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4)
+
+#define AFI_PLLE_CONTROL 0x160
+#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9)
+#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1)
+
+#define AFI_PEXBIAS_CTRL_0 0x168
+
+#define RP_VEND_XP 0x00000F00
+#define RP_VEND_XP_DL_UP (1 << 30)
+
+#define RP_PRIV_MISC 0x00000FE0
+#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0)
+#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0)
+
+#define RP_LINK_CONTROL_STATUS 0x00000090
+#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000
+#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000
+
+#define PADS_CTL_SEL 0x0000009C
+
+#define PADS_CTL 0x000000A0
+#define PADS_CTL_IDDQ_1L (1 << 0)
+#define PADS_CTL_TX_DATA_EN_1L (1 << 6)
+#define PADS_CTL_RX_DATA_EN_1L (1 << 10)
+
+#define PADS_PLL_CTL_TEGRA20 0x000000B8
+#define PADS_PLL_CTL_TEGRA30 0x000000B4
+#define PADS_PLL_CTL_RST_B4SM (1 << 1)
+#define PADS_PLL_CTL_LOCKDET (1 << 8)
+#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16)
+#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16)
+#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16)
+#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16)
+#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20)
+#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20)
+#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20)
+#define PADS_PLL_CTL_TXCLKREF_BUF_EN (1 << 22)
+
+#define PADS_REFCLK_CFG0 0x000000C8
+#define PADS_REFCLK_CFG1 0x000000CC
+
+/*
+ * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit
+ * entries, one entry per PCIe port. These field definitions and desired
+ * values aren't in the TRM, but do come from NVIDIA.
+ */
+#define PADS_REFCLK_CFG_TERM_SHIFT 2 /* 6:2 */
+#define PADS_REFCLK_CFG_E_TERM_SHIFT 7
+#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */
+#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */
+
+/* Default value provided by HW engineering is 0xfa5c */
+#define PADS_REFCLK_CFG_VALUE \
+ ( \
+ (0x17 << PADS_REFCLK_CFG_TERM_SHIFT) | \
+ (0 << PADS_REFCLK_CFG_E_TERM_SHIFT) | \
+ (0xa << PADS_REFCLK_CFG_PREDI_SHIFT) | \
+ (0xf << PADS_REFCLK_CFG_DRVI_SHIFT) \
+ )
+
+/* used to differentiate between Tegra SoC generations */
+struct tegra_pcie_soc_data {
+ unsigned int num_ports;
+ unsigned int msi_base_shift;
+ u32 pads_pll_ctl;
+ u32 tx_ref_sel;
+ bool has_pex_clkreq_en;
+ bool has_pex_bias_ctrl;
+ bool has_intr_prsnt_sense;
+ bool has_avdd_supply;
+ bool has_cml_clk;
+ bool has_gen2;
+};
+
+struct tegra_pcie {
+ struct device_d *dev;
+ struct pci_controller pci;
+
+ void __iomem *pads;
+ void __iomem *afi;
+ void __iomem *cs;
+
+ struct list_head buses;
+
+ struct resource io;
+ struct resource mem;
+ struct resource prefetch;
+ struct resource busn;
+ struct resource *cs_res;
+
+ struct clk *pex_clk;
+ struct clk *afi_clk;
+ struct clk *pll_e;
+ struct clk *cml_clk;
+
+ struct reset_control *pex_rst;
+ struct reset_control *afi_rst;
+ struct reset_control *pcie_xrst;
+
+ struct phy *phy;
+
+ struct list_head ports;
+ unsigned int num_ports;
+ u32 xbar_config;
+
+ struct regulator *pex_clk_supply;
+ struct regulator *vdd_supply;
+ struct regulator *avdd_supply;
+
+ const struct tegra_pcie_soc_data *soc_data;
+};
+
+struct tegra_pcie_port {
+ struct tegra_pcie *pcie;
+ struct list_head list;
+ struct resource regs;
+ void __iomem *base;
+ unsigned int index;
+ unsigned int lanes;
+};
+
+static inline struct tegra_pcie *host_to_pcie(struct pci_controller *host)
+{
+ return container_of(host, struct tegra_pcie, pci);
+}
+
+static inline void afi_writel(struct tegra_pcie *pcie, u32 value,
+ unsigned long offset)
+{
+ writel(value, pcie->afi + offset);
+}
+
+static inline u32 afi_readl(struct tegra_pcie *pcie, unsigned long offset)
+{
+ return readl(pcie->afi + offset);
+}
+
+static inline void pads_writel(struct tegra_pcie *pcie, u32 value,
+ unsigned long offset)
+{
+ writel(value, pcie->pads + offset);
+}
+
+static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset)
+{
+ return readl(pcie->pads + offset);
+}
+
+static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
+ unsigned int devfn,
+ int where)
+{
+ struct tegra_pcie *pcie = host_to_pcie(bus->host);
+ void __iomem *addr = NULL;
+
+ if (bus->number == 0) {
+ unsigned int slot = PCI_SLOT(devfn);
+ struct tegra_pcie_port *port;
+
+ list_for_each_entry(port, &pcie->ports, list) {
+ if (port->index + 1 == slot) {
+ addr = (void __force __iomem *)
+ port->regs.start + (where & ~3);
+ break;
+ }
+ }
+ } else {
+ addr = pcie->cs;
+
+ addr += ((where & 0xf00) << 16) | (bus->number << 16) |
+ (PCI_SLOT(devfn) << 11) | (PCI_FUNC(devfn) << 8) |
+ (where & 0xfc);
+ }
+
+ return addr;
+}
+
+static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *value)
+{
+ void __iomem *addr;
+
+ addr = tegra_pcie_conf_address(bus, devfn, where);
+ if (!addr) {
+ *value = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ *value = readl(addr);
+
+ if (size == 1)
+ *value = (*value >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *value = (*value >> (8 * (where & 3))) & 0xffff;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 value)
+{
+ void __iomem *addr;
+ u32 mask, tmp;
+
+ addr = tegra_pcie_conf_address(bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (size == 4) {
+ writel(value, addr);
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (size == 2)
+ mask = ~(0xffff << ((where & 0x3) * 8));
+ else if (size == 1)
+ mask = ~(0xff << ((where & 0x3) * 8));
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ tmp = readl(addr) & mask;
+ tmp |= value << ((where & 0x3) * 8);
+ writel(tmp, addr);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int tegra_pcie_res_start(struct pci_bus *bus, resource_size_t res_addr)
+{
+ return res_addr;
+}
+
+static struct pci_ops tegra_pcie_ops = {
+ .read = tegra_pcie_read_conf,
+ .write = tegra_pcie_write_conf,
+ .res_start = tegra_pcie_res_start,
+};
+
+static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port)
+{
+ unsigned long ret = 0;
+
+ switch (port->index) {
+ case 0:
+ ret = AFI_PEX0_CTRL;
+ break;
+
+ case 1:
+ ret = AFI_PEX1_CTRL;
+ break;
+
+ case 2:
+ ret = AFI_PEX2_CTRL;
+ break;
+ }
+
+ return ret;
+}
+
+static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
+{
+ unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+ unsigned long value;
+
+ /* pulse reset signal */
+ value = afi_readl(port->pcie, ctrl);
+ value &= ~AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
+
+ mdelay(1);
+
+ value = afi_readl(port->pcie, ctrl);
+ value |= AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
+}
+
+static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
+{
+ const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
+ unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+ unsigned long value;
+
+ /* enable reference clock */
+ value = afi_readl(port->pcie, ctrl);
+ value |= AFI_PEX_CTRL_REFCLK_EN;
+
+ if (soc->has_pex_clkreq_en)
+ value |= AFI_PEX_CTRL_CLKREQ_EN;
+
+ value |= AFI_PEX_CTRL_OVERRIDE_EN;
+
+ afi_writel(port->pcie, value, ctrl);
+
+ tegra_pcie_port_reset(port);
+}
+
+static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
+{
+ unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+ unsigned long value;
+
+ /* assert port reset */
+ value = afi_readl(port->pcie, ctrl);
+ value &= ~AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
+
+ /* disable reference clock */
+ value = afi_readl(port->pcie, ctrl);
+ value &= ~AFI_PEX_CTRL_REFCLK_EN;
+ afi_writel(port->pcie, value, ctrl);
+}
+
+static void tegra_pcie_port_free(struct tegra_pcie_port *port)
+{
+ list_del(&port->list);
+ kfree(port);
+}
+
+#if 0
+static void tegra_pcie_fixup_bridge(struct pci_dev *dev)
+{
+ u16 reg;
+
+ if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) {
+ pci_read_config_word(dev, PCI_COMMAND, &reg);
+ reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
+ pci_write_config_word(dev, PCI_COMMAND, reg);
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge);
+
+/* Tegra PCIE root complex wrongly reports device class */
+static void tegra_pcie_fixup_class(struct pci_dev *dev)
+{
+ dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class);
+
+/* Tegra PCIE requires relaxed ordering */
+static void tegra_pcie_relax_enable(struct pci_dev *dev)
+{
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
+#endif
+
+/*
+ * FPCI map is as follows:
+ * - 0xfdfc000000: I/O space
+ * - 0xfdfe000000: type 0 configuration space
+ * - 0xfdff000000: type 1 configuration space
+ * - 0xfe00000000: type 0 extended configuration space
+ * - 0xfe10000000: type 1 extended configuration space
+ */
+static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
+{
+ u32 fpci_bar, size, axi_address;
+
+ /* Bar 0: type 1 extended configuration space */
+ fpci_bar = 0xfe100000;
+ size = resource_size(pcie->cs_res);
+ axi_address = pcie->cs_res->start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR0_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR0);
+
+ /* Bar 1: downstream IO bar */
+ fpci_bar = 0xfdfc0000;
+ size = resource_size(&pcie->io);
+ axi_address = pcie->io.start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
+
+ /* Bar 2: prefetchable memory BAR */
+ fpci_bar = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1;
+ size = resource_size(&pcie->prefetch);
+ axi_address = pcie->prefetch.start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR2_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR2);
+
+ /* Bar 3: non prefetchable memory BAR */
+ fpci_bar = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1;
+ size = resource_size(&pcie->mem);
+ axi_address = pcie->mem.start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR3_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR3);
+
+ /* NULL out the remaining BARs as they are not used */
+ afi_writel(pcie, 0, AFI_AXI_BAR4_START);
+ afi_writel(pcie, 0, AFI_AXI_BAR4_SZ);
+ afi_writel(pcie, 0, AFI_FPCI_BAR4);
+
+ afi_writel(pcie, 0, AFI_AXI_BAR5_START);
+ afi_writel(pcie, 0, AFI_AXI_BAR5_SZ);
+ afi_writel(pcie, 0, AFI_FPCI_BAR5);
+
+ /* map all upstream transactions as uncached */
+ /* FIXME: is 0 ok for BAR0 start ??? */
+ afi_writel(pcie, 0, AFI_CACHE_BAR0_ST);
+ afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ);
+ afi_writel(pcie, 0, AFI_CACHE_BAR1_ST);
+ afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ);
+
+ /* MSI translations are setup only when needed */
+ afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST);
+ afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
+ afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST);
+ afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
+}
+
+static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ u32 value;
+
+ /* initialize internal PHY, enable up to 16 PCIE lanes */
+ pads_writel(pcie, 0x0, PADS_CTL_SEL);
+
+ /* override IDDQ to 1 on all 4 lanes */
+ value = pads_readl(pcie, PADS_CTL);
+ value |= PADS_CTL_IDDQ_1L;
+ pads_writel(pcie, value, PADS_CTL);
+
+ /*
+ * Set up PHY PLL inputs select PLLE output as refclock,
+ * set TX ref sel to div10 (not div5).
+ */
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK);
+ value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
+ pads_writel(pcie, value, soc->pads_pll_ctl);
+
+ /* reset PLL */
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ value &= ~PADS_PLL_CTL_RST_B4SM;
+ pads_writel(pcie, value, soc->pads_pll_ctl);
+
+ udelay(20);
+
+ /* take PLL out of reset */
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ value |= PADS_PLL_CTL_RST_B4SM;
+ pads_writel(pcie, value, soc->pads_pll_ctl);
+
+ /* Configure the reference clock driver */
+ value = PADS_REFCLK_CFG_VALUE | (PADS_REFCLK_CFG_VALUE << 16);
+ pads_writel(pcie, value, PADS_REFCLK_CFG0);
+ if (soc->num_ports > 2)
+ pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1);
+
+ /* wait for the PLL to lock */
+ if (wait_on_timeout(300 * MSECOND,
+ (pads_readl(pcie, soc->pads_pll_ctl) & PADS_PLL_CTL_LOCKDET))) {
+ pr_err("Tegra PCIe error: timeout waiting for PLL\n");
+ return -EBUSY;
+ }
+
+ /* turn off IDDQ override */
+ value = pads_readl(pcie, PADS_CTL);
+ value &= ~PADS_CTL_IDDQ_1L;
+ pads_writel(pcie, value, PADS_CTL);
+
+ /* enable TX/RX data */
+ value = pads_readl(pcie, PADS_CTL);
+ value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
+ pads_writel(pcie, value, PADS_CTL);
+
+ return 0;
+}
+
+static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ struct tegra_pcie_port *port;
+ unsigned long value;
+ int err;
+
+ /* enable PLL power down */
+ if (pcie->phy) {
+ value = afi_readl(pcie, AFI_PLLE_CONTROL);
+ value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
+ value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
+ afi_writel(pcie, value, AFI_PLLE_CONTROL);
+ }
+
+ /* power down PCIe slot clock bias pad */
+ if (soc->has_pex_bias_ctrl)
+ afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+
+ /* configure mode and disable all ports */
+ value = afi_readl(pcie, AFI_PCIE_CONFIG);
+ value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+ value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+
+ list_for_each_entry(port, &pcie->ports, list)
+ value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+
+ afi_writel(pcie, value, AFI_PCIE_CONFIG);
+
+ if (soc->has_gen2) {
+ value = afi_readl(pcie, AFI_FUSE);
+ value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
+ } else {
+ value = afi_readl(pcie, AFI_FUSE);
+ value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
+ }
+
+ if (!pcie->phy)
+ err = tegra_pcie_phy_enable(pcie);
+ else
+ err = phy_power_on(pcie->phy);
+
+ /* take the PCIe interface module out of reset */
+ reset_control_deassert(pcie->pcie_xrst);
+
+ /* finally enable PCIe */
+ value = afi_readl(pcie, AFI_CONFIGURATION);
+ value |= AFI_CONFIGURATION_EN_FPCI;
+ afi_writel(pcie, value, AFI_CONFIGURATION);
+
+ value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR |
+ AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR |
+ AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR;
+
+ if (soc->has_intr_prsnt_sense)
+ value |= AFI_INTR_EN_PRSNT_SENSE;
+
+ afi_writel(pcie, value, AFI_AFI_INTR_ENABLE);
+ afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE);
+
+ /* don't enable MSI for now, only when needed */
+ afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK);
+
+ /* disable all exceptions */
+ afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS);
+
+ return 0;
+}
+
+static void tegra_pcie_power_off(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ int err;
+
+ /* TODO: disable and unprepare clocks? */
+
+ err = phy_power_off(pcie->phy);
+ if (err < 0)
+ dev_warn(pcie->dev, "failed to power off PHY: %d\n", err);
+
+ reset_control_assert(pcie->pcie_xrst);
+ reset_control_assert(pcie->afi_rst);
+ reset_control_assert(pcie->pex_rst);
+
+ tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
+
+ if (soc->has_avdd_supply) {
+ err = regulator_disable(pcie->avdd_supply);
+ if (err < 0)
+ dev_warn(pcie->dev,
+ "failed to disable AVDD regulator: %d\n",
+ err);
+ }
+
+ err = regulator_disable(pcie->pex_clk_supply);
+ if (err < 0)
+ dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n",
+ err);
+
+ err = regulator_disable(pcie->vdd_supply);
+ if (err < 0)
+ dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
+ err);
+}
+
+static int tegra_pcie_power_on(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ int err;
+
+ reset_control_assert(pcie->pcie_xrst);
+ reset_control_assert(pcie->afi_rst);
+ reset_control_assert(pcie->pex_rst);
+
+ tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
+
+ /* enable regulators */
+ err = regulator_enable(pcie->vdd_supply);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable VDD regulator: %d\n", err);
+ return err;
+ }
+
+ err = regulator_enable(pcie->pex_clk_supply);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable pex-clk regulator: %d\n",
+ err);
+ return err;
+ }
+
+ if (soc->has_avdd_supply) {
+ err = regulator_enable(pcie->avdd_supply);
+ if (err < 0) {
+ dev_err(pcie->dev,
+ "failed to enable AVDD regulator: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
+ pcie->pex_clk,
+ pcie->pex_rst);
+ if (err) {
+ dev_err(pcie->dev, "powerup sequence failed: %d\n", err);
+ return err;
+ }
+
+ reset_control_deassert(pcie->afi_rst);
+
+ err = clk_enable(pcie->afi_clk);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable AFI clock: %d\n", err);
+ return err;
+ }
+
+ if (soc->has_cml_clk) {
+ err = clk_enable(pcie->cml_clk);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable CML clock: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ err = clk_enable(pcie->pll_e);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+
+ pcie->pex_clk = clk_get(pcie->dev, "pex");
+ if (IS_ERR(pcie->pex_clk))
+ return PTR_ERR(pcie->pex_clk);
+
+ pcie->afi_clk = clk_get(pcie->dev, "afi");
+ if (IS_ERR(pcie->afi_clk))
+ return PTR_ERR(pcie->afi_clk);
+
+ pcie->pll_e = clk_get(pcie->dev, "pll_e");
+ if (IS_ERR(pcie->pll_e))
+ return PTR_ERR(pcie->pll_e);
+
+ if (soc->has_cml_clk) {
+ pcie->cml_clk = clk_get(pcie->dev, "cml");
+ if (IS_ERR(pcie->cml_clk))
+ return PTR_ERR(pcie->cml_clk);
+ }
+
+ return 0;
+}
+
+static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
+{
+ pcie->pex_rst = reset_control_get(pcie->dev, "pex");
+ if (IS_ERR(pcie->pex_rst))
+ return PTR_ERR(pcie->pex_rst);
+
+ pcie->afi_rst = reset_control_get(pcie->dev, "afi");
+ if (IS_ERR(pcie->afi_rst))
+ return PTR_ERR(pcie->afi_rst);
+
+ pcie->pcie_xrst = reset_control_get(pcie->dev, "pcie_x");
+ if (IS_ERR(pcie->pcie_xrst))
+ return PTR_ERR(pcie->pcie_xrst);
+
+ return 0;
+}
+
+static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
+{
+ struct device_d *dev = pcie->dev;
+ int err;
+
+ err = tegra_pcie_clocks_get(pcie);
+ if (err) {
+ dev_err(dev, "failed to get clocks: %d\n", err);
+ return err;
+ }
+
+ err = tegra_pcie_resets_get(pcie);
+ if (err) {
+ dev_err(dev, "failed to get resets: %d\n", err);
+ return err;
+ }
+
+ pcie->phy = phy_optional_get(pcie->dev, "pcie");
+ if (IS_ERR(pcie->phy)) {
+ err = PTR_ERR(pcie->phy);
+ dev_err(dev, "failed to get PHY: %d\n", err);
+ return err;
+ }
+
+ err = phy_init(pcie->phy);
+ if (err < 0) {
+ dev_err(dev, "failed to initialize PHY: %d\n", err);
+ return err;
+ }
+
+ err = tegra_pcie_power_on(pcie);
+ if (err) {
+ dev_err(dev, "failed to power up: %d\n", err);
+ return err;
+ }
+
+ pcie->pads = dev_request_mem_region_by_name(dev, "pads");
+ if (IS_ERR(pcie->pads)) {
+ err = PTR_ERR(pcie->pads);
+ goto poweroff;
+ }
+
+ pcie->afi = dev_request_mem_region_by_name(dev, "afi");
+ if (IS_ERR(pcie->afi)) {
+ err = PTR_ERR(pcie->afi);
+ goto poweroff;
+ }
+
+ pcie->cs_res = dev_get_resource_by_name(dev, IORESOURCE_MEM, "cs");
+ pcie->cs = dev_request_mem_region_by_name(dev, "cs");
+ if (IS_ERR(pcie->cs)) {
+ err = PTR_ERR(pcie->cs);
+ goto poweroff;
+ }
+
+ return 0;
+
+poweroff:
+ tegra_pcie_power_off(pcie);
+ return err;
+}
+
+static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
+{
+ int err;
+
+ tegra_pcie_power_off(pcie);
+
+ err = phy_exit(pcie->phy);
+ if (err < 0)
+ dev_err(pcie->dev, "failed to teardown PHY: %d\n", err);
+
+ return 0;
+}
+
+static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
+ u32 *xbar)
+{
+ struct device_node *np = pcie->dev->device_node;
+
+ if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+ switch (lanes) {
+ case 0x0000104:
+ dev_info(pcie->dev, "4x1, 1x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1;
+ return 0;
+
+ case 0x0000102:
+ dev_info(pcie->dev, "2x1, 1x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1;
+ return 0;
+ }
+ } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+ switch (lanes) {
+ case 0x00000204:
+ dev_info(pcie->dev, "4x1, 2x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420;
+ return 0;
+
+ case 0x00020202:
+ dev_info(pcie->dev, "2x3 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222;
+ return 0;
+
+ case 0x00010104:
+ dev_info(pcie->dev, "4x1, 1x2 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411;
+ return 0;
+ }
+ } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
+ switch (lanes) {
+ case 0x00000004:
+ dev_info(pcie->dev, "single-mode configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE;
+ return 0;
+
+ case 0x00000202:
+ dev_info(pcie->dev, "dual-mode configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ struct device_node *np = pcie->dev->device_node, *port;
+ struct of_pci_range_parser parser;
+ struct of_pci_range range;
+ struct resource *rp_res;
+ struct resource res;
+ u32 lanes = 0;
+ int err;
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(pcie->dev, "missing \"ranges\" property\n");
+ return -EINVAL;
+ }
+
+ pcie->vdd_supply = regulator_get(pcie->dev, "vdd");
+ if (IS_ERR(pcie->vdd_supply))
+ return PTR_ERR(pcie->vdd_supply);
+
+ pcie->pex_clk_supply = regulator_get(pcie->dev, "pex-clk");
+ if (IS_ERR(pcie->pex_clk_supply))
+ return PTR_ERR(pcie->pex_clk_supply);
+
+ if (soc->has_avdd_supply) {
+ pcie->avdd_supply = regulator_get(pcie->dev, "avdd");
+ if (IS_ERR(pcie->avdd_supply))
+ return PTR_ERR(pcie->avdd_supply);
+ }
+
+ for_each_of_pci_range(&parser, &range) {
+ of_pci_range_to_resource(&range, np, &res);
+
+ switch (res.flags & IORESOURCE_TYPE_BITS) {
+ case IORESOURCE_IO:
+ memcpy(&pcie->io, &res, sizeof(res));
+ pcie->io.name = "I/O";
+ break;
+
+ case IORESOURCE_MEM:
+ if (res.flags & IORESOURCE_PREFETCH) {
+ memcpy(&pcie->prefetch, &res, sizeof(res));
+ pcie->prefetch.name = "PREFETCH";
+ } else {
+ memcpy(&pcie->mem, &res, sizeof(res));
+ pcie->mem.name = "MEM";
+ }
+ break;
+ }
+ }
+#if 0
+ 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;
+ }
+#endif
+ /* parse root ports */
+ for_each_child_of_node(np, port) {
+ struct tegra_pcie_port *rp;
+ unsigned int index;
+ u32 value;
+
+ 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 < 1 || index > soc->num_ports) {
+ dev_err(pcie->dev, "invalid port number: %d\n", index);
+ return -EINVAL;
+ }
+
+ index--;
+
+ err = of_property_read_u32(port, "nvidia,num-lanes", &value);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to parse # of lanes: %d\n",
+ err);
+ return err;
+ }
+
+ if (value > 16) {
+ dev_err(pcie->dev, "invalid # of lanes: %u\n", value);
+ return -EINVAL;
+ }
+
+ lanes |= value << (index << 3);
+
+ if (!of_device_is_available(port))
+ continue;
+
+ rp = kzalloc(sizeof(*rp), GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ err = of_address_to_resource(port, 0, &rp->regs);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to parse address: %d\n",
+ err);
+ return err;
+ }
+
+ /*
+ * if the port address is inside the NULL page we alias this
+ * range at +1MB and fix up the resource, otherwise just request
+ */
+ if (!(rp->regs.start & 0xfffff000)) {
+ rp_res = request_iomem_region(dev_name(pcie->dev),
+ rp->regs.start + SZ_1M,
+ rp->regs.end + SZ_1M);
+ if (!rp_res)
+ return -ENOMEM;
+
+ map_io_sections(rp->regs.start,
+ (void *)rp_res->start, SZ_1M);
+
+ rp->regs.start = rp_res->start;
+ rp->regs.end = rp_res->end;
+ } else {
+ rp_res = request_iomem_region(dev_name(pcie->dev),
+ rp->regs.start, rp->regs.end);
+ if (!rp_res)
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&rp->list);
+ rp->index = index;
+ rp->lanes = value;
+ rp->pcie = pcie;
+
+ rp->base = (void __force __iomem *)rp->regs.start;
+ if (IS_ERR(rp->base))
+ return PTR_ERR(rp->base);
+
+ list_add_tail(&rp->list, &pcie->ports);
+ }
+
+ err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config);
+ if (err < 0) {
+ dev_err(pcie->dev, "invalid lane configuration\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * FIXME: If there are no PCIe cards attached, then calling this function
+ * can result in the increase of the bootup time as there are big timeout
+ * loops.
+ */
+#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */
+static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
+{
+ unsigned int timeout;
+ unsigned int retries = 2;
+ u32 value;
+
+ /* override presence detection */
+ value = readl(port->base + RP_PRIV_MISC);
+ value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT;
+ value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT;
+ writel(value, port->base + RP_PRIV_MISC);
+
+ do {
+ timeout = wait_on_timeout(50 * MSECOND,
+ readl(port->regs.start + RP_VEND_XP) & RP_VEND_XP_DL_UP);
+
+ if (timeout) {
+ dev_dbg(port->pcie->dev, "link %u down, retrying\n",
+ port->index);
+ goto retry;
+ }
+
+ timeout = wait_on_timeout(200 * MSECOND,
+ readl(port->regs.start + RP_LINK_CONTROL_STATUS) &
+ RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE);
+
+ if (!timeout)
+ return true;
+
+retry:
+ tegra_pcie_port_reset(port);
+ } while (--retries);
+
+ return false;
+}
+
+static int tegra_pcie_enable(struct tegra_pcie *pcie)
+{
+ struct tegra_pcie_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ dev_dbg(pcie->dev, "probing port %u, using %u lanes\n",
+ port->index, port->lanes);
+
+ tegra_pcie_port_enable(port);
+
+ if (tegra_pcie_port_check_link(port))
+ continue;
+
+ dev_dbg(pcie->dev, "link %u down, ignoring\n", port->index);
+
+ tegra_pcie_port_disable(port);
+ tegra_pcie_port_free(port);
+ }
+
+ pcie->pci.parent = pcie->dev;
+ pcie->pci.pci_ops = &tegra_pcie_ops;
+ pcie->pci.mem_resource = &pcie->mem;
+ pcie->pci.mem_pref_resource = &pcie->prefetch;
+ pcie->pci.io_resource = &pcie->io;
+
+ register_pci_controller(&pcie->pci);
+
+ return 0;
+}
+
+static const struct tegra_pcie_soc_data tegra20_pcie_data = {
+ .num_ports = 2,
+ .msi_base_shift = 0,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10,
+ .has_pex_clkreq_en = false,
+ .has_pex_bias_ctrl = false,
+ .has_intr_prsnt_sense = false,
+ .has_avdd_supply = false,
+ .has_cml_clk = false,
+ .has_gen2 = false,
+};
+
+static const struct tegra_pcie_soc_data tegra30_pcie_data = {
+ .num_ports = 3,
+ .msi_base_shift = 8,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+ .has_pex_clkreq_en = true,
+ .has_pex_bias_ctrl = true,
+ .has_intr_prsnt_sense = true,
+ .has_avdd_supply = true,
+ .has_cml_clk = true,
+ .has_gen2 = false,
+};
+
+static const struct tegra_pcie_soc_data tegra124_pcie_data = {
+ .num_ports = 2,
+ .msi_base_shift = 8,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+ .has_pex_clkreq_en = true,
+ .has_pex_bias_ctrl = true,
+ .has_intr_prsnt_sense = true,
+ .has_cml_clk = true,
+ .has_gen2 = true,
+};
+
+static __maybe_unused struct of_device_id tegra_pcie_of_match[] = {
+ {
+ .compatible = "nvidia,tegra124-pcie",
+ .data = (unsigned long)&tegra124_pcie_data
+ }, {
+ .compatible = "nvidia,tegra30-pcie",
+ .data = (unsigned long)&tegra30_pcie_data
+ }, {
+ .compatible = "nvidia,tegra20-pcie",
+ .data = (unsigned long)&tegra20_pcie_data
+ }, {
+ /* sentinel */
+ },
+};
+
+static int tegra_pcie_probe(struct device_d *dev)
+{
+ struct tegra_pcie *pcie;
+ int err;
+
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&pcie->buses);
+ INIT_LIST_HEAD(&pcie->ports);
+ dev_get_drvdata(dev, (unsigned long *)&pcie->soc_data);
+ pcie->dev = dev;
+
+ err = tegra_pcie_parse_dt(pcie);
+ if (err < 0) {
+ printk("parse DT failed\n");
+ return err;
+ }
+
+ err = tegra_pcie_get_resources(pcie);
+ if (err < 0) {
+ dev_err(dev, "failed to request resources: %d\n", err);
+ return err;
+ }
+
+ err = tegra_pcie_enable_controller(pcie);
+ if (err)
+ goto put_resources;
+
+ /* setup the AFI address translations */
+ tegra_pcie_setup_translations(pcie);
+
+ err = tegra_pcie_enable(pcie);
+ if (err < 0) {
+ dev_err(dev, "failed to enable PCIe ports: %d\n", err);
+ goto put_resources;
+ }
+
+ return 0;
+
+put_resources:
+ tegra_pcie_put_resources(pcie);
+ return err;
+}
+
+static struct driver_d tegra_pcie_driver = {
+ .name = "tegra-pcie",
+ .of_compatible = DRV_OF_COMPAT(tegra_pcie_of_match),
+ .probe = tegra_pcie_probe,
+};
+device_platform_driver(tegra_pcie_driver);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a1b7680254..ba9d097230 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -12,6 +12,9 @@ static struct pci_controller *hose_head, **hose_tail = &hose_head;
LIST_HEAD(pci_root_buses);
EXPORT_SYMBOL(pci_root_buses);
static u8 bus_index;
+static resource_size_t last_mem;
+static resource_size_t last_mem_pref;
+static resource_size_t last_io;
static struct pci_bus *pci_alloc_bus(void)
{
@@ -37,14 +40,32 @@ void register_pci_controller(struct pci_controller *hose)
bus = pci_alloc_bus();
hose->bus = bus;
+ bus->parent = hose->parent;
bus->host = hose;
bus->ops = hose->pci_ops;
- bus->resource[0] = hose->mem_resource;
- bus->resource[1] = hose->io_resource;
+ bus->resource[PCI_BUS_RESOURCE_MEM] = hose->mem_resource;
+ bus->resource[PCI_BUS_RESOURCE_MEM_PREF] = hose->mem_pref_resource;
+ bus->resource[PCI_BUS_RESOURCE_IO] = hose->io_resource;
bus->number = bus_index++;
if (hose->set_busno)
hose->set_busno(hose, bus->number);
+
+ if (bus->resource[PCI_BUS_RESOURCE_MEM])
+ last_mem = bus->resource[PCI_BUS_RESOURCE_MEM]->start;
+ else
+ last_mem = 0;
+
+ if (bus->resource[PCI_BUS_RESOURCE_MEM])
+ last_mem_pref = bus->resource[PCI_BUS_RESOURCE_MEM_PREF]->start;
+ else
+ last_mem_pref = 0;
+
+ if (bus->resource[PCI_BUS_RESOURCE_IO])
+ last_io = bus->resource[PCI_BUS_RESOURCE_IO]->start;
+ else
+ last_io = 0;
+
pci_scan_bus(bus);
list_add_tail(&bus->node, &pci_root_buses);
@@ -111,27 +132,162 @@ static struct pci_dev *alloc_pci_dev(void)
return dev;
}
+static void setup_device(struct pci_dev *dev, int max_bar)
+{
+ int bar, size;
+ u32 mask;
+ u8 cmd;
+
+ pci_read_config_byte(dev, PCI_COMMAND, &cmd);
+ pci_write_config_byte(dev, PCI_COMMAND,
+ cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+
+ for (bar = 0; bar < max_bar; bar++) {
+ resource_size_t last_addr;
+
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, 0xfffffffe);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &mask);
+
+ if (mask == 0 || mask == 0xffffffff) {
+ DBG(" PCI: pbar%d set bad mask\n", bar);
+ continue;
+ }
+
+ if (mask & 0x01) { /* IO */
+ size = -(mask & 0xfffffffe);
+ DBG(" PCI: pbar%d: mask=%08x io %d bytes\n", bar, mask, size);
+ if (last_io + size >
+ dev->bus->resource[PCI_BUS_RESOURCE_IO]->end) {
+ DBG("BAR does not fit within bus IO res\n");
+ return;
+ }
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_io);
+ dev->resource[bar].flags = IORESOURCE_IO;
+ last_addr = last_io;
+ last_io += size;
+ } else if ((mask & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
+ last_mem_pref) /* prefetchable MEM */ {
+ size = -(mask & 0xfffffff0);
+ DBG(" PCI: pbar%d: mask=%08x P memory %d bytes\n",
+ bar, mask, size);
+ if (last_mem_pref + size >
+ dev->bus->resource[PCI_BUS_RESOURCE_MEM_PREF]->end) {
+ DBG("BAR does not fit within bus p-mem res\n");
+ return;
+ }
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem_pref);
+ dev->resource[bar].flags = IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+ last_addr = last_mem_pref;
+ last_mem_pref += size;
+ } else { /* non-prefetch MEM */
+ size = -(mask & 0xfffffff0);
+ DBG(" PCI: pbar%d: mask=%08x NP memory %d bytes\n",
+ bar, mask, size);
+ if (last_mem + size >
+ dev->bus->resource[PCI_BUS_RESOURCE_MEM]->end) {
+ DBG("BAR does not fit within bus np-mem res\n");
+ return;
+ }
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem);
+ dev->resource[bar].flags = IORESOURCE_MEM;
+ last_addr = last_mem;
+ last_mem += size;
+ }
+
+ dev->resource[bar].start = last_addr;
+ dev->resource[bar].end = last_addr + size - 1;
+
+ if ((mask & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+ PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ dev->resource[bar].flags |= IORESOURCE_MEM_64;
+ pci_write_config_dword(dev,
+ PCI_BASE_ADDRESS_1 + bar * 4, 0);
+ bar++;
+ }
+ }
+
+ pci_write_config_byte(dev, PCI_COMMAND, cmd);
+ list_add_tail(&dev->bus_list, &dev->bus->devices);
+}
+
+static void prescan_setup_bridge(struct pci_dev *dev)
+{
+ u16 cmdstat;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmdstat);
+
+ /* Configure bus number registers */
+ pci_write_config_byte(dev, PCI_PRIMARY_BUS, dev->bus->number);
+ pci_write_config_byte(dev, PCI_SECONDARY_BUS, dev->subordinate->number);
+ pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, 0xff);
+
+ if (last_mem) {
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ pci_write_config_word(dev, PCI_MEMORY_BASE,
+ (last_mem & 0xfff00000) >> 16);
+ cmdstat |= PCI_COMMAND_MEMORY;
+ }
+
+ if (last_mem_pref) {
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ pci_write_config_word(dev, PCI_PREF_MEMORY_BASE,
+ (last_mem_pref & 0xfff00000) >> 16);
+ cmdstat |= PCI_COMMAND_MEMORY;
+ } else {
+
+ /* We don't support prefetchable memory for now, so disable */
+ pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0x1000);
+ pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0x0);
+ }
+
+ if (last_io) {
+ pci_write_config_byte(dev, PCI_IO_BASE,
+ (last_io & 0x0000f000) >> 8);
+ pci_write_config_word(dev, PCI_IO_BASE_UPPER16,
+ (last_io & 0xffff0000) >> 16);
+ cmdstat |= PCI_COMMAND_IO;
+ }
+
+ /* Enable memory and I/O accesses, enable bus master */
+ pci_write_config_word(dev, PCI_COMMAND, cmdstat | PCI_COMMAND_MASTER);
+}
+
+static void postscan_setup_bridge(struct pci_dev *dev)
+{
+ /* limit subordinate to last used bus number */
+ pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, bus_index - 1);
+
+ if (last_mem)
+ pci_write_config_word(dev, PCI_MEMORY_LIMIT,
+ ((last_mem - 1) & 0xfff00000) >> 16);
+
+ if (last_mem_pref)
+ pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT,
+ ((last_mem_pref - 1) & 0xfff00000) >> 16);
+
+ if (last_io) {
+ pci_write_config_byte(dev, PCI_IO_LIMIT,
+ ((last_io - 1) & 0x0000f000) >> 8);
+ pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16,
+ ((last_io - 1) & 0xffff0000) >> 16);
+ }
+}
+
unsigned int pci_scan_bus(struct pci_bus *bus)
{
+ struct pci_dev *dev;
+ struct pci_bus *child_bus;
unsigned int devfn, l, max, class;
unsigned char cmd, tmp, hdr_type, is_multi = 0;
- struct pci_dev *dev;
- resource_size_t last_mem;
- resource_size_t last_io;
-
- /* FIXME: use res_start() */
- last_mem = bus->resource[0]->start;
- last_io = bus->resource[1]->start;
DBG("pci_scan_bus for bus %d\n", bus->number);
- DBG(" last_io = 0x%08x, last_mem = 0x%08x\n", last_io, last_mem);
+ DBG(" last_io = 0x%08x, last_mem = 0x%08x, last_mem_pref = 0x%08x\n",
+ last_io, last_mem, last_mem_pref);
+
max = bus->secondary;
for (devfn = 0; devfn < 0xff; ++devfn) {
- int bar;
- u32 old_bar, mask;
- int size;
-
if (PCI_FUNC(devfn) && !is_multi) {
/* not a multi-function device */
continue;
@@ -154,6 +310,7 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
dev->devfn = devfn;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
+ dev->dev.parent = bus->parent;
/* non-destructively determine if device can be a master: */
pci_read_config_byte(dev, PCI_COMMAND, &cmd);
@@ -169,9 +326,11 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
dev->hdr_type = hdr_type;
DBG("PCI: class = %08x, hdr_type = %08x\n", class, hdr_type);
+ DBG("PCI: %02x:%02x [%04x:%04x]\n", bus->number, dev->devfn,
+ dev->vendor, dev->device);
- switch (hdr_type & 0x7f) { /* header type */
- case PCI_HEADER_TYPE_NORMAL: /* standard header */
+ switch (hdr_type & 0x7f) {
+ case PCI_HEADER_TYPE_NORMAL:
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
@@ -181,70 +340,48 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
*/
pci_read_config_dword(dev, PCI_ROM_ADDRESS, &l);
dev->rom_address = (l == 0xffffffff) ? 0 : l;
+
+ setup_device(dev, 6);
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ setup_device(dev, 2);
+
+ child_bus = pci_alloc_bus();
+ /* inherit parent properties */
+ child_bus->host = bus->host;
+ child_bus->ops = bus->host->pci_ops;
+ child_bus->resource[PCI_BUS_RESOURCE_MEM] =
+ bus->resource[PCI_BUS_RESOURCE_MEM];
+ child_bus->resource[PCI_BUS_RESOURCE_MEM_PREF] =
+ bus->resource[PCI_BUS_RESOURCE_MEM_PREF];
+ child_bus->resource[PCI_BUS_RESOURCE_IO] =
+ bus->resource[PCI_BUS_RESOURCE_IO];
+
+ child_bus->parent = &dev->dev;
+ child_bus->number = bus_index++;
+ list_add_tail(&child_bus->node, &bus->children);
+ dev->subordinate = child_bus;
+
+ prescan_setup_bridge(dev);
+ pci_scan_bus(child_bus);
+ postscan_setup_bridge(dev);
+ /* first activate bridge then all devices on it's bus */
+ pci_register_device(dev);
+ list_for_each_entry(dev, &child_bus->devices, bus_list)
+ if (!dev->subordinate)
+ pci_register_device(dev);
break;
- default: /* unknown header */
+ default:
bad:
printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type);
continue;
}
- DBG("PCI: %02x:%02x [%04x/%04x]\n", bus->number, dev->devfn, dev->vendor, dev->device);
-
if (class == PCI_CLASS_BRIDGE_HOST) {
DBG("PCI: skip pci host bridge\n");
continue;
}
-
- pci_read_config_byte(dev, PCI_COMMAND, &cmd);
- pci_write_config_byte(dev, PCI_COMMAND,
- cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
-
- for (bar = 0; bar < 6; bar++) {
- resource_size_t last_addr;
-
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &old_bar);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, 0xfffffffe);
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &mask);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, old_bar);
-
- if (mask == 0 || mask == 0xffffffff) {
- DBG(" PCI: pbar%d set bad mask\n", bar);
- continue;
- }
-
- if (mask & 0x01) { /* IO */
- size = -(mask & 0xfffffffe);
- DBG(" PCI: pbar%d: mask=%08x io %d bytes\n", bar, mask, size);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_io);
- dev->resource[bar].flags = IORESOURCE_IO;
- last_addr = last_io;
- last_io += size;
- } else { /* MEM */
- size = -(mask & 0xfffffff0);
- DBG(" PCI: pbar%d: mask=%08x memory %d bytes\n", bar, mask, size);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem);
- dev->resource[bar].flags = IORESOURCE_MEM;
- last_addr = last_mem;
- last_mem += size;
-
- if ((mask & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
- PCI_BASE_ADDRESS_MEM_TYPE_64) {
- dev->resource[bar].flags |= IORESOURCE_MEM_64;
- pci_write_config_dword(dev,
- PCI_BASE_ADDRESS_1 + bar * 4, 0);
- }
- }
-
- dev->resource[bar].start = last_addr;
- dev->resource[bar].end = last_addr + size - 1;
- if (dev->resource[bar].flags & IORESOURCE_MEM_64)
- bar++;
- }
-
- pci_write_config_byte(dev, PCI_COMMAND, cmd);
- list_add_tail(&dev->bus_list, &bus->devices);
- pci_register_device(dev);
}
/*
@@ -254,6 +391,7 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
*
* Return how far we've got finding sub-buses.
*/
+ max = bus_index;
DBG("PCI: pci_scan_bus returning with max=%02x\n", max);
return max;
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
new file mode 100644
index 0000000000..e9461e1a25
--- /dev/null
+++ b/drivers/phy/Kconfig
@@ -0,0 +1,18 @@
+#
+# PHY
+#
+
+menu "PHY Subsystem"
+
+config GENERIC_PHY
+ bool "PHY Core"
+ help
+ Generic PHY support.
+
+ This framework is designed to provide a generic interface for PHY
+ devices present in the kernel. This layer will have the generic
+ API by which phy drivers can create PHY using the phy framework and
+ phy users can obtain reference to the PHY. All the users of this
+ framework should select this config.
+
+endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
new file mode 100644
index 0000000000..74514aeaed
--- /dev/null
+++ b/drivers/phy/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the phy drivers.
+#
+
+obj-$(CONFIG_GENERIC_PHY) += phy-core.o
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
new file mode 100644
index 0000000000..67af14f680
--- /dev/null
+++ b/drivers/phy/phy-core.c
@@ -0,0 +1,318 @@
+/*
+ * phy-core.c -- Generic Phy framework.
+ *
+ * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/phy/phy.h>
+
+static LIST_HEAD(phy_provider_list);
+static int phy_ida;
+
+/**
+ * phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @node: device node of the phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Called to create a phy using phy framework.
+ */
+struct phy *phy_create(struct device_d *dev, struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data)
+{
+ int ret;
+ int id;
+ struct phy *phy;
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return ERR_PTR(-ENOMEM);
+
+ id = phy_ida++;
+
+ snprintf(phy->dev.name, MAX_DRIVER_NAME, "phy");
+ phy->dev.id = id;
+ phy->dev.parent = dev;
+ phy->dev.device_node = node ?: dev->device_node;
+ phy->id = id;
+ phy->ops = ops;
+ phy->init_data = init_data;
+
+ ret = register_device(&phy->dev);
+ if (ret)
+ goto free_ida;
+
+ return phy;
+
+free_ida:
+ phy_ida--;
+ kfree(phy);
+ return ERR_PTR(ret);
+}
+
+/**
+ * __of_phy_provider_register() - create/register phy provider with the framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider.
+ */
+struct phy_provider *__of_phy_provider_register(struct device_d *dev,
+ struct phy * (*of_xlate)(struct device_d *dev,
+ struct of_phandle_args *args))
+{
+ struct phy_provider *phy_provider;
+
+ phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
+ if (!phy_provider)
+ return ERR_PTR(-ENOMEM);
+
+ phy_provider->dev = dev;
+ phy_provider->of_xlate = of_xlate;
+
+ list_add_tail(&phy_provider->list, &phy_provider_list);
+
+ return phy_provider;
+}
+
+/**
+ * of_phy_provider_unregister() - unregister phy provider from the framework
+ * @phy_provider: phy provider returned by of_phy_provider_register()
+ *
+ * Removes the phy_provider created using of_phy_provider_register().
+ */
+void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+ if (IS_ERR(phy_provider))
+ return;
+
+ list_del(&phy_provider->list);
+ kfree(phy_provider);
+}
+
+int phy_init(struct phy *phy)
+{
+ int ret;
+
+ if (!phy)
+ return 0;
+
+ if (phy->init_count == 0 && phy->ops->init) {
+ ret = phy->ops->init(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy init failed --> %d\n", ret);
+ return ret;
+ }
+ }
+ ++phy->init_count;
+
+ return 0;
+}
+
+int phy_exit(struct phy *phy)
+{
+ int ret;
+
+ if (!phy)
+ return 0;
+
+ if (phy->init_count == 1 && phy->ops->exit) {
+ ret = phy->ops->exit(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
+ return ret;
+ }
+ }
+ --phy->init_count;
+
+ return 0;
+}
+
+int phy_power_on(struct phy *phy)
+{
+ int ret;
+
+ if (!phy)
+ return 0;
+
+ if (phy->pwr) {
+ ret = regulator_enable(phy->pwr);
+ if (ret)
+ return ret;
+ }
+
+ if (phy->power_count == 0 && phy->ops->power_on) {
+ ret = phy->ops->power_on(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
+ goto out;
+ }
+ } else {
+ ret = 0; /* Override possible ret == -ENOTSUPP */
+ }
+ ++phy->power_count;
+
+ return 0;
+
+out:
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
+
+ return ret;
+}
+
+int phy_power_off(struct phy *phy)
+{
+ int ret;
+
+ if (!phy)
+ return 0;
+
+ if (phy->power_count == 1 && phy->ops->power_off) {
+ ret = phy->ops->power_off(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
+ return ret;
+ }
+ }
+ --phy->power_count;
+
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
+
+ return 0;
+}
+
+static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
+{
+ struct phy_provider *phy_provider;
+ struct device_node *child;
+
+ list_for_each_entry(phy_provider, &phy_provider_list, list) {
+ if (phy_provider->dev->device_node == node)
+ return phy_provider;
+
+ for_each_child_of_node(phy_provider->dev->device_node, child)
+ if (child == node)
+ return phy_provider;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
+/**
+ * _of_phy_get() - lookup and obtain a reference to a phy by phandle
+ * @np: device_node for which to get the phy
+ * @index: the index of the phy
+ *
+ * Returns the phy associated with the given phandle value,
+ * after getting a refcount to it or -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. This function uses of_xlate call back function provided
+ * while registering the phy_provider to find the phy instance.
+ */
+static struct phy *_of_phy_get(struct device_node *np, int index)
+{
+ int ret;
+ struct phy_provider *phy_provider;
+ struct of_phandle_args args;
+
+ ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
+ index, &args);
+ if (ret)
+ return ERR_PTR(-ENODEV);
+
+ phy_provider = of_phy_provider_lookup(args.np);
+ if (IS_ERR(phy_provider)) {
+ return ERR_PTR(-ENODEV);
+ }
+
+ return phy_provider->of_xlate(phy_provider->dev, &args);
+}
+
+/**
+ * of_phy_get() - lookup and obtain a reference to a phy using a device_node.
+ * @np: device_node for which to get the phy
+ * @con_id: name of the phy from device's point of view
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *of_phy_get(struct device_node *np, const char *con_id)
+{
+ int index = 0;
+
+ if (con_id)
+ index = of_property_match_string(np, "phy-names", con_id);
+
+ return _of_phy_get(np, index);
+}
+
+/**
+ * phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *phy_get(struct device_d *dev, const char *string)
+{
+ int index = 0;
+ struct phy *phy = ERR_PTR(-ENODEV);
+
+ if (string == NULL) {
+ dev_warn(dev, "missing string\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (dev->device_node) {
+ index = of_property_match_string(dev->device_node, "phy-names",
+ string);
+ phy = _of_phy_get(dev->device_node, index);
+ }
+
+ return phy;
+}
+
+/**
+ * phy_optional_get() - lookup and obtain a reference to an optional phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * NULL if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *phy_optional_get(struct device_d *dev, const char *string)
+{
+ struct phy *phy = phy_get(dev, string);
+
+ if (PTR_ERR(phy) == -ENODEV)
+ phy = NULL;
+
+ return phy;
+}
+
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 770fb2d414..4cbc167cd0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -60,6 +60,14 @@ config PINCTRL_TEGRA30
help
The pinmux controller found on the Tegra 30+ line of SoCs.
+config PINCTRL_TEGRA_XUSB
+ bool
+ default y if ARCH_TEGRA_124_SOC
+ select GENERIC_PHY
+ help
+ The pinmux controller found on the Tegra 124 line of SoCs used for
+ the SerDes lanes.
+
source drivers/pinctrl/mvebu/Kconfig
endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 3ea8649efb..724e6d7d06 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
+obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o
obj-$(CONFIG_ARCH_MVEBU) += mvebu/
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
new file mode 100644
index 0000000000..05cdecbabf
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * Partly based on code
+ * Copyright (C) 2014, NVIDIA CORPORATION.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <pinctrl.h>
+#include <linux/err.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+
+#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
+
+#define XUSB_PADCTL_ELPG_PROGRAM 0x01c
+#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
+#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
+#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
+
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf << 12)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1)
+
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
+
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
+
+struct tegra_xusb_padctl_soc {
+ const struct tegra_xusb_padctl_lane *lanes;
+ unsigned int num_lanes;
+};
+
+struct tegra_xusb_padctl_lane {
+ const char *name;
+
+ unsigned int offset;
+ unsigned int shift;
+ unsigned int mask;
+ unsigned int iddq;
+
+ const char **funcs;
+ unsigned int num_funcs;
+};
+
+struct tegra_xusb_padctl {
+ struct device_d *dev;
+ void __iomem *regs;
+ struct reset_control *rst;
+
+ const struct tegra_xusb_padctl_soc *soc;
+ struct pinctrl_device pinctrl;
+
+ struct phy_provider *provider;
+ struct phy *phys[2];
+
+ unsigned int enable;
+};
+
+static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
+ unsigned long offset)
+{
+ writel(value, padctl->regs + offset);
+}
+
+static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
+ unsigned long offset)
+{
+ return readl(padctl->regs + offset);
+}
+
+static int tegra_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ if (padctl->enable++ > 0)
+ return 0;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ udelay(100);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ udelay(100);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ return 0;
+}
+
+static int tegra_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ if (WARN_ON(padctl->enable == 0))
+ return 0;
+
+ if (--padctl->enable > 0)
+ return 0;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ udelay(100);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ udelay(100);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ return 0;
+}
+
+static int tegra_xusb_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+
+ return tegra_xusb_padctl_enable(padctl);
+}
+
+static int tegra_xusb_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+
+ return tegra_xusb_padctl_disable(padctl);
+}
+
+static int pcie_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int err;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
+ value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN |
+ XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN |
+ XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+
+ err = wait_on_timeout(50 * MSECOND,
+ padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1) &
+ XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET);
+
+ return err;
+}
+
+static int pcie_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+
+ return 0;
+}
+
+static const struct phy_ops pcie_phy_ops = {
+
+ .init = tegra_xusb_phy_init,
+ .exit = tegra_xusb_phy_exit,
+ .power_on = pcie_phy_power_on,
+ .power_off = pcie_phy_power_off,
+};
+
+static int sata_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int err;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+ value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ err = wait_on_timeout(50 * MSECOND,
+ padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1) &
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET);
+
+ return err;
+}
+
+static int sata_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+ value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+ value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+
+ return 0;
+}
+
+static const struct phy_ops sata_phy_ops = {
+
+ .init = tegra_xusb_phy_init,
+ .exit = tegra_xusb_phy_exit,
+ .power_on = sata_phy_power_on,
+ .power_off = sata_phy_power_off,
+};
+
+static struct phy *tegra_xusb_padctl_xlate(struct device_d *dev,
+ struct of_phandle_args *args)
+{
+ struct tegra_xusb_padctl *padctl = dev->priv;
+ unsigned int index = args->args[0];
+
+ if (args->args_count <= 0)
+ return ERR_PTR(-EINVAL);
+
+ if (index >= ARRAY_SIZE(padctl->phys))
+ return ERR_PTR(-EINVAL);
+
+ return padctl->phys[index];
+}
+
+static int pinctrl_tegra_xusb_set_state(struct pinctrl_device *pdev,
+ struct device_node *np)
+{
+ struct tegra_xusb_padctl *padctl =
+ container_of(pdev, struct tegra_xusb_padctl, pinctrl);
+ struct device_node *childnode;
+ int iddq = -1, i, j, k;
+ const char *lanes, *func = NULL;
+ const struct tegra_xusb_padctl_lane *lane = NULL;
+ u32 val;
+
+ /*
+ * At first look if the node we are pointed at has children,
+ * which we may want to visit.
+ */
+ list_for_each_entry(childnode, &np->children, parent_list)
+ pinctrl_tegra_xusb_set_state(pdev, childnode);
+
+ /* read relevant state from devicetree */
+ of_property_read_string(np, "nvidia,function", &func);
+ of_property_read_u32_array(np, "nvidia,iddq", &iddq, 1);
+
+ /* iterate over all lanes referenced in the dt node */
+ for (i = 0; ; i++) {
+ if (of_property_read_string_index(np, "nvidia,lanes", i, &lanes))
+ break;
+
+ for (j = 0; j < padctl->soc->num_lanes; j++) {
+ if (!strcmp(lanes, padctl->soc->lanes[j].name)) {
+ lane = &padctl->soc->lanes[j];
+ break;
+ }
+ }
+ /* if no matching lane is found */
+ if (j == padctl->soc->num_lanes) {
+ /* nothing matching found, warn and bail out */
+ dev_warn(padctl->pinctrl.dev,
+ "invalid lane %s referenced in node %s\n",
+ lanes, np->name);
+ continue;
+ }
+
+ if (func) {
+ for (k = 0; k < lane->num_funcs; k++) {
+ if (!strcmp(func, lane->funcs[k]))
+ break;
+ }
+ if (k < lane->num_funcs) {
+ val = padctl_readl(padctl, lane->offset);
+ val &= ~(lane->mask << lane->shift);
+ val |= k << lane->shift;
+ padctl_writel(padctl, val, lane->offset);
+ } else {
+ dev_warn(padctl->pinctrl.dev,
+ "invalid function %s for lane %s in node %s\n",
+ func, lane->name, np->name);
+ }
+ }
+
+ if (iddq >= 0) {
+ if (lane->iddq) {
+ val = padctl_readl(padctl, lane->offset);
+ if (iddq)
+ val &= ~BIT(lane->iddq);
+ else
+ val |= BIT(lane->iddq);
+ padctl_writel(padctl, val, lane->offset);
+ } else {
+ dev_warn(padctl->pinctrl.dev,
+ "invalid iddq setting for lane %s in node %s\n",
+ lane->name, np->name);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops pinctrl_tegra_xusb_ops = {
+ .set_state = pinctrl_tegra_xusb_set_state,
+};
+
+static int pinctrl_tegra_xusb_probe(struct device_d *dev)
+{
+ struct tegra_xusb_padctl *padctl;
+ struct phy *phy;
+ int err;
+
+ padctl = xzalloc(sizeof(*padctl));
+
+ dev->priv = padctl;
+ padctl->dev = dev;
+
+ dev_get_drvdata(dev, (unsigned long *)&padctl->soc);
+
+ padctl->regs = dev_request_mem_region(dev, 0);
+ if (IS_ERR(padctl->regs)) {
+ dev_err(dev, "Could not get iomem region\n");
+ return PTR_ERR(padctl->regs);
+ }
+
+ padctl->rst = reset_control_get(dev, NULL);
+ if (IS_ERR(padctl->rst))
+ return PTR_ERR(padctl->rst);
+
+ err = reset_control_deassert(padctl->rst);
+ if (err < 0)
+ return err;
+
+ padctl->pinctrl.dev = dev;
+ padctl->pinctrl.ops = &pinctrl_tegra_xusb_ops;
+
+ err = pinctrl_register(&padctl->pinctrl);
+ if (err) {
+ dev_err(dev, "failed to register pincontrol\n");
+ err = -ENODEV;
+ goto reset;
+ }
+
+ phy = phy_create(dev, NULL, &pcie_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ err = PTR_ERR(phy);
+ goto unregister;
+ }
+
+ padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy;
+ phy_set_drvdata(phy, padctl);
+
+ phy = phy_create(dev, NULL, &sata_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ err = PTR_ERR(phy);
+ goto unregister;
+ }
+
+ padctl->phys[TEGRA_XUSB_PADCTL_SATA] = phy;
+ phy_set_drvdata(phy, padctl);
+
+ padctl->provider = of_phy_provider_register(dev, tegra_xusb_padctl_xlate);
+ if (IS_ERR(padctl->provider)) {
+ err = PTR_ERR(padctl->provider);
+ dev_err(dev, "failed to register PHYs: %d\n", err);
+ goto unregister;
+ }
+
+ return 0;
+
+unregister:
+ pinctrl_unregister(&padctl->pinctrl);
+reset:
+ reset_control_assert(padctl->rst);
+ return err;
+}
+
+static const char *tegra124_otg_functions[] = {
+ "snps",
+ "xusb",
+ "uart",
+ "rsvd",
+};
+
+static const char *tegra124_usb_functions[] = {
+ "snps",
+ "xusb",
+};
+
+static const char *tegra124_pci_functions[] = {
+ "pcie",
+ "usb3",
+ "sata",
+ "rsvd",
+};
+
+#define TEGRA124_LANE(_name, _offs, _shift, _mask, _iddq, _funcs, _num) \
+ { \
+ .name = _name, \
+ .offset = _offs, \
+ .shift = _shift, \
+ .mask = _mask, \
+ .iddq = _iddq, \
+ .num_funcs = _num, \
+ .funcs = tegra124_##_funcs##_functions, \
+ }
+
+static const struct tegra_xusb_padctl_lane tegra124_lanes[] = {
+ TEGRA124_LANE("otg-0", 0x004, 0, 0x3, 0, otg, 4),
+ TEGRA124_LANE("otg-1", 0x004, 2, 0x3, 0, otg, 4),
+ TEGRA124_LANE("otg-2", 0x004, 4, 0x3, 0, otg, 4),
+ TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, 0, usb, 2),
+ TEGRA124_LANE("hsic-0", 0x004, 14, 0x1, 0, usb, 2),
+ TEGRA124_LANE("hsic-1", 0x004, 15, 0x1, 0, usb, 2),
+ TEGRA124_LANE("pcie-0", 0x134, 16, 0x3, 1, pci, 4),
+ TEGRA124_LANE("pcie-1", 0x134, 18, 0x3, 2, pci, 4),
+ TEGRA124_LANE("pcie-2", 0x134, 20, 0x3, 3, pci, 4),
+ TEGRA124_LANE("pcie-3", 0x134, 22, 0x3, 4, pci, 4),
+ TEGRA124_LANE("pcie-4", 0x134, 24, 0x3, 5, pci, 4),
+ TEGRA124_LANE("sata-0", 0x134, 26, 0x3, 6, pci, 4),
+};
+
+static const struct tegra_xusb_padctl_soc tegra124_soc = {
+ .num_lanes = ARRAY_SIZE(tegra124_lanes),
+ .lanes = tegra124_lanes,
+};
+
+static __maybe_unused struct of_device_id pinctrl_tegra_xusb_dt_ids[] = {
+ {
+ .compatible = "nvidia,tegra124-xusb-padctl",
+ .data = (unsigned long)&tegra124_soc,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d pinctrl_tegra_xusb_driver = {
+ .name = "pinctrl-tegra-xusb",
+ .probe = pinctrl_tegra_xusb_probe,
+ .of_compatible = DRV_OF_COMPAT(pinctrl_tegra_xusb_dt_ids),
+};
+
+static int pinctrl_tegra_xusb_init(void)
+{
+ return platform_driver_register(&pinctrl_tegra_xusb_driver);
+}
+postcore_initcall(pinctrl_tegra_xusb_init);
diff --git a/drivers/pinctrl/pinctrl-tegra20.c b/drivers/pinctrl/pinctrl-tegra20.c
index 3c11be6f55..be9d8a996d 100644
--- a/drivers/pinctrl/pinctrl-tegra20.c
+++ b/drivers/pinctrl/pinctrl-tegra20.c
@@ -320,10 +320,14 @@ static int pinctrl_tegra20_probe(struct device_d *dev)
ctrl->pinctrl.ops = &pinctrl_tegra20_ops;
ret = pinctrl_register(&ctrl->pinctrl);
- if (ret)
+ if (ret) {
free(ctrl);
+ return ret;
+ }
+
+ of_pinctrl_select_state(dev->device_node, "boot");
- return ret;
+ return 0;
}
static __maybe_unused struct of_device_id pinctrl_tegra20_dt_ids[] = {
diff --git a/drivers/pinctrl/pinctrl-tegra30.c b/drivers/pinctrl/pinctrl-tegra30.c
index 82772187d5..aac67605cf 100644
--- a/drivers/pinctrl/pinctrl-tegra30.c
+++ b/drivers/pinctrl/pinctrl-tegra30.c
@@ -897,10 +897,14 @@ static int pinctrl_tegra30_probe(struct device_d *dev)
ctrl->pinctrl.ops = &pinctrl_tegra30_ops;
ret = pinctrl_register(&ctrl->pinctrl);
- if (ret)
+ if (ret) {
free(ctrl);
+ return ret;
+ }
+
+ of_pinctrl_select_state(dev->device_node, "boot");
- return ret;
+ return 0;
}
static __maybe_unused struct of_device_id pinctrl_tegra30_dt_ids[] = {
diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c
index 53d48a06c2..8f2e93fa68 100644
--- a/drivers/serial/serial_ns16550.c
+++ b/drivers/serial/serial_ns16550.c
@@ -311,6 +311,11 @@ static __maybe_unused struct ns16550_drvdata jz_drvdata = {
.init_port = ns16550_jz_init_port,
};
+static __maybe_unused struct ns16550_drvdata tegra_drvdata = {
+ .init_port = ns16550_serial_init_port,
+ .linux_console_name = "ttyS",
+};
+
static int ns16550_init_iomem(struct device_d *dev, struct ns16550_priv *priv)
{
struct resource *res;
@@ -418,6 +423,7 @@ static int ns16550_probe(struct device_d *dev)
ret = PTR_ERR(priv->clk);
goto err;
}
+ clk_enable(priv->clk);
priv->plat.clock = clk_get_rate(priv->clk);
}
@@ -476,6 +482,12 @@ static struct of_device_id ns16550_serial_dt_ids[] = {
.data = (unsigned long)&omap_drvdata,
},
#endif
+#if IS_ENABLED(CONFIG_ARCH_TEGRA)
+ {
+ .compatible = "nvidia,tegra20-uart",
+ .data = (unsigned long)&tegra_drvdata,
+ },
+#endif
#if IS_ENABLED(CONFIG_MACH_MIPS_XBURST)
{
.compatible = "ingenic,jz4740-uart",