diff options
Diffstat (limited to 'arch/arm/cpu')
-rw-r--r-- | arch/arm/cpu/arm926ejs/kirkwood/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/board.c | 3 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 26 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/cpu_info.c | 57 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/dram_sun4i.c | 17 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 77 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/dram_sun8i.c | 345 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/p2wi.c | 14 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/psci.S | 201 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/rsb.c | 158 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/sunxi/usbc.c | 272 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/cache.S | 2 |
13 files changed, 1085 insertions, 96 deletions
diff --git a/arch/arm/cpu/arm926ejs/kirkwood/Kconfig b/arch/arm/cpu/arm926ejs/kirkwood/Kconfig index 6c037a1..45c6687 100644 --- a/arch/arm/cpu/arm926ejs/kirkwood/Kconfig +++ b/arch/arm/cpu/arm926ejs/kirkwood/Kconfig @@ -57,6 +57,9 @@ config TARGET_DOCKSTAR config TARGET_GOFLEXHOME bool "GoFlex Home Board" +config TARGET_NAS220 + bool "BlackArmor NAS220" + endchoice config SYS_SOC @@ -80,5 +83,6 @@ source "board/LaCie/wireless_space/Kconfig" source "board/raidsonic/ib62x0/Kconfig" source "board/Seagate/dockstar/Kconfig" source "board/Seagate/goflexhome/Kconfig" +source "board/Seagate/nas220/Kconfig" endif diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 3b6ae47..1720f7d 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -10,10 +10,13 @@ obj-y += timer.o obj-y += board.o obj-y += clock.o +obj-y += cpu_info.o obj-y += pinmux.o +obj-y += usbc.o obj-$(CONFIG_MACH_SUN6I) += prcm.o obj-$(CONFIG_MACH_SUN8I) += prcm.o obj-$(CONFIG_MACH_SUN6I) += p2wi.o +obj-$(CONFIG_MACH_SUN8I) += rsb.o obj-$(CONFIG_MACH_SUN4I) += clock_sun4i.o obj-$(CONFIG_MACH_SUN5I) += clock_sun4i.o obj-$(CONFIG_MACH_SUN6I) += clock_sun6i.o @@ -21,7 +24,6 @@ obj-$(CONFIG_MACH_SUN7I) += clock_sun4i.o obj-$(CONFIG_MACH_SUN8I) += clock_sun6i.o ifndef CONFIG_SPL_BUILD -obj-y += cpu_info.o ifdef CONFIG_ARMV7_PSCI obj-y += psci.o endif @@ -32,6 +34,7 @@ obj-$(CONFIG_MACH_SUN4I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN5I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN6I) += dram_sun6i.o obj-$(CONFIG_MACH_SUN7I) += dram_sun4i.o +obj-$(CONFIG_MACH_SUN8I) += dram_sun8i.o ifdef CONFIG_SPL_FEL obj-y += start.o endif diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 9b3e80c..bc98c56 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -114,7 +114,8 @@ void reset_cpu(ulong addr) /* do some early init */ void s_init(void) { -#if defined CONFIG_SPL_BUILD && defined CONFIG_MACH_SUN6I +#if defined CONFIG_SPL_BUILD && \ + (defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I) /* Magic (undocmented) value taken from boot0, without this DRAM * access gets messed up (seems cache related) */ setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800); diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 8e949c6..d7a7040 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -97,6 +97,7 @@ void clock_set_pll1(unsigned int clk) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int p = 0; int k = 1; int m = 1; @@ -113,8 +114,11 @@ void clock_set_pll1(unsigned int clk) CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT, &ccm->cpu_axi_cfg); - /* PLL1 rate = 24000000 * n * k / m */ - writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_MAGIC | + /* + * sun6i: PLL1 rate = ((24000000 * n * k) >> 0) / m (p is ignored) + * sun8i: PLL1 rate = ((24000000 * n * k) >> p) / m + */ + writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) | CCM_PLL1_CTRL_N(clk / (24000000 * k / m)) | CCM_PLL1_CTRL_K(k) | CCM_PLL1_CTRL_M(m), &ccm->pll1_cfg); sdelay(200); @@ -144,15 +148,25 @@ void clock_set_pll3(unsigned int clk) &ccm->pll3_cfg); } -void clock_set_pll5(unsigned int clk) +void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - const int k = 2; - const int m = 1; + const int max_n = 32; + int k = 1, m = 2; + + if (sigma_delta_enable) + writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg); /* PLL5 rate = 24000000 * n * k / m */ - writel(CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD | + if (clk > 24000000 * k * max_n / m) { + m = 1; + if (clk > 24000000 * k * max_n / m) + k = 2; + } + writel(CCM_PLL5_CTRL_EN | + (sigma_delta_enable ? CCM_PLL5_CTRL_SIGMA_DELTA_EN : 0) | + CCM_PLL5_CTRL_UPD | CCM_PLL5_CTRL_N(clk / (24000000 * k / m)) | CCM_PLL5_CTRL_K(k) | CCM_PLL5_CTRL_M(m), &ccm->pll5_cfg); diff --git a/arch/arm/cpu/armv7/sunxi/cpu_info.c b/arch/arm/cpu/armv7/sunxi/cpu_info.c index 41b9add..b6cb9de 100644 --- a/arch/arm/cpu/armv7/sunxi/cpu_info.c +++ b/arch/arm/cpu/armv7/sunxi/cpu_info.c @@ -9,6 +9,33 @@ #include <common.h> #include <asm/io.h> #include <asm/arch/cpu.h> +#include <asm/arch/clock.h> +#include <axp221.h> + +#ifdef CONFIG_MACH_SUN6I +int sunxi_get_ss_bonding_id(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + static int bonding_id = -1; + + if (bonding_id != -1) + return bonding_id; + + /* Enable Security System */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_SS); + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_SS); + + bonding_id = readl(SUNXI_SS_BASE); + bonding_id = (bonding_id >> 16) & 0x7; + + /* Disable Security System again */ + clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_SS); + clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_SS); + + return bonding_id; +} +#endif #ifdef CONFIG_DISPLAY_CPUINFO int print_cpuinfo(void) @@ -24,7 +51,17 @@ int print_cpuinfo(void) default: puts("CPU: Allwinner A1X (SUN5I)\n"); } #elif defined CONFIG_MACH_SUN6I - puts("CPU: Allwinner A31 (SUN6I)\n"); + switch (sunxi_get_ss_bonding_id()) { + case SUNXI_SS_BOND_ID_A31: + puts("CPU: Allwinner A31 (SUN6I)\n"); + break; + case SUNXI_SS_BOND_ID_A31S: + puts("CPU: Allwinner A31s (SUN6I)\n"); + break; + default: + printf("CPU: Allwinner A31? (SUN6I, id: %d)\n", + sunxi_get_ss_bonding_id()); + } #elif defined CONFIG_MACH_SUN7I puts("CPU: Allwinner A20 (SUN7I)\n"); #elif defined CONFIG_MACH_SUN8I @@ -36,3 +73,21 @@ int print_cpuinfo(void) return 0; } #endif + +int sunxi_get_sid(unsigned int *sid) +{ +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I +#ifdef CONFIG_AXP221_POWER + return axp221_get_sid(sid); +#else + return -ENODEV; +#endif +#else + int i; + + for (i = 0; i< 4; i++) + sid[i] = readl(SUNXI_SID_BASE + 4 * i); + + return 0; +#endif +} diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun4i.c b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c index ec8aaa7..c736fa3 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun4i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c @@ -36,24 +36,11 @@ #define CPU_CFG_CHIP_REV_B 0x3 /* - * Wait up to 1s for value to be set in given part of reg. - */ -static void await_completion(u32 *reg, u32 mask, u32 val) -{ - unsigned long tmo = timer_get_us() + 1000000; - - while ((readl(reg) & mask) != val) { - if (timer_get_us() > tmo) - panic("Timeout initialising DRAM\n"); - } -} - -/* * Wait up to 1s for mask to be clear in given reg. */ static inline void await_bits_clear(u32 *reg, u32 mask) { - await_completion(reg, mask, 0); + mctl_await_completion(reg, mask, 0); } /* @@ -61,7 +48,7 @@ static inline void await_bits_clear(u32 *reg, u32 mask) */ static inline void await_bits_set(u32 *reg, u32 mask) { - await_completion(reg, mask, mask); + mctl_await_completion(reg, mask, mask); } /* diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index 699173c..5dbbf61 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -17,9 +17,7 @@ #include <asm/arch/dram.h> #include <asm/arch/prcm.h> -/* DRAM clk & zq defaults, maybe turn these into Kconfig options ? */ -#define DRAM_CLK_DEFAULT 312000000 -#define DRAM_ZQ_DEFAULT 0x78 +#define DRAM_CLK (CONFIG_DRAM_CLK * 1000000) struct dram_sun6i_para { u8 bus_width; @@ -29,31 +27,18 @@ struct dram_sun6i_para { u16 page_size; }; -/* - * Wait up to 1s for value to be set in given part of reg. - */ -static void await_completion(u32 *reg, u32 mask, u32 val) -{ - unsigned long tmo = timer_get_us() + 1000000; - - while ((readl(reg) & mask) != val) { - if (timer_get_us() > tmo) - panic("Timeout initialising DRAM\n"); - } -} - static void mctl_sys_init(void) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int dram_clk_div = 2; - clock_set_pll5(DRAM_CLK_DEFAULT * dram_clk_div); + clock_set_pll5(DRAM_CLK * dram_clk_div, false); clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST | CCM_DRAMCLK_CFG_UPD); - await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0); + mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0); writel(MDFS_CLK_DEFAULT, &ccm->mdfs_clk_cfg); @@ -109,8 +94,8 @@ static bool mctl_rank_detect(u32 *gsr0, int rank) const u32 done = MCTL_DX_GSR0_RANK0_TRAIN_DONE << rank; const u32 err = MCTL_DX_GSR0_RANK0_TRAIN_ERR << rank; - await_completion(gsr0, done, done); - await_completion(gsr0 + 0x10, done, done); + mctl_await_completion(gsr0, done, done); + mctl_await_completion(gsr0 + 0x10, done, done); return !(readl(gsr0) & err) && !(readl(gsr0 + 0x10) & err); } @@ -131,7 +116,7 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) } writel(MCTL_MCMD_NOP, &mctl_ctl->mcmd); - await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0); + mctl_await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0); /* PHY initialization */ writel(MCTL_PGCR, &mctl_phy->pgcr); @@ -168,14 +153,14 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx2gcr); writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx3gcr); - await_completion(&mctl_phy->pgsr, 0x03, 0x03); + mctl_await_completion(&mctl_phy->pgsr, 0x03, 0x03); - writel(DRAM_ZQ_DEFAULT, &mctl_phy->zq0cr1); + writel(CONFIG_DRAM_ZQ, &mctl_phy->zq0cr1); setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); writel(MCTL_PIR_STEP1, &mctl_phy->pir); udelay(10); - await_completion(&mctl_phy->pgsr, 0x1f, 0x1f); + mctl_await_completion(&mctl_phy->pgsr, 0x1f, 0x1f); /* rank detect */ if (!mctl_rank_detect(&mctl_phy->dx0gsr0, 1)) { @@ -206,19 +191,19 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); writel(MCTL_PIR_STEP2, &mctl_phy->pir); udelay(10); - await_completion(&mctl_phy->pgsr, 0x11, 0x11); + mctl_await_completion(&mctl_phy->pgsr, 0x11, 0x11); if (readl(&mctl_phy->pgsr) & MCTL_PGSR_TRAIN_ERR_MASK) panic("Training error initialising DRAM\n"); /* Move to configure state */ writel(MCTL_SCTL_CONFIG, &mctl_ctl->sctl); - await_completion(&mctl_ctl->sstat, 0x07, 0x01); + mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x01); /* Set number of clks per micro-second */ - writel(DRAM_CLK_DEFAULT / 1000000, &mctl_ctl->togcnt1u); + writel(DRAM_CLK / 1000000, &mctl_ctl->togcnt1u); /* Set number of clks per 100 nano-seconds */ - writel(DRAM_CLK_DEFAULT / 10000000, &mctl_ctl->togcnt100n); + writel(DRAM_CLK / 10000000, &mctl_ctl->togcnt100n); /* Set memory timing registers */ writel(MCTL_TREFI, &mctl_ctl->trefi); writel(MCTL_TMRD, &mctl_ctl->tmrd); @@ -272,7 +257,7 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) /* Move to access state */ writel(MCTL_SCTL_ACCESS, &mctl_ctl->sctl); - await_completion(&mctl_ctl->sstat, 0x07, 0x03); + mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x03); } static void mctl_com_init(struct dram_sun6i_para *para) @@ -341,20 +326,6 @@ static void mctl_port_cfg(void) writel(0x00000307, &mctl_com->mbagcr[5]); } -static bool mctl_mem_matches(u32 offset) -{ - const int match_count = 64; - int i, matches = 0; - - for (i = 0; i < match_count; i++) { - if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) == - readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4)) - matches++; - } - - return matches == match_count; -} - unsigned long sunxi_dram_init(void) { struct sunxi_mctl_com_reg * const mctl_com = @@ -371,18 +342,26 @@ unsigned long sunxi_dram_init(void) .rows = 16, }; + /* A31s only has one channel */ + if (sunxi_get_ss_bonding_id() == SUNXI_SS_BOND_ID_A31S) + para.chan = 1; + mctl_sys_init(); mctl_dll_init(0, ¶); - mctl_dll_init(1, ¶); + setbits_le32(&mctl_com->ccr, MCTL_CCR_CH0_CLK_EN); + + if (para.chan == 2) { + mctl_dll_init(1, ¶); + setbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN); + } - setbits_le32(&mctl_com->ccr, - MCTL_CCR_MASTER_CLK_EN | - MCTL_CCR_CH0_CLK_EN | - MCTL_CCR_CH1_CLK_EN); + setbits_le32(&mctl_com->ccr, MCTL_CCR_MASTER_CLK_EN); mctl_channel_init(0, ¶); - mctl_channel_init(1, ¶); + if (para.chan == 2) + mctl_channel_init(1, ¶); + mctl_com_init(¶); mctl_port_cfg(); diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun8i.c b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c new file mode 100644 index 0000000..3d7964d --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c @@ -0,0 +1,345 @@ +/* + * Sun8i platform dram controller init. + * + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Note this code uses a lot of magic hex values, that is because this code + * simply replays the init sequence as done by the Allwinner boot0 code, so + * we do not know what these values mean. There are no symbolic constants for + * these magic values, since we do not know how to name them and making up + * names for them is not useful. + * + * The register-layout of the sunxi_mctl_phy_reg-s looks a lot like the one + * found in the TI Keystone2 documentation: + * http://www.ti.com/lit/ug/spruhn7a/spruhn7a.pdf + * "Table4-2 DDR3 PHY Registers" + * This may be used as a (possible) reference for future work / cleanups. + */ + +#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h> + +static const struct dram_para dram_para = { + .clock = CONFIG_DRAM_CLK, + .type = 3, + .zq = CONFIG_DRAM_ZQ, + .odt_en = 1, + .para1 = 0, /* not used (only used when tpr13 bit 31 is set */ + .para2 = 0, /* not used (only used when tpr13 bit 31 is set */ + .mr0 = 6736, + .mr1 = 4, + .mr2 = 16, + .mr3 = 0, + /* tpr0 - 10 contain timing constants or-ed together in u32 vals */ + .tpr0 = 0x2ab83def, + .tpr1 = 0x18082356, + .tpr2 = 0x00034156, + .tpr3 = 0x448c5533, + .tpr4 = 0x08010d00, + .tpr5 = 0x0340b20f, + .tpr6 = 0x20d118cc, + .tpr7 = 0x14062485, + .tpr8 = 0x220d1d52, + .tpr9 = 0x1e078c22, + .tpr10 = 0x3c, + .tpr11 = 0, /* not used */ + .tpr12 = 0, /* not used */ + .tpr13 = 0x30000, +}; + +static void mctl_sys_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* enable pll5, note the divide by 2 is deliberate! */ + clock_set_pll5(dram_para.clock * 1000000 / 2, + dram_para.tpr13 & 0x40000); + + /* deassert ahb mctl reset */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + + /* enable ahb mctl clock */ + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); +} + +static void mctl_apply_odt_correction(u32 *reg, int correction) +{ + int val; + + val = (readl(reg) >> 8) & 0xff; + val += correction; + + /* clamp */ + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + + clrsetbits_le32(reg, 0xff00, val << 8); +} + +static void mctl_init(u32 *bus_width) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int correction; + + if (dram_para.tpr13 & 0x20) + writel(0x40b, &mctl_phy->dcr); + else + writel(0x1000040b, &mctl_phy->dcr); + + if (dram_para.clock >= 480) + writel(0x5c000, &mctl_phy->dllgcr); + else + writel(0xdc000, &mctl_phy->dllgcr); + + writel(0x0a003e3f, &mctl_phy->pgcr0); + writel(0x03008421, &mctl_phy->pgcr1); + + writel(dram_para.mr0, &mctl_phy->mr0); + writel(dram_para.mr1, &mctl_phy->mr1); + writel(dram_para.mr2, &mctl_phy->mr2); + writel(dram_para.mr3, &mctl_phy->mr3); + + if (!(dram_para.tpr13 & 0x10000)) { + clrsetbits_le32(&mctl_phy->dx0gcr, 0x3800, 0x2000); + clrsetbits_le32(&mctl_phy->dx1gcr, 0x3800, 0x2000); + } + + /* + * All the masking and shifting below converts what I assume are DDR + * timing constants from Allwinner dram_para tpr format to the actual + * timing registers format. + */ + + writel((dram_para.tpr0 & 0x000fffff), &mctl_phy->ptr2); + writel((dram_para.tpr1 & 0x1fffffff), &mctl_phy->ptr3); + writel((dram_para.tpr0 & 0x3ff00000) >> 2 | + (dram_para.tpr2 & 0x0003ffff), &mctl_phy->ptr4); + + writel(dram_para.tpr3, &mctl_phy->dtpr0); + writel(dram_para.tpr4, &mctl_phy->dtpr2); + + writel(0x01000081, &mctl_phy->dtcr); + + if (dram_para.clock <= 240 || !(dram_para.odt_en & 0x01)) { + clrbits_le32(&mctl_phy->dx0gcr, 0x600); + clrbits_le32(&mctl_phy->dx1gcr, 0x600); + } + if (dram_para.clock <= 240) { + writel(0, &mctl_phy->odtcr); + writel(0, &mctl_ctl->odtmap); + } + + writel(((dram_para.tpr5 & 0x0f00) << 12) | + ((dram_para.tpr5 & 0x00f8) << 9) | + ((dram_para.tpr5 & 0x0007) << 8), + &mctl_ctl->rfshctl0); + + writel(((dram_para.tpr5 & 0x0003f000) << 12) | + ((dram_para.tpr5 & 0x00fc0000) >> 2) | + ((dram_para.tpr5 & 0x3f000000) >> 16) | + ((dram_para.tpr6 & 0x0000003f) >> 0), + &mctl_ctl->dramtmg0); + + writel(((dram_para.tpr6 & 0x000007c0) << 10) | + ((dram_para.tpr6 & 0x0000f800) >> 3) | + ((dram_para.tpr6 & 0x003f0000) >> 16), + &mctl_ctl->dramtmg1); + + writel(((dram_para.tpr6 & 0x0fc00000) << 2) | + ((dram_para.tpr7 & 0x0000001f) << 16) | + ((dram_para.tpr7 & 0x000003e0) << 3) | + ((dram_para.tpr7 & 0x0000fc00) >> 10), + &mctl_ctl->dramtmg2); + + writel(((dram_para.tpr7 & 0x03ff0000) >> 16) | + ((dram_para.tpr6 & 0xf0000000) >> 16), + &mctl_ctl->dramtmg3); + + writel(((dram_para.tpr7 & 0x3c000000) >> 2 ) | + ((dram_para.tpr8 & 0x00000007) << 16) | + ((dram_para.tpr8 & 0x00000038) << 5) | + ((dram_para.tpr8 & 0x000003c0) >> 6), + &mctl_ctl->dramtmg4); + + writel(((dram_para.tpr8 & 0x00003c00) << 14) | + ((dram_para.tpr8 & 0x0003c000) << 2) | + ((dram_para.tpr8 & 0x00fc0000) >> 10) | + ((dram_para.tpr8 & 0x0f000000) >> 24), + &mctl_ctl->dramtmg5); + + writel(0x00000008, &mctl_ctl->dramtmg8); + + writel(((dram_para.tpr8 & 0xf0000000) >> 4) | + ((dram_para.tpr9 & 0x00007c00) << 6) | + ((dram_para.tpr9 & 0x000003e0) << 3) | + ((dram_para.tpr9 & 0x0000001f) >> 0), + &mctl_ctl->pitmg0); + + setbits_le32(&mctl_ctl->pitmg1, 0x80000); + + writel(((dram_para.tpr9 & 0x003f8000) << 9) | 0x2001, + &mctl_ctl->sched); + + writel((dram_para.mr0 << 16) | dram_para.mr1, &mctl_ctl->init3); + writel((dram_para.mr2 << 16) | dram_para.mr3, &mctl_ctl->init4); + + writel(0x00000000, &mctl_ctl->pimisc); + writel(0x80000000, &mctl_ctl->upd0); + + writel(((dram_para.tpr9 & 0xffc00000) >> 22) | + ((dram_para.tpr10 & 0x00000fff) << 16), + &mctl_ctl->rfshtmg); + + if (dram_para.tpr13 & 0x20) + writel(0x01040001, &mctl_ctl->mstr); + else + writel(0x01040401, &mctl_ctl->mstr); + + if (!(dram_para.tpr13 & 0x20000)) { + writel(0x00000002, &mctl_ctl->pwrctl); + writel(0x00008001, &mctl_ctl->pwrtmg); + } + + writel(0x00000001, &mctl_ctl->rfshctl3); + writel(0x00000001, &mctl_ctl->pimisc); + + /* deassert dram_clk_cfg reset */ + setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST); + + setbits_le32(&mctl_com->ccr, 0x80000); + + /* zq stuff */ + writel((dram_para.zq >> 8) & 0xff, &mctl_phy->zqcr1); + + writel(0x00000003, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr0, 0x09, 0x09); + + writel(readl(&mctl_phy->zqsr0) | 0x10000000, &mctl_phy->zqcr2); + writel(dram_para.zq & 0xff, &mctl_phy->zqcr1); + + /* A23-v1.0 SDK uses 0xfdf3, A23-v2.0 SDK uses 0x5f3 */ + writel(0x000005f3, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr0, 0x03, 0x03); + + if (readl(&mctl_phy->dx1gsr0) & 0x1000000) { + *bus_width = 8; + writel(0, &mctl_phy->dx1gcr); + writel(dram_para.zq & 0xff, &mctl_phy->zqcr1); + writel(0x5f3, &mctl_phy->pir); + udelay(10000); + setbits_le32(&mctl_ctl->mstr, 0x1000); + } else + *bus_width = 16; + + correction = (dram_para.odt_en >> 8) & 0xff; + if (correction) { + if (dram_para.odt_en & 0x80000000) + correction = -correction; + + mctl_apply_odt_correction(&mctl_phy->dx0lcdlr1, correction); + mctl_apply_odt_correction(&mctl_phy->dx1lcdlr1, correction); + } + + mctl_await_completion(&mctl_ctl->statr, 0x01, 0x01); + + writel(0x08003e3f, &mctl_phy->pgcr0); + writel(0x00000000, &mctl_ctl->rfshctl3); +} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + const u32 columns = 13; + u32 bus, bus_width, offset, page_size, rows; + + mctl_sys_init(); + mctl_init(&bus_width); + + if (bus_width == 16) { + page_size = 8; + bus = 1; + } else { + page_size = 7; + bus = 0; + } + + if (!(dram_para.tpr13 & 0x80000000)) { + /* Detect and set rows */ + writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size), + &mctl_com->cr); + setbits_le32(&mctl_com->swonr, 0x0003ffff); + for (rows = 11; rows < 16; rows++) { + offset = 1 << (rows + columns + bus); + if (mctl_mem_matches(offset)) + break; + } + clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK, + MCTL_CR_ROW(rows)); + } else { + rows = (dram_para.para1 >> 16) & 0xff; + writel(((dram_para.para2 & 0x000000f0) << 11) | + ((rows - 1) << 4) | + ((dram_para.para1 & 0x0f000000) >> 22) | + 0x31000 | MCTL_CR_PAGE_SIZE(page_size), + &mctl_com->cr); + setbits_le32(&mctl_com->swonr, 0x0003ffff); + } + + /* Setup DRAM master priority? If this is left out things still work */ + writel(0x00000008, &mctl_com->mcr0_0); + writel(0x0001000d, &mctl_com->mcr1_0); + writel(0x00000004, &mctl_com->mcr0_1); + writel(0x00000080, &mctl_com->mcr1_1); + writel(0x00000004, &mctl_com->mcr0_2); + writel(0x00000019, &mctl_com->mcr1_2); + writel(0x00000004, &mctl_com->mcr0_3); + writel(0x00000080, &mctl_com->mcr1_3); + writel(0x00000004, &mctl_com->mcr0_4); + writel(0x01010040, &mctl_com->mcr1_4); + writel(0x00000004, &mctl_com->mcr0_5); + writel(0x0001002f, &mctl_com->mcr1_5); + writel(0x00000004, &mctl_com->mcr0_6); + writel(0x00010020, &mctl_com->mcr1_6); + writel(0x00000004, &mctl_com->mcr0_7); + writel(0x00010020, &mctl_com->mcr1_7); + writel(0x00000008, &mctl_com->mcr0_8); + writel(0x00000001, &mctl_com->mcr1_8); + writel(0x00000008, &mctl_com->mcr0_9); + writel(0x00000005, &mctl_com->mcr1_9); + writel(0x00000008, &mctl_com->mcr0_10); + writel(0x00000003, &mctl_com->mcr1_10); + writel(0x00000008, &mctl_com->mcr0_11); + writel(0x00000005, &mctl_com->mcr1_11); + writel(0x00000008, &mctl_com->mcr0_12); + writel(0x00000003, &mctl_com->mcr1_12); + writel(0x00000008, &mctl_com->mcr0_13); + writel(0x00000004, &mctl_com->mcr1_13); + writel(0x00000008, &mctl_com->mcr0_14); + writel(0x00000002, &mctl_com->mcr1_14); + writel(0x00000008, &mctl_com->mcr0_15); + writel(0x00000003, &mctl_com->mcr1_15); + writel(0x00010138, &mctl_com->bwcr); + + return 1 << (rows + columns + bus); +} diff --git a/arch/arm/cpu/armv7/sunxi/p2wi.c b/arch/arm/cpu/armv7/sunxi/p2wi.c index 48613bd..26a9cfc 100644 --- a/arch/arm/cpu/armv7/sunxi/p2wi.c +++ b/arch/arm/cpu/armv7/sunxi/p2wi.c @@ -26,13 +26,13 @@ void p2wi_init(void) { - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; /* Enable p2wi and PIO clk, and de-assert their resets */ prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI); - sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUNXI_GPL0_R_P2WI_SCK); - sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUNXI_GPL1_R_P2WI_SDA); + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA); /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */ writel(P2WI_CTRL_RESET, &p2wi->ctrl); @@ -43,7 +43,7 @@ void p2wi_init(void) int p2wi_change_to_p2wi_mode(u8 slave_addr, u8 ctrl_reg, u8 init_data) { - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; unsigned long tmo = timer_get_us() + 1000000; writel(P2WI_PM_DEV_ADDR(slave_addr) | @@ -62,7 +62,7 @@ int p2wi_change_to_p2wi_mode(u8 slave_addr, u8 ctrl_reg, u8 init_data) static int p2wi_await_trans(void) { - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; unsigned long tmo = timer_get_us() + 1000000; int ret; u8 reg; @@ -88,7 +88,7 @@ static int p2wi_await_trans(void) int p2wi_read(const u8 addr, u8 *data) { - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; int ret; writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); @@ -105,7 +105,7 @@ int p2wi_read(const u8 addr, u8 *data) int p2wi_write(const u8 addr, u8 data) { - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); writel(P2WI_DATA_BYTE_1(data), &p2wi->data0); diff --git a/arch/arm/cpu/armv7/sunxi/psci.S b/arch/arm/cpu/armv7/sunxi/psci.S index b9ea78b..5be497b 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.S +++ b/arch/arm/cpu/armv7/sunxi/psci.S @@ -18,6 +18,7 @@ */ #include <config.h> +#include <asm/gic.h> #include <asm/psci.h> #include <asm/arch/cpu.h> @@ -38,6 +39,8 @@ #define ONE_MS (CONFIG_SYS_CLK_FREQ / 1000) #define TEN_MS (10 * ONE_MS) +#define GICD_BASE 0x1c81000 +#define GICC_BASE 0x1c82000 .macro timer_wait reg, ticks @ Program CNTP_TVAL @@ -59,25 +62,77 @@ isb .endm -.globl psci_arch_init -psci_arch_init: - mrc p15, 0, r5, c1, c1, 0 @ Read SCR - bic r5, r5, #1 @ Secure mode - mcr p15, 0, r5, c1, c1, 0 @ Write SCR +.globl psci_fiq_enter +psci_fiq_enter: + push {r0-r12} + + @ Switch to secure + mrc p15, 0, r7, c1, c1, 0 + bic r8, r7, #1 + mcr p15, 0, r8, c1, c1, 0 isb - mrc p15, 0, r4, c0, c0, 5 @ MPIDR - and r4, r4, #3 @ cpu number in cluster - mov r5, #400 @ 1kB of stack per CPU - mul r4, r4, r5 + @ Validate reason based on IAR and acknowledge + movw r8, #(GICC_BASE & 0xffff) + movt r8, #(GICC_BASE >> 16) + ldr r9, [r8, #GICC_IAR] + movw r10, #0x3ff + movt r10, #0 + cmp r9, r10 @ skip spurious interrupt 1023 + beq out + movw r10, #0x3fe @ ...and 1022 + cmp r9, r10 + beq out + str r9, [r8, #GICC_EOIR] @ acknowledge the interrupt + dsb - adr r5, text_end @ end of text - add r5, r5, #0x2000 @ Skip two pages - lsr r5, r5, #12 @ Align to start of page - lsl r5, r5, #12 - sub sp, r5, r4 @ here's our stack! + @ Compute CPU number + lsr r9, r9, #10 + and r9, r9, #0xf - bx lr + movw r8, #(SUN7I_CPUCFG_BASE & 0xffff) + movt r8, #(SUN7I_CPUCFG_BASE >> 16) + + @ Wait for the core to enter WFI + lsl r11, r9, #6 @ x64 + add r11, r11, r8 + +1: ldr r10, [r11, #0x48] + tst r10, #(1 << 2) + bne 2f + timer_wait r10, ONE_MS + b 1b + + @ Reset CPU +2: mov r10, #0 + str r10, [r11, #0x40] + + @ Lock CPU + mov r10, #1 + lsl r9, r10, r9 @ r9 is now CPU mask + ldr r10, [r8, #0x1e4] + bic r10, r10, r9 + str r10, [r8, #0x1e4] + + @ Set power gating + ldr r10, [r8, #0x1b4] + orr r10, r10, #1 + str r10, [r8, #0x1b4] + timer_wait r10, ONE_MS + + @ Activate power clamp + mov r10, #1 +1: str r10, [r8, #0x1b0] + lsl r10, r10, #1 + orr r10, r10, #1 + tst r10, #0x100 + beq 1b + + @ Restore security level +out: mcr p15, 0, r7, c1, c1, 0 + + pop {r0-r12} + subs pc, lr, #4 @ r1 = target CPU @ r2 = target PC @@ -144,6 +199,53 @@ psci_cpu_on: _target_pc: .word 0 +/* Imported from Linux kernel */ +v7_flush_dcache_all: + dmb @ ensure ordering with previous memory accesses + mrc p15, 1, r0, c0, c0, 1 @ read clidr + ands r3, r0, #0x7000000 @ extract loc from clidr + mov r3, r3, lsr #23 @ left align loc bit field + beq finished @ if loc is 0, then no need to clean + mov r10, #0 @ start clean at cache level 0 +flush_levels: + add r2, r10, r10, lsr #1 @ work out 3x current cache level + mov r1, r0, lsr r2 @ extract cache type bits from clidr + and r1, r1, #7 @ mask of the bits for current cache only + cmp r1, #2 @ see what cache we have at this level + blt skip @ skip if no cache, or just i-cache + mrs r9, cpsr @ make cssr&csidr read atomic + mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr + isb @ isb to sych the new cssr&csidr + mrc p15, 1, r1, c0, c0, 0 @ read the new csidr + msr cpsr_c, r9 + and r2, r1, #7 @ extract the length of the cache lines + add r2, r2, #4 @ add 4 (line length offset) + ldr r4, =0x3ff + ands r4, r4, r1, lsr #3 @ find maximum number on the way size + clz r5, r4 @ find bit position of way size increment + ldr r7, =0x7fff + ands r7, r7, r1, lsr #13 @ extract max number of the index size +loop1: + mov r9, r7 @ create working copy of max index +loop2: + orr r11, r10, r4, lsl r5 @ factor way and cache number into r11 + orr r11, r11, r9, lsl r2 @ factor index number into r11 + mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way + subs r9, r9, #1 @ decrement the index + bge loop2 + subs r4, r4, #1 @ decrement the way + bge loop1 +skip: + add r10, r10, #2 @ increment cache number + cmp r3, r10 + bgt flush_levels +finished: + mov r10, #0 @ swith back to cache level 0 + mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr + dsb st + isb + bx lr + _sunxi_cpu_entry: @ Set SMP bit mrc p15, 0, r0, c1, c0, 1 @@ -158,5 +260,74 @@ _sunxi_cpu_entry: ldr r0, [r0] b _do_nonsec_entry +.globl psci_cpu_off +psci_cpu_off: + mrc p15, 0, r0, c1, c0, 0 @ SCTLR + bic r0, r0, #(1 << 2) @ Clear C bit + mcr p15, 0, r0, c1, c0, 0 @ SCTLR + isb + dsb + + bl v7_flush_dcache_all + + clrex @ Why??? + + mrc p15, 0, r0, c1, c0, 1 @ ACTLR + bic r0, r0, #(1 << 6) @ Clear SMP bit + mcr p15, 0, r0, c1, c0, 1 @ ACTLR + isb + dsb + + @ Ask CPU0 to pull the rug... + movw r0, #(GICD_BASE & 0xffff) + movt r0, #(GICD_BASE >> 16) + movw r1, #15 @ SGI15 + movt r1, #1 @ Target is CPU0 + str r1, [r0, #GICD_SGIR] + dsb + +1: wfi + b 1b + +.globl psci_arch_init +psci_arch_init: + movw r4, #(GICD_BASE & 0xffff) + movt r4, #(GICD_BASE >> 16) + + ldr r5, [r4, #GICD_IGROUPRn] + bic r5, r5, #(1 << 15) @ SGI15 as Group-0 + str r5, [r4, #GICD_IGROUPRn] + + mov r5, #0 @ Set SGI15 priority to 0 + strb r5, [r4, #(GICD_IPRIORITYRn + 15)] + + add r4, r4, #0x1000 @ GICC address + + mov r5, #0xff + str r5, [r4, #GICC_PMR] @ Be cool with non-secure + + ldr r5, [r4, #GICC_CTLR] + orr r5, r5, #(1 << 3) @ Switch FIQEn on + str r5, [r4, #GICC_CTLR] + + mrc p15, 0, r5, c1, c1, 0 @ Read SCR + orr r5, r5, #4 @ Enable FIQ in monitor mode + bic r5, r5, #1 @ Secure mode + mcr p15, 0, r5, c1, c1, 0 @ Write SCR + isb + + mrc p15, 0, r4, c0, c0, 5 @ MPIDR + and r4, r4, #3 @ cpu number in cluster + mov r5, #0x400 @ 1kB of stack per CPU + mul r4, r4, r5 + + adr r5, text_end @ end of text + add r5, r5, #0x2000 @ Skip two pages + lsr r5, r5, #12 @ Align to start of page + lsl r5, r5, #12 + sub sp, r5, r4 @ here's our stack! + + bx lr + text_end: .popsection diff --git a/arch/arm/cpu/armv7/sunxi/rsb.c b/arch/arm/cpu/armv7/sunxi/rsb.c new file mode 100644 index 0000000..b72bb9d --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/rsb.c @@ -0,0 +1,158 @@ +/* + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * Based on allwinner u-boot sources rsb code which is: + * (C) Copyright 2007-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * lixiang <lixiang@allwinnertech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/prcm.h> +#include <asm/arch/rsb.h> + +static void rsb_cfg_io(void) +{ + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL0_R_RSB_SCK); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL1_R_RSB_SDA); + sunxi_gpio_set_pull(SUNXI_GPL(0), 1); + sunxi_gpio_set_pull(SUNXI_GPL(1), 1); + sunxi_gpio_set_drv(SUNXI_GPL(0), 2); + sunxi_gpio_set_drv(SUNXI_GPL(1), 2); +} + +static void rsb_set_clk(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + u32 div = 0; + u32 cd_odly = 0; + + /* Source is Hosc24M, set RSB clk to 3Mhz */ + div = 24000000 / 3000000 / 2 - 1; + cd_odly = div >> 1; + if (!cd_odly) + cd_odly = 1; + + writel((cd_odly << 8) | div, &rsb->ccr); +} + +void rsb_init(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + rsb_cfg_io(); + + /* Enable RSB and PIO clk, and de-assert their resets */ + prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB); + + writel(RSB_CTRL_SOFT_RST, &rsb->ctrl); + rsb_set_clk(); +} + +static int rsb_await_trans(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + unsigned long tmo = timer_get_us() + 1000000; + u32 stat; + int ret; + + while (1) { + stat = readl(&rsb->stat); + if (stat & RSB_STAT_LBSY_INT) { + ret = -EBUSY; + break; + } + if (stat & RSB_STAT_TERR_INT) { + ret = -EIO; + break; + } + if (stat & RSB_STAT_TOVER_INT) { + ret = 0; + break; + } + if (timer_get_us() > tmo) { + ret = -ETIME; + break; + } + } + writel(stat, &rsb->stat); /* Clear status bits */ + + return ret; +} + +int rsb_set_device_mode(u32 device_mode_data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + unsigned long tmo = timer_get_us() + 1000000; + + writel(RSB_DMCR_DEVICE_MODE_START | device_mode_data, &rsb->dmcr); + + while (readl(&rsb->dmcr) & RSB_DMCR_DEVICE_MODE_START) { + if (timer_get_us() > tmo) + return -ETIME; + } + + return rsb_await_trans(); +} + +static int rsb_do_trans(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + setbits_le32(&rsb->ctrl, RSB_CTRL_START_TRANS); + return rsb_await_trans(); +} + +int rsb_set_device_address(u16 device_addr, u16 runtime_addr) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_addr) | + RSB_DEVADDR_DEVICE_ADDR(device_addr), &rsb->devaddr); + writel(RSB_CMD_SET_RTSADDR, &rsb->cmd); + + return rsb_do_trans(); +} + +int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); + writel(reg_addr, &rsb->addr); + writel(data, &rsb->data); + writel(RSB_CMD_BYTE_WRITE, &rsb->cmd); + + return rsb_do_trans(); +} + +int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + int ret; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); + writel(reg_addr, &rsb->addr); + writel(RSB_CMD_BYTE_READ, &rsb->cmd); + + ret = rsb_do_trans(); + if (ret) + return ret; + + *data = readl(&rsb->data) & 0xff; + + return 0; +} diff --git a/arch/arm/cpu/armv7/sunxi/usbc.c b/arch/arm/cpu/armv7/sunxi/usbc.c new file mode 100644 index 0000000..14de9f9 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/usbc.c @@ -0,0 +1,272 @@ +/* + * Sunxi usb-controller code shared between the ehci and musb controllers + * + * Copyright (C) 2014 Roman Byshko + * + * Roman Byshko <rbyshko@gmail.com> + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> +#include <asm/arch/usbc.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <common.h> +#ifdef CONFIG_AXP152_POWER +#include <axp152.h> +#endif +#ifdef CONFIG_AXP209_POWER +#include <axp209.h> +#endif +#ifdef CONFIG_AXP221_POWER +#include <axp221.h> +#endif + +#define SUNXI_USB_PMU_IRQ_ENABLE 0x800 +#define SUNXI_USB_CSR 0x404 +#define SUNXI_USB_PASSBY_EN 1 + +#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) +#define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9) +#define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8) +#define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0) + +static struct sunxi_usbc_hcd { + struct usb_hcd *hcd; + int usb_rst_mask; + int ahb_clk_mask; + int gpio_vbus; + int irq; + int id; +} sunxi_usbc_hcd[] = { + { + .usb_rst_mask = CCM_USB_CTRL_PHY0_RST | CCM_USB_CTRL_PHY0_CLK, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB0, +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + .irq = 71, +#else + .irq = 38, +#endif + .id = 0, + }, + { + .usb_rst_mask = CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + .irq = 72, +#else + .irq = 39, +#endif + .id = 1, + }, +#if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) + { + .usb_rst_mask = CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, +#ifdef CONFIG_MACH_SUN6I + .irq = 74, +#else + .irq = 40, +#endif + .id = 2, + } +#endif +}; + +static int enabled_hcd_count; + +static bool use_axp_drivebus(int index) +{ + return index == 0 && + strcmp(CONFIG_USB0_VBUS_PIN, "axp_drivebus") == 0; +} + +void *sunxi_usbc_get_io_base(int index) +{ + switch (index) { + case 0: + return (void *)SUNXI_USB0_BASE; + case 1: + return (void *)SUNXI_USB1_BASE; + case 2: + return (void *)SUNXI_USB2_BASE; + default: + return NULL; + } +} + +static int get_vbus_gpio(int index) +{ + if (use_axp_drivebus(index)) + return -1; + + switch (index) { + case 0: return sunxi_name_to_gpio(CONFIG_USB0_VBUS_PIN); + case 1: return sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN); + case 2: return sunxi_name_to_gpio(CONFIG_USB2_VBUS_PIN); + } + return -1; +} + +static void usb_phy_write(struct sunxi_usbc_hcd *sunxi_usbc, int addr, + int data, int len) +{ + int j = 0, usbc_bit = 0; + void *dest = sunxi_usbc_get_io_base(0) + SUNXI_USB_CSR; + + usbc_bit = 1 << (sunxi_usbc->id * 2); + for (j = 0; j < len; j++) { + /* set the bit address to be written */ + clrbits_le32(dest, 0xff << 8); + setbits_le32(dest, (addr + j) << 8); + + clrbits_le32(dest, usbc_bit); + /* set data bit */ + if (data & 0x1) + setbits_le32(dest, 1 << 7); + else + clrbits_le32(dest, 1 << 7); + + setbits_le32(dest, usbc_bit); + + clrbits_le32(dest, usbc_bit); + + data >>= 1; + } +} + +static void sunxi_usb_phy_init(struct sunxi_usbc_hcd *sunxi_usbc) +{ + /* The following comments are machine + * translated from Chinese, you have been warned! + */ + + /* Regulation 45 ohms */ + if (sunxi_usbc->id == 0) + usb_phy_write(sunxi_usbc, 0x0c, 0x01, 1); + + /* adjust PHY's magnitude and rate */ + usb_phy_write(sunxi_usbc, 0x20, 0x14, 5); + + /* threshold adjustment disconnect */ +#if defined CONFIG_MACH_SUN4I || defined CONFIG_MACH_SUN6I + usb_phy_write(sunxi_usbc, 0x2a, 3, 2); +#else + usb_phy_write(sunxi_usbc, 0x2a, 2, 2); +#endif + + return; +} + +static void sunxi_usb_passby(struct sunxi_usbc_hcd *sunxi_usbc, int enable) +{ + unsigned long bits = 0; + void *addr = sunxi_usbc_get_io_base(sunxi_usbc->id) + + SUNXI_USB_PMU_IRQ_ENABLE; + + bits = SUNXI_EHCI_AHB_ICHR8_EN | + SUNXI_EHCI_AHB_INCR4_BURST_EN | + SUNXI_EHCI_AHB_INCRX_ALIGN_EN | + SUNXI_EHCI_ULPI_BYPASS_EN; + + if (enable) + setbits_le32(addr, bits); + else + clrbits_le32(addr, bits); + + return; +} + +int sunxi_usbc_request_resources(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; + + sunxi_usbc->gpio_vbus = get_vbus_gpio(index); + if (sunxi_usbc->gpio_vbus != -1) + return gpio_request(sunxi_usbc->gpio_vbus, "usbc_vbus"); + + return 0; +} + +int sunxi_usbc_free_resources(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; + + if (sunxi_usbc->gpio_vbus != -1) + return gpio_free(sunxi_usbc->gpio_vbus); + + return 0; +} + +void sunxi_usbc_enable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* enable common PHY only once */ + if (enabled_hcd_count == 0) + setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + + setbits_le32(&ccm->usb_clk_cfg, sunxi_usbc->usb_rst_mask); + setbits_le32(&ccm->ahb_gate0, sunxi_usbc->ahb_clk_mask); +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + setbits_le32(&ccm->ahb_reset0_cfg, sunxi_usbc->ahb_clk_mask); +#endif + + sunxi_usb_phy_init(sunxi_usbc); + + if (sunxi_usbc->id != 0) + sunxi_usb_passby(sunxi_usbc, SUNXI_USB_PASSBY_EN); + + enabled_hcd_count++; +} + +void sunxi_usbc_disable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (sunxi_usbc->id != 0) + sunxi_usb_passby(sunxi_usbc, !SUNXI_USB_PASSBY_EN); + +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + clrbits_le32(&ccm->ahb_reset0_cfg, sunxi_usbc->ahb_clk_mask); +#endif + clrbits_le32(&ccm->ahb_gate0, sunxi_usbc->ahb_clk_mask); + clrbits_le32(&ccm->usb_clk_cfg, sunxi_usbc->usb_rst_mask); + + /* disable common PHY only once, for the last enabled hcd */ + if (enabled_hcd_count == 1) + clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + + enabled_hcd_count--; +} + +void sunxi_usbc_vbus_enable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; + +#ifdef AXP_DRIVEBUS + if (use_axp_drivebus(index)) + axp_drivebus_enable(); +#endif + if (sunxi_usbc->gpio_vbus != -1) + gpio_direction_output(sunxi_usbc->gpio_vbus, 1); +} + +void sunxi_usbc_vbus_disable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; + +#ifdef AXP_DRIVEBUS + if (use_axp_drivebus(index)) + axp_drivebus_disable(); +#endif + if (sunxi_usbc->gpio_vbus != -1) + gpio_direction_output(sunxi_usbc->gpio_vbus, 0); +} diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index 4b3ee6e..9c6e824 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -94,7 +94,7 @@ skip: b.gt loop_level mov x0, #0 - msr csselr_el1, x0 /* resotre csselr_el1 */ + msr csselr_el1, x0 /* restore csselr_el1 */ dsb sy isb mov lr, x15 |