[U-Boot] [PATCH v3 0/11] sun6i: Add dram init and SPL support

Hi All,
Here is v3 of my A31 dram init & SPL support series, this now includes A31 ehci (usb) support :)
Changes since v2: -The previous series were based on top of some not yet ready for general consumption patches in my tree, which caused conflict when basing them on master, rebased the series on top of master -EHCI support
Changes since v1: -"sun6i: Add new p2wi controller driver" -Rename p2wi_set_pmu_address to p2wi_change_to_p2wi_mode -"sun6i: Add basic axp221 driver" -Add support for all dldo and aldo-s -Add Kconfig option to select building AXP221 and to select voltage of dldo and aldo-s -"sun6i: Add cpucfg register definitions" -Rename existing SUNXI_CPUCFG_BASE to SUN7I_CPUCFG_BASE, name sun6i one SUN6I_CPUCFG_BASE -Rename cpucfg.h to cpucfg_sun6i.h -"sun4i: Rename dram files to dram_sun4i.x" -Do not include non existing dram_sun6i.h -"sun6i: Add Mele M9 board" -Add defconfig settings for various ldo-s
Please review.
Regards,
Hans

From: Oliver Schinagl oliver@schinagl.nl
The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.
Signed-off-by: Oliver Schinagl oliver@schinagl.nl Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/p2wi.c | 117 +++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/gpio.h | 3 + arch/arm/include/asm/arch-sunxi/p2wi.h | 140 +++++++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 arch/arm/cpu/armv7/sunxi/p2wi.c create mode 100644 arch/arm/include/asm/arch-sunxi/p2wi.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 82dbf76..b3a3601 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -13,6 +13,7 @@ obj-y += clock.o obj-y += pinmux.o obj-$(CONFIG_MACH_SUN6I) += prcm.o obj-$(CONFIG_MACH_SUN8I) += prcm.o +obj-$(CONFIG_MACH_SUN6I) += p2wi.o obj-$(CONFIG_MACH_SUN4I) += clock_sun4i.o obj-$(CONFIG_MACH_SUN5I) += clock_sun4i.o obj-$(CONFIG_MACH_SUN6I) += clock_sun6i.o diff --git a/arch/arm/cpu/armv7/sunxi/p2wi.c b/arch/arm/cpu/armv7/sunxi/p2wi.c new file mode 100644 index 0000000..8d58955 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/p2wi.c @@ -0,0 +1,117 @@ +/* + * Sunxi A31 Power Management Unit + * + * (C) Copyright 2013 Oliver Schinagl oliver@schinagl.nl + * http://linux-sunxi.org + * + * Based on sun6i sources and earlier U-Boot Allwiner A10 SPL work + * + * (C) Copyright 2006-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/p2wi.h> +#include <asm/arch/prcm.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +void p2wi_init(void) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_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); + + /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */ + writel(P2WI_CTRL_RESET, &p2wi->ctrl); + sdelay(0x100); + writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8), + &p2wi->cc); +} + +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; + unsigned long tmo = timer_get_us() + 1000000; + + writel(P2WI_PM_DEV_ADDR(slave_addr) | + P2WI_PM_CTRL_ADDR(ctrl_reg) | + P2WI_PM_INIT_DATA(init_data) | + P2WI_PM_INIT_SEND, + &p2wi->pm); + + while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) { + if (timer_get_us() > tmo) + return -EFAULT; + } + + return 0; +} + +static int p2wi_await_trans(void) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + unsigned long tmo = timer_get_us() + 1000000; + int ret; + u8 reg; + + while (1) { + reg = readl(&p2wi->status); + if (reg & P2WI_STAT_TRANS_ERR) { + ret = -EIO; + break; + } + if (reg & P2WI_STAT_TRANS_DONE) { + ret = 0; + break; + } + if (timer_get_us() > tmo) { + ret = -ETIME; + break; + } + } + writel(reg, &p2wi->status); /* Clear status bits */ + return ret; +} + +int p2wi_read(const u8 addr, u8 *data) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + int ret; + + writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); + writel(P2WI_DATA_NUM_BYTES(1) | + P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes); + writel(P2WI_STAT_TRANS_DONE, &p2wi->status); + writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl); + + ret = p2wi_await_trans(); + + *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK; + return ret; +} + +int p2wi_write(const u8 addr, u8 data) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE; + + writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); + writel(P2WI_DATA_BYTE_1(data), &p2wi->data0); + writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes); + writel(P2WI_STAT_TRANS_DONE, &p2wi->status); + writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl); + + return p2wi_await_trans(); +} diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 437dd35..c734cf0 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -172,6 +172,9 @@ enum sunxi_gpio_number {
#define SUN4I_GPI4_SDC3 2
+#define SUNXI_GPL0_R_P2WI_SCK 3 +#define SUNXI_GPL1_R_P2WI_SDA 3 + #define SUN8I_GPL2_R_UART_TX 2 #define SUN8I_GPL3_R_UART_RX 2
diff --git a/arch/arm/include/asm/arch-sunxi/p2wi.h b/arch/arm/include/asm/arch-sunxi/p2wi.h new file mode 100644 index 0000000..2cf2d51 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/p2wi.h @@ -0,0 +1,140 @@ +/* + * Sunxi platform Push-Push i2c register definition. + * + * (c) Copyright 2013 Oliver Schinagl oliver@schinagl.nl + * http://linux-sunxi.org + * + * (c)Copyright 2006-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_P2WI_H +#define _SUNXI_P2WI_H + +#include <linux/types.h> + +#define P2WI_CTRL_RESET (0x1 << 0) +#define P2WI_CTRL_IRQ_EN (0x1 << 1) +#define P2WI_CTRL_TRANS_ABORT (0x1 << 6) +#define P2WI_CTRL_TRANS_START (0x1 << 7) + +#define __P2WI_CC_CLK(n) (((n) & 0xff) << 0) +#define P2WI_CC_CLK_MASK __P2WI_CC_CLK_DIV(0xff) +#define __P2WI_CC_CLK_DIV(n) (((n) >> 1) - 1) +#define P2WI_CC_CLK_DIV(n) \ + __P2WI_CC_CLK(__P2WI_CC_CLK_DIV(n)) +#define P2WI_CC_SDA_OUT_DELAY(n) (((n) & 0x7) << 8) +#define P2WI_CC_SDA_OUT_DELAY_MASK P2WI_CC_SDA_OUT_DELAY(0x7) + +#define P2WI_IRQ_TRANS_DONE (0x1 << 0) +#define P2WI_IRQ_TRANS_ERR (0x1 << 1) +#define P2WI_IRQ_LOAD_BUSY (0x1 << 2) + +#define P2WI_STAT_TRANS_DONE (0x1 << 0) +#define P2WI_STAT_TRANS_ERR (0x1 << 1) +#define P2WI_STAT_LOAD_BUSY (0x1 << 2) +#define __P2WI_STAT_TRANS_ERR(n) (((n) & 0xff) << 8) +#define P2WI_STAT_TRANS_ERR_MASK __P2WI_STAT_TRANS_ERR_ID(0xff) +#define __P2WI_STAT_TRANS_ERR_BYTE_1 0x01 +#define __P2WI_STAT_TRANS_ERR_BYTE_2 0x02 +#define __P2WI_STAT_TRANS_ERR_BYTE_3 0x04 +#define __P2WI_STAT_TRANS_ERR_BYTE_4 0x08 +#define __P2WI_STAT_TRANS_ERR_BYTE_5 0x10 +#define __P2WI_STAT_TRANS_ERR_BYTE_6 0x20 +#define __P2WI_STAT_TRANS_ERR_BYTE_7 0x40 +#define __P2WI_STAT_TRANS_ERR_BYTE_8 0x80 +#define P2WI_STAT_TRANS_ERR_BYTE_1 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_1) +#define P2WI_STAT_TRANS_ERR_BYTE_2 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_2) +#define P2WI_STAT_TRANS_ERR_BYTE_3 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_3) +#define P2WI_STAT_TRANS_ERR_BYTE_4 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_4) +#define P2WI_STAT_TRANS_ERR_BYTE_5 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_5) +#define P2WI_STAT_TRANS_ERR_BYTE_6 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_6) +#define P2WI_STAT_TRANS_ERR_BYTE_7 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_7) +#define P2WI_STAT_TRANS_ERR_BYTE_8 \ + __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_8) + +#define P2WI_DATADDR_BYTE_1(n) (((n) & 0xff) << 0) +#define P2WI_DATADDR_BYTE_1_MASK P2WI_DATADDR_BYTE_1(0xff) +#define P2WI_DATADDR_BYTE_2(n) (((n) & 0xff) << 8) +#define P2WI_DATADDR_BYTE_2_MASK P2WI_DATADDR_BYTE_2(0xff) +#define P2WI_DATADDR_BYTE_3(n) (((n) & 0xff) << 16) +#define P2WI_DATADDR_BYTE_3_MASK P2WI_DATADDR_BYTE_3(0xff) +#define P2WI_DATADDR_BYTE_4(n) (((n) & 0xff) << 24) +#define P2WI_DATADDR_BYTE_4_MASK P2WI_DATADDR_BYTE_4(0xff) +#define P2WI_DATADDR_BYTE_5(n) (((n) & 0xff) << 0) +#define P2WI_DATADDR_BYTE_5_MASK P2WI_DATADDR_BYTE_5(0xff) +#define P2WI_DATADDR_BYTE_6(n) (((n) & 0xff) << 8) +#define P2WI_DATADDR_BYTE_6_MASK P2WI_DATADDR_BYTE_6(0xff) +#define P2WI_DATADDR_BYTE_7(n) (((n) & 0xff) << 16) +#define P2WI_DATADDR_BYTE_7_MASK P2WI_DATADDR_BYTE_7(0xff) +#define P2WI_DATADDR_BYTE_8(n) (((n) & 0xff) << 24) +#define P2WI_DATADDR_BYTE_8_MASK P2WI_DATADDR_BYTE_8(0xff) + +#define __P2WI_DATA_NUM_BYTES(n) (((n) & 0x7) << 0) +#define P2WI_DATA_NUM_BYTES_MASK __P2WI_DATA_NUM_BYTES(0x7) +#define P2WI_DATA_NUM_BYTES(n) __P2WI_DATA_NUM_BYTES((n) - 1) +#define P2WI_DATA_NUM_BYTES_READ (0x1 << 4) + +#define P2WI_DATA_BYTE_1(n) (((n) & 0xff) << 0) +#define P2WI_DATA_BYTE_1_MASK P2WI_DATA_BYTE_1(0xff) +#define P2WI_DATA_BYTE_2(n) (((n) & 0xff) << 8) +#define P2WI_DATA_BYTE_2_MASK P2WI_DATA_BYTE_2(0xff) +#define P2WI_DATA_BYTE_3(n) (((n) & 0xff) << 16) +#define P2WI_DATA_BYTE_3_MASK P2WI_DATA_BYTE_3(0xff) +#define P2WI_DATA_BYTE_4(n) (((n) & 0xff) << 24) +#define P2WI_DATA_BYTE_4_MASK P2WI_DATA_BYTE_4(0xff) +#define P2WI_DATA_BYTE_5(n) (((n) & 0xff) << 0) +#define P2WI_DATA_BYTE_5_MASK P2WI_DATA_BYTE_5(0xff) +#define P2WI_DATA_BYTE_6(n) (((n) & 0xff) << 8) +#define P2WI_DATA_BYTE_6_MASK P2WI_DATA_BYTE_6(0xff) +#define P2WI_DATA_BYTE_7(n) (((n) & 0xff) << 16) +#define P2WI_DATA_BYTE_7_MASK P2WI_DATA_BYTE_7(0xff) +#define P2WI_DATA_BYTE_8(n) (((n) & 0xff) << 24) +#define P2WI_DATA_BYTE_8_MASK P2WI_DATA_BYTE_8(0xff) + +#define P2WI_LINECTRL_SDA_CTRL_EN (0x1 << 0) +#define P2WI_LINECTRL_SDA_OUT_HIGH (0x1 << 1) +#define P2WI_LINECTRL_SCL_CTRL_EN (0x1 << 2) +#define P2WI_LINECTRL_SCL_OUT_HIGH (0x1 << 3) +#define P2WI_LINECTRL_SDA_STATE_HIGH (0x1 << 4) +#define P2WI_LINECTRL_SCL_STATE_HIGH (0x1 << 5) + +#define P2WI_PM_DEV_ADDR(n) (((n) & 0xff) << 0) +#define P2WI_PM_DEV_ADDR_MASK P2WI_PM_DEV_ADDR(0xff) +#define P2WI_PM_CTRL_ADDR(n) (((n) & 0xff) << 8) +#define P2WI_PM_CTRL_ADDR_MASK P2WI_PM_CTRL_ADDR(0xff) +#define P2WI_PM_INIT_DATA(n) (((n) & 0xff) << 16) +#define P2WI_PM_INIT_DATA_MASK P2WI_PM_INIT_DATA(0xff) +#define P2WI_PM_INIT_SEND (0x1 << 31) + +struct sunxi_p2wi_reg { + u32 ctrl; /* 0x00 control */ + u32 cc; /* 0x04 clock control */ + u32 irq; /* 0x08 interrupt */ + u32 status; /* 0x0c status */ + u32 dataddr0; /* 0x10 data address 0 */ + u32 dataddr1; /* 0x14 data address 1 */ + u32 numbytes; /* 0x18 num bytes */ + u32 data0; /* 0x1c data buffer 0 */ + u32 data1; /* 0x20 data buffer 1 */ + u32 linectrl; /* 0x24 line control */ + u32 pm; /* 0x28 power management */ +}; + +void p2wi_init(void); +int p2wi_change_to_p2wi_mode(u8 slave_addr, u8 ctrl_reg, u8 init_data); +int p2wi_read(const u8 addr, u8 *data); +int p2wi_write(const u8 addr, u8 data); + +#endif /* _SUNXI_P2WI_H */

On Fri, 2014-11-07 at 20:46 +0100, Hans de Goede wrote:
From: Oliver Schinagl oliver@schinagl.nl
The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.
Signed-off-by: Oliver Schinagl oliver@schinagl.nl Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
One question:
+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;
- unsigned long tmo = timer_get_us() + 1000000;
- writel(P2WI_PM_DEV_ADDR(slave_addr) |
P2WI_PM_CTRL_ADDR(ctrl_reg) |
P2WI_PM_INIT_DATA(init_data) |
P2WI_PM_INIT_SEND,
&p2wi->pm);
- while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
if (timer_get_us() > tmo)
return -EFAULT;
You don't mean ETIME(DOUT) or something here, do you? EFAULT seems a bit odd.
Ian.

Hi,
On 11/08/2014 11:14 AM, Ian Campbell wrote:
On Fri, 2014-11-07 at 20:46 +0100, Hans de Goede wrote:
From: Oliver Schinagl oliver@schinagl.nl
The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.
Signed-off-by: Oliver Schinagl oliver@schinagl.nl Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
One question:
+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;
- unsigned long tmo = timer_get_us() + 1000000;
- writel(P2WI_PM_DEV_ADDR(slave_addr) |
P2WI_PM_CTRL_ADDR(ctrl_reg) |
P2WI_PM_INIT_DATA(init_data) |
P2WI_PM_INIT_SEND,
&p2wi->pm);
- while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
if (timer_get_us() > tmo)
return -EFAULT;
You don't mean ETIME(DOUT) or something here, do you? EFAULT seems a bit odd.
Yes, ETIME would be much better will fix for v4.
Regards,
Hans

From: Oliver Schinagl oliver@schinagl.nl
The A31 uses the AXP221 pmic for various voltages.
Signed-off-by: Oliver Schinagl oliver@schinagl.nl Signed-off-by: Hans de Goede hdegoede@redhat.com --- board/sunxi/board.c | 26 +++++++ drivers/power/Kconfig | 47 ++++++++++++ drivers/power/Makefile | 1 + drivers/power/axp221.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++ include/axp221.h | 42 ++++++++++ 5 files changed, 321 insertions(+) create mode 100644 drivers/power/axp221.c create mode 100644 include/axp221.h
diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 03890c8..e6ec5b8 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -19,6 +19,9 @@ #ifdef CONFIG_AXP209_POWER #include <axp209.h> #endif +#ifdef CONFIG_AXP221_POWER +#include <axp221.h> +#endif #include <asm/arch/clock.h> #include <asm/arch/cpu.h> #include <asm/arch/dram.h> @@ -169,6 +172,29 @@ void sunxi_board_init(void) power_failed |= axp209_set_ldo3(2800); power_failed |= axp209_set_ldo4(2800); #endif +#ifdef CONFIG_AXP221_POWER + power_failed = axp221_init(); + power_failed |= axp221_set_dcdc1(3000); + power_failed |= axp221_set_dcdc2(1200); + power_failed |= axp221_set_dcdc3(1200); + power_failed |= axp221_set_dcdc4(1200); + power_failed |= axp221_set_dcdc5(1500); +#if CONFIG_AXP221_DLDO1_VOLT != -1 + power_failed |= axp221_set_dldo1(CONFIG_AXP221_DLDO1_VOLT); +#endif +#if CONFIG_AXP221_DLDO4_VOLT != -1 + power_failed |= axp221_set_dldo4(CONFIG_AXP221_DLDO4_VOLT); +#endif +#if CONFIG_AXP221_ALDO1_VOLT != -1 + power_failed |= axp221_set_aldo1(CONFIG_AXP221_ALDO1_VOLT); +#endif +#if CONFIG_AXP221_ALDO2_VOLT != -1 + power_failed |= axp221_set_aldo2(CONFIG_AXP221_ALDO2_VOLT); +#endif +#if CONFIG_AXP221_ALDO3_VOLT != -1 + power_failed |= axp221_set_aldo3(CONFIG_AXP221_ALDO3_VOLT); +#endif +#endif
printf("DRAM:"); ramsize = sunxi_dram_init(); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e69de29..1ec7c0e 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -0,0 +1,47 @@ +config AXP221_POWER + boolean "axp221 pmic support" + depends on MACH_SUN6I + default y + ---help--- + Say y here to enable support for the axp221 pmic found on most sun6i + (A31) boards. + +config AXP221_DLDO1_VOLT + int "axp221 dldo1 voltage" + depends on AXP221_POWER + default -1 + ---help--- + Set the voltage (mV) to program the axp221 dldo1 at, set to -1 to + disable dldo1. + +config AXP221_DLDO4_VOLT + int "axp221 dldo4 voltage" + depends on AXP221_POWER + default -1 + ---help--- + Set the voltage (mV) to program the axp221 dldo4 at, set to -1 to + disable dldo4. + +config AXP221_ALDO1_VOLT + int "axp221 aldo1 voltage" + depends on AXP221_POWER + default -1 + ---help--- + Set the voltage (mV) to program the axp221 aldo1 at, set to -1 to + disable aldo1. + +config AXP221_ALDO2_VOLT + int "axp221 aldo2 voltage" + depends on AXP221_POWER + default -1 + ---help--- + Set the voltage (mV) to program the axp221 aldo2 at, set to -1 to + disable aldo2. + +config AXP221_ALDO3_VOLT + int "axp221 aldo3 voltage" + depends on AXP221_POWER + default -1 + ---help--- + Set the voltage (mV) to program the axp221 aldo3 at, set to -1 to + disable aldo3. diff --git a/drivers/power/Makefile b/drivers/power/Makefile index dc64e4d..04bd996 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -7,6 +7,7 @@
obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o +obj-$(CONFIG_AXP221_POWER) += axp221.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o obj-$(CONFIG_FTPMU010_POWER) += ftpmu010.o obj-$(CONFIG_TPS6586X_POWER) += tps6586x.o diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c new file mode 100644 index 0000000..f4dc72e --- /dev/null +++ b/drivers/power/axp221.c @@ -0,0 +1,205 @@ +/* + * (C) Copyright 2013 Oliver Schinagl oliver@schinagl.nl + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/p2wi.h> +#include <axp221.h> + +static u8 axp221_mvolt_to_cfg(int mvolt, int min, int max, int div) +{ + if (mvolt < min) + mvolt = min; + else if (mvolt > max) + mvolt = max; + + return (mvolt - min) / div; +} + +int axp221_set_dcdc1(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 1600, 3400, 100); + + ret = p2wi_write(AXP221_DCDC1_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL2, &cfg); + if (ret) + return ret; + + cfg |= 1 << 7; + return p2wi_write(AXP221_OUTPUT_CTRL2, cfg); +} + +int axp221_set_dcdc2(unsigned int mvolt) +{ + u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20); + + return p2wi_write(AXP221_DCDC2_CTRL, cfg); +} + +int axp221_set_dcdc3(unsigned int mvolt) +{ + u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1860, 20); + + return p2wi_write(AXP221_DCDC3_CTRL, cfg); +} + +int axp221_set_dcdc4(unsigned int mvolt) +{ + u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20); + + return p2wi_write(AXP221_DCDC4_CTRL, cfg); +} + +int axp221_set_dcdc5(unsigned int mvolt) +{ + u8 cfg = axp221_mvolt_to_cfg(mvolt, 1000, 2550, 50); + + return p2wi_write(AXP221_DCDC5_CTRL, cfg); +} + +int axp221_set_dldo1(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + ret = p2wi_write(AXP221_DLDO1_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL2, &cfg); + if (ret) + return ret; + + cfg |= 1 << 3; + return p2wi_write(AXP221_OUTPUT_CTRL2, cfg); +} + +int axp221_set_dldo2(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + ret = p2wi_write(AXP221_DLDO2_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL2, &cfg); + if (ret) + return ret; + + cfg |= 1 << 4; + return p2wi_write(AXP221_OUTPUT_CTRL2, cfg); +} + +int axp221_set_dldo3(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + ret = p2wi_write(AXP221_DLDO3_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL2, &cfg); + if (ret) + return ret; + + cfg |= 1 << 5; + return p2wi_write(AXP221_OUTPUT_CTRL2, cfg); +} + +int axp221_set_dldo4(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + ret = p2wi_write(AXP221_DLDO4_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL2, &cfg); + if (ret) + return ret; + + cfg |= 1 << 6; + return p2wi_write(AXP221_OUTPUT_CTRL2, cfg); +} + +int axp221_set_aldo1(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + ret = p2wi_write(AXP221_ALDO1_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL1, &cfg); + if (ret) + return ret; + + cfg |= 1 << 6; + return p2wi_write(AXP221_OUTPUT_CTRL1, cfg); +} + +int axp221_set_aldo2(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + ret = p2wi_write(AXP221_ALDO2_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL1, &cfg); + if (ret) + return ret; + + cfg |= 1 << 7; + return p2wi_write(AXP221_OUTPUT_CTRL1, cfg); +} + +int axp221_set_aldo3(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + ret = p2wi_write(AXP221_ALDO3_CTRL, cfg); + if (ret) + return ret; + + ret = p2wi_read(AXP221_OUTPUT_CTRL3, &cfg); + if (ret) + return ret; + + cfg |= 1 << 7; + return p2wi_write(AXP221_OUTPUT_CTRL3, cfg); +} + +int axp221_init(void) +{ + u8 axp_chip_id; + int ret; + + p2wi_init(); + ret = p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR, + AXP221_INIT_DATA); + if (ret) + return ret; + + ret = p2wi_read(AXP221_CHIP_ID, &axp_chip_id); + if (ret) + return ret; + + if (!(axp_chip_id == 0x6 || axp_chip_id == 0x7 || axp_chip_id == 0x17)) + return -ENODEV; + + return 0; +} diff --git a/include/axp221.h b/include/axp221.h new file mode 100644 index 0000000..4fb3c35 --- /dev/null +++ b/include/axp221.h @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2013 Oliver Schinagl oliver@schinagl.nl + * + * X-Powers AXP221 Power Management IC driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define AXP221_CHIP_ADDR 0x68 +#define AXP221_CTRL_ADDR 0x3e +#define AXP221_INIT_DATA 0x3e + +#define AXP221_CHIP_ID 0x03 +#define AXP221_OUTPUT_CTRL1 0x10 +#define AXP221_OUTPUT_CTRL2 0x12 +#define AXP221_OUTPUT_CTRL3 0x13 +#define AXP221_DLDO1_CTRL 0x15 +#define AXP221_DLDO2_CTRL 0x16 +#define AXP221_DLDO3_CTRL 0x17 +#define AXP221_DLDO4_CTRL 0x18 +#define AXP221_DCDC1_CTRL 0x21 +#define AXP221_DCDC2_CTRL 0x22 +#define AXP221_DCDC3_CTRL 0x23 +#define AXP221_DCDC4_CTRL 0x24 +#define AXP221_DCDC5_CTRL 0x25 +#define AXP221_ALDO1_CTRL 0x28 +#define AXP221_ALDO2_CTRL 0x28 +#define AXP221_ALDO3_CTRL 0x2a + +int axp221_set_dcdc1(unsigned int mvolt); +int axp221_set_dcdc2(unsigned int mvolt); +int axp221_set_dcdc3(unsigned int mvolt); +int axp221_set_dcdc4(unsigned int mvolt); +int axp221_set_dcdc5(unsigned int mvolt); +int axp221_set_dldo1(unsigned int mvolt); +int axp221_set_dldo2(unsigned int mvolt); +int axp221_set_dldo3(unsigned int mvolt); +int axp221_set_dldo4(unsigned int mvolt); +int axp221_set_aldo1(unsigned int mvolt); +int axp221_set_aldo2(unsigned int mvolt); +int axp221_set_aldo3(unsigned int mvolt); +int axp221_init(void);

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
From: Oliver Schinagl oliver@schinagl.nl
The A31 uses the AXP221 pmic for various voltages.
Signed-off-by: Oliver Schinagl oliver@schinagl.nl Signed-off-by: Hans de Goede hdegoede@redhat.com
- cfg |= 1 << 7;
- cfg |= 1 << 3;
- cfg |= 1 << 4;
- cfg |= 1 << 5;
- cfg |= 1 << 6;
- cfg |= 1 << 6;
- cfg |= 1 << 7;
- cfg |= 1 << 7;
Could we get some symbolic names for these please?
Looks like a helper to read/modify/write a p2wi register would reduce the repetition a fair bit.
Ian.

Add clock_init_safe and clockset_pll5 functions, as these are needed for SPL support resp. DRAM init (which is needed for SPL too).
Also add some extra clock register constant defines.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 77 +++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock.h | 1 + arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 27 +++++++++- arch/arm/include/asm/arch-sunxi/prcm.h | 1 + include/configs/sun6i.h | 1 + 5 files changed, 106 insertions(+), 1 deletion(-)
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 1eae976..16ab6f3 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -16,6 +16,33 @@ #include <asm/arch/prcm.h> #include <asm/arch/sys_proto.h>
+#ifdef CONFIG_SPL_BUILD +void clock_init_safe(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_prcm_reg * const prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + + /* Set PLL ldo voltage without this PLL6 does not work properly */ + clrsetbits_le32(&prcm->pll_ctrl1, PRCM_PLL_CTRL_LDO_KEY_MASK, + PRCM_PLL_CTRL_LDO_KEY); + clrsetbits_le32(&prcm->pll_ctrl1, ~PRCM_PLL_CTRL_LDO_KEY_MASK, + PRCM_PLL_CTRL_LDO_DIGITAL_EN | PRCM_PLL_CTRL_LDO_ANALOG_EN | + PRCM_PLL_CTRL_EXT_OSC_EN | PRCM_PLL_CTRL_LDO_OUT_L(1140)); + clrbits_le32(&prcm->pll_ctrl1, PRCM_PLL_CTRL_LDO_KEY_MASK); + + clock_set_pll1(408000000); + + writel(AHB1_ABP1_DIV_DEFAULT, &ccm->ahb1_apb1_div); + + writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg); + + writel(MBUS_CLK_DEFAULT, &ccm->mbus0_clk_cfg); + writel(MBUS_CLK_DEFAULT, &ccm->mbus1_clk_cfg); +} +#endif + void clock_init_uart(void) { struct sunxi_ccm_reg *const ccm = @@ -65,6 +92,56 @@ int clock_twi_onoff(int port, int state) return 0; }
+#ifdef CONFIG_SPL_BUILD +void clock_set_pll1(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int k = 1; + int m = 1; + + if (clk > 1152000000) { + k = 2; + } else if (clk > 768000000) { + k = 3; + m = 2; + } + + /* Switch to 24MHz clock while changing PLL1 */ + writel(AXI_DIV_3 << AXI_DIV_SHIFT | + ATB_DIV_2 << ATB_DIV_SHIFT | + 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 | + CCM_PLL1_CTRL_N(clk / (24000000 * k / m)) | + CCM_PLL1_CTRL_K(k) | CCM_PLL1_CTRL_M(m), &ccm->pll1_cfg); + sdelay(200); + + /* Switch CPU to PLL1 */ + writel(AXI_DIV_3 << AXI_DIV_SHIFT | + ATB_DIV_2 << ATB_DIV_SHIFT | + CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT, + &ccm->cpu_axi_cfg); +} +#endif + +void clock_set_pll5(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int k = 2; + const int m = 1; + + /* PLL5 rate = 24000000 * n * k / m */ + writel(CCM_PLL5_CTRL_EN | 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); + + udelay(5500); +} + unsigned int clock_get_pll6(void) { struct sunxi_ccm_reg *const ccm = diff --git a/arch/arm/include/asm/arch-sunxi/clock.h b/arch/arm/include/asm/arch-sunxi/clock.h index 42382a8..b40c16b 100644 --- a/arch/arm/include/asm/arch-sunxi/clock.h +++ b/arch/arm/include/asm/arch-sunxi/clock.h @@ -25,6 +25,7 @@ int clock_init(void); int clock_twi_onoff(int port, int state); void clock_set_pll1(unsigned int hz); +void clock_set_pll5(unsigned int hz); unsigned int clock_get_pll5p(void); unsigned int clock_get_pll6(void); void clock_init_safe(void); diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 1397b35..4992dbc 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -170,7 +170,17 @@ struct sunxi_ccm_reg { #define CPU_CLK_SRC_OSC24M 1 #define CPU_CLK_SRC_PLL1 2
-#define PLL1_CFG_DEFAULT 0x90011b21 +#define CCM_PLL1_CTRL_M(n) ((((n) - 1) & 0x3) << 0) +#define CCM_PLL1_CTRL_K(n) ((((n) - 1) & 0x3) << 4) +#define CCM_PLL1_CTRL_N(n) ((((n) - 1) & 0x1f) << 8) +#define CCM_PLL1_CTRL_MAGIC (0x1 << 16) +#define CCM_PLL1_CTRL_EN (0x1 << 31) + +#define CCM_PLL5_CTRL_M(n) ((((n) - 1) & 0x3) << 0) +#define CCM_PLL5_CTRL_K(n) ((((n) - 1) & 0x3) << 4) +#define CCM_PLL5_CTRL_N(n) ((((n) - 1) & 0x1f) << 8) +#define CCM_PLL5_CTRL_UPD (0x1 << 20) +#define CCM_PLL5_CTRL_EN (0x1 << 31)
#define PLL6_CFG_DEFAULT 0x90041811
@@ -179,6 +189,11 @@ struct sunxi_ccm_reg { #define CCM_PLL6_CTRL_K_SHIFT 4 #define CCM_PLL6_CTRL_K_MASK (0x3 << CCM_PLL6_CTRL_K_SHIFT)
+#define AHB1_ABP1_DIV_DEFAULT 0x00002020 + +#define AXI_GATE_OFFSET_DRAM 0 + +#define AHB_GATE_OFFSET_MCTL 14 #define AHB_GATE_OFFSET_MMC3 11 #define AHB_GATE_OFFSET_MMC2 10 #define AHB_GATE_OFFSET_MMC1 9 @@ -190,6 +205,16 @@ struct sunxi_ccm_reg {
#define CCM_MMC_CTRL_ENABLE (0x1 << 31)
+#define MDFS_CLK_DEFAULT 0x81000002 /* PLL6 / 3 */ + +#define CCM_DRAMCLK_CFG_DIV0(x) ((x - 1) << 8) +#define CCM_DRAMCLK_CFG_DIV0_MASK (0xf << 8) +#define CCM_DRAMCLK_CFG_UPD (0x1 << 16) +#define CCM_DRAMCLK_CFG_RST (0x1 << 31) + +#define MBUS_CLK_DEFAULT 0x81000001 /* PLL6 / 2 */ + +#define AHB_RESET_OFFSET_MCTL 14 #define AHB_RESET_OFFSET_MMC3 11 #define AHB_RESET_OFFSET_MMC2 10 #define AHB_RESET_OFFSET_MMC1 9 diff --git a/arch/arm/include/asm/arch-sunxi/prcm.h b/arch/arm/include/asm/arch-sunxi/prcm.h index 3d3bfa6..88de1ff 100644 --- a/arch/arm/include/asm/arch-sunxi/prcm.h +++ b/arch/arm/include/asm/arch-sunxi/prcm.h @@ -119,6 +119,7 @@ #define PRCM_PLL_CTRL_LDO_OUT_HV(n) \ __PRCM_PLL_CTRL_VDD_LDO_OUT((((n) & 0x7) * 30) + 1160) #define PRCM_PLL_CTRL_LDO_KEY (0xa7 << 24) +#define PRCM_PLL_CTRL_LDO_KEY_MASK (0xff << 24)
#define PRCM_CLK_1WIRE_GATE (0x1 << 31)
diff --git a/include/configs/sun6i.h b/include/configs/sun6i.h index b714474..9558771 100644 --- a/include/configs/sun6i.h +++ b/include/configs/sun6i.h @@ -14,6 +14,7 @@ /* * A31 specific configuration */ +#define CONFIG_CLK_FULL_SPEED 1008000000
#define CONFIG_SYS_PROMPT "sun6i# "

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Add clock_init_safe and clockset_pll5 functions, as these are needed for SPL support resp. DRAM init (which is needed for SPL too).
Also add some extra clock register constant defines.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

Not used atm, for future use (e.g. PSCI).
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/psci.S | 4 +- arch/arm/include/asm/arch-sunxi/cpu.h | 3 +- arch/arm/include/asm/arch-sunxi/cpucfg_sun6i.h | 67 ++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 arch/arm/include/asm/arch-sunxi/cpucfg_sun6i.h
diff --git a/arch/arm/cpu/armv7/sunxi/psci.S b/arch/arm/cpu/armv7/sunxi/psci.S index 0084c81..b9ea78b 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.S +++ b/arch/arm/cpu/armv7/sunxi/psci.S @@ -87,8 +87,8 @@ psci_cpu_on: str r2, [r0] dsb
- movw r0, #(SUNXI_CPUCFG_BASE & 0xffff) - movt r0, #(SUNXI_CPUCFG_BASE >> 16) + movw r0, #(SUN7I_CPUCFG_BASE & 0xffff) + movt r0, #(SUN7I_CPUCFG_BASE >> 16)
@ CPU mask and r1, r1, #3 @ only care about first cluster diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 0de79a0..d34690e 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -70,7 +70,7 @@
#define SUNXI_TP_BASE 0x01c25000 #define SUNXI_PMU_BASE 0x01c25400 -#define SUNXI_CPUCFG_BASE 0x01c25c00 +#define SUN7I_CPUCFG_BASE 0x01c25c00
#define SUNXI_UART0_BASE 0x01c28000 #define SUNXI_UART1_BASE 0x01c28400 @@ -111,6 +111,7 @@ #define SUNXI_AVG_BASE 0x01ea0000
#define SUNXI_PRCM_BASE 0x01f01400 +#define SUN6I_CPUCFG_BASE 0x01f01c00 #define SUNXI_R_UART_BASE 0x01f02800 #define SUNXI_R_PIO_BASE 0x01f02c00 #define SUNXI_P2WI_BASE 0x01f03400 diff --git a/arch/arm/include/asm/arch-sunxi/cpucfg_sun6i.h b/arch/arm/include/asm/arch-sunxi/cpucfg_sun6i.h new file mode 100644 index 0000000..e2a29cb --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/cpucfg_sun6i.h @@ -0,0 +1,67 @@ +/* + * Sunxi A31 CPUCFG register definition. + * + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_CPUCFG_H +#define _SUNXI_CPUCFG_H + +#ifndef __ASSEMBLY__ + +struct sunxi_cpucfg_reg { + u8 res0[0x40]; /* 0x000 */ + u32 cpu0_rst; /* 0x040 */ + u32 cpu0_ctrl; /* 0x044 */ + u32 cpu0_status; /* 0x048 */ + u8 res1[0x34]; /* 0x04c */ + u32 cpu1_rst; /* 0x080 */ + u32 cpu1_ctrl; /* 0x084 */ + u32 cpu1_status; /* 0x088 */ + u8 res2[0x34]; /* 0x08c */ + u32 cpu2_rst; /* 0x0c0 */ + u32 cpu2_ctrl; /* 0x0c4 */ + u32 cpu2_status; /* 0x0c8 */ + u8 res3[0x34]; /* 0x0cc */ + u32 cpu3_rst; /* 0x100 */ + u32 cpu3_ctrl; /* 0x104 */ + u32 cpu3_status; /* 0x108 */ + u8 res4[0x78]; /* 0x10c */ + u32 gen_ctrl; /* 0x184 */ + u32 l2_status; /* 0x188 */ + u8 res5[0x4]; /* 0x18c */ + u32 event_in; /* 0x190 */ + u8 res6[0xc]; /* 0x194 */ + u32 super_standy_flag; /* 0x1a0 */ + u32 priv0; /* 0x1a4 */ + u32 priv1; /* 0x1a8 */ + u8 res7[0x54]; /* 0x1ac */ + u32 idle_cnt0_low; /* 0x200 */ + u32 idle_cnt0_high; /* 0x204 */ + u32 idle_cnt0_ctrl; /* 0x208 */ + u8 res8[0x4]; /* 0x20c */ + u32 idle_cnt1_low; /* 0x210 */ + u32 idle_cnt1_high; /* 0x214 */ + u32 idle_cnt1_ctrl; /* 0x218 */ + u8 res9[0x4]; /* 0x21c */ + u32 idle_cnt2_low; /* 0x220 */ + u32 idle_cnt2_high; /* 0x224 */ + u32 idle_cnt2_ctrl; /* 0x228 */ + u8 res10[0x4]; /* 0x22c */ + u32 idle_cnt3_low; /* 0x230 */ + u32 idle_cnt3_high; /* 0x234 */ + u32 idle_cnt3_ctrl; /* 0x238 */ + u8 res11[0x4]; /* 0x23c */ + u32 idle_cnt4_low; /* 0x240 */ + u32 idle_cnt4_high; /* 0x244 */ + u32 idle_cnt4_ctrl; /* 0x248 */ + u8 res12[0x34]; /* 0x24c */ + u32 cnt64_ctrl; /* 0x280 */ + u32 cnt64_low; /* 0x284 */ + u32 cnt64_high; /* 0x288 */ +}; + +#endif /* __ASSEMBLY__ */ +#endif /* _SUNXI_CPUCFG_H */

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Not used atm, for future use (e.g. PSCI).
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

In preparation for adding sun6i dram support.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/Makefile | 6 +- arch/arm/cpu/armv7/sunxi/dram.c | 750 --------------------------- arch/arm/cpu/armv7/sunxi/dram_sun4i.c | 750 +++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/dram.h | 167 +----- arch/arm/include/asm/arch-sunxi/dram_sun4i.h | 182 +++++++ 5 files changed, 937 insertions(+), 918 deletions(-) delete mode 100644 arch/arm/cpu/armv7/sunxi/dram.c create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun4i.c create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun4i.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index b3a3601..48cca0b 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -28,9 +28,9 @@ endif endif
ifdef CONFIG_SPL_BUILD -obj-$(CONFIG_MACH_SUN4I) += dram.o -obj-$(CONFIG_MACH_SUN5I) += dram.o -obj-$(CONFIG_MACH_SUN7I) += dram.o +obj-$(CONFIG_MACH_SUN4I) += dram_sun4i.o +obj-$(CONFIG_MACH_SUN5I) += dram_sun4i.o +obj-$(CONFIG_MACH_SUN7I) += dram_sun4i.o ifdef CONFIG_SPL_FEL obj-y += start.o endif diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c deleted file mode 100644 index dc9fdb9..0000000 --- a/arch/arm/cpu/armv7/sunxi/dram.c +++ /dev/null @@ -1,750 +0,0 @@ -/* - * sunxi DRAM controller initialization - * (C) Copyright 2012 Henrik Nordstrom henrik@henriknordstrom.net - * (C) Copyright 2013 Luke Kenneth Casson Leighton lkcl@lkcl.net - * - * Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c - * and earlier U-Boot Allwiner A10 SPL work - * - * (C) Copyright 2007-2012 - * Allwinner Technology Co., Ltd. <www.allwinnertech.com> - * Berg Xing bergxing@allwinnertech.com - * Tom Cubie tangliang@allwinnertech.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Unfortunately the only documentation we have on the sun7i DRAM - * controller is Allwinner boot0 + boot1 code, and that code uses - * magic numbers & shifts with no explanations. Hence this code is - * rather undocumented and full of magic. - */ - -#include <common.h> -#include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/dram.h> -#include <asm/arch/timer.h> -#include <asm/arch/sys_proto.h> - -#define CPU_CFG_CHIP_VER(n) ((n) << 6) -#define CPU_CFG_CHIP_VER_MASK CPU_CFG_CHIP_VER(0x3) -#define CPU_CFG_CHIP_REV_A 0x0 -#define CPU_CFG_CHIP_REV_C1 0x1 -#define CPU_CFG_CHIP_REV_C2 0x2 -#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); -} - -/* - * Wait up to 1s for mask to be set in given reg. - */ -static inline void await_bits_set(u32 *reg, u32 mask) -{ - await_completion(reg, mask, mask); -} - -/* - * This performs the external DRAM reset by driving the RESET pin low and - * then high again. According to the DDR3 spec, the RESET pin needs to be - * kept low for at least 200 us. - */ -static void mctl_ddr3_reset(void) -{ - struct sunxi_dram_reg *dram = - (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - -#ifdef CONFIG_MACH_SUN4I - struct sunxi_timer_reg *timer = - (struct sunxi_timer_reg *)SUNXI_TIMER_BASE; - u32 reg_val; - - writel(0, &timer->cpu_cfg); - reg_val = readl(&timer->cpu_cfg); - - if ((reg_val & CPU_CFG_CHIP_VER_MASK) != - CPU_CFG_CHIP_VER(CPU_CFG_CHIP_REV_A)) { - setbits_le32(&dram->mcr, DRAM_MCR_RESET); - udelay(200); - clrbits_le32(&dram->mcr, DRAM_MCR_RESET); - } else -#endif - { - clrbits_le32(&dram->mcr, DRAM_MCR_RESET); - udelay(200); - setbits_le32(&dram->mcr, DRAM_MCR_RESET); - } - /* After the RESET pin is de-asserted, the DDR3 spec requires to wait - * for additional 500 us before driving the CKE pin (Clock Enable) - * high. The duration of this delay can be configured in the SDR_IDCR - * (Initialization Delay Configuration Register) and applied - * automatically by the DRAM controller during the DDR3 initialization - * step. But SDR_IDCR has limited range on sun4i/sun5i hardware and - * can't provide sufficient delay at DRAM clock frequencies higher than - * 524 MHz (while Allwinner A13 supports DRAM clock frequency up to - * 533 MHz according to the datasheet). Additionally, there is no - * official documentation for the SDR_IDCR register anywhere, and - * there is always a chance that we are interpreting it wrong. - * Better be safe than sorry, so add an explicit delay here. */ - udelay(500); -} - -static void mctl_set_drive(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - -#ifdef CONFIG_MACH_SUN7I - clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3) | (0x3 << 28), -#else - clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3), -#endif - DRAM_MCR_MODE_EN(0x3) | - 0xffc); -} - -static void mctl_itm_disable(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - - clrsetbits_le32(&dram->ccr, DRAM_CCR_INIT, DRAM_CCR_ITM_OFF); -} - -static void mctl_itm_enable(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - - clrbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF); -} - -static void mctl_itm_reset(void) -{ - mctl_itm_disable(); - udelay(1); /* ITM reset needs a bit of delay */ - mctl_itm_enable(); - udelay(1); -} - -static void mctl_enable_dll0(u32 phase) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - - clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, - ((phase >> 16) & 0x3f) << 6); - clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET, DRAM_DLLCR_DISABLE); - udelay(2); - - clrbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET | DRAM_DLLCR_DISABLE); - udelay(22); - - clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_DISABLE, DRAM_DLLCR_NRESET); - udelay(22); -} - -/* Get the number of DDR byte lanes */ -static u32 mctl_get_number_of_lanes(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - if ((readl(&dram->dcr) & DRAM_DCR_BUS_WIDTH_MASK) == - DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT)) - return 4; - else - return 2; -} - -/* - * Note: This differs from pm/standby in that it checks the bus width - */ -static void mctl_enable_dllx(u32 phase) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 i, number_of_lanes; - - number_of_lanes = mctl_get_number_of_lanes(); - - for (i = 1; i <= number_of_lanes; i++) { - clrsetbits_le32(&dram->dllcr[i], 0xf << 14, - (phase & 0xf) << 14); - clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET, - DRAM_DLLCR_DISABLE); - phase >>= 4; - } - udelay(2); - - for (i = 1; i <= number_of_lanes; i++) - clrbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET | - DRAM_DLLCR_DISABLE); - udelay(22); - - for (i = 1; i <= number_of_lanes; i++) - clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_DISABLE, - DRAM_DLLCR_NRESET); - udelay(22); -} - -static u32 hpcr_value[32] = { -#ifdef CONFIG_MACH_SUN5I - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0x1031, 0x1031, 0x0735, 0x1035, - 0x1035, 0x0731, 0x1031, 0, - 0x0301, 0x0301, 0x0301, 0x0301, - 0x0301, 0x0301, 0x0301, 0 -#endif -#ifdef CONFIG_MACH_SUN4I - 0x0301, 0x0301, 0x0301, 0x0301, - 0x0301, 0x0301, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0x1031, 0x1031, 0x0735, 0x5031, - 0x1035, 0x0731, 0x1031, 0x0735, - 0x1035, 0x1031, 0x0731, 0x1035, - 0x1031, 0x0301, 0x0301, 0x0731 -#endif -#ifdef CONFIG_MACH_SUN7I - 0x0301, 0x0301, 0x0301, 0x0301, - 0x0301, 0x0301, 0x0301, 0x0301, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0x1031, 0x1031, 0x0735, 0x1035, - 0x1035, 0x0731, 0x1031, 0x0735, - 0x1035, 0x1031, 0x0731, 0x1035, - 0x0001, 0x1031, 0, 0x1031 - /* last row differs from boot0 source table - * 0x1031, 0x0301, 0x0301, 0x0731 - * but boot0 code skips #28 and #30, and sets #29 and #31 to the - * value from #28 entry (0x1031) - */ -#endif -}; - -static void mctl_configure_hostport(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 i; - - for (i = 0; i < 32; i++) - writel(hpcr_value[i], &dram->hpcr[i]); -} - -static void mctl_setup_dram_clock(u32 clk, u32 mbus_clk) -{ - u32 reg_val; - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - u32 pll5p_clk, pll6x_clk; - u32 pll5p_div, pll6x_div; - u32 pll5p_rate, pll6x_rate; - - /* setup DRAM PLL */ - reg_val = readl(&ccm->pll5_cfg); - reg_val &= ~CCM_PLL5_CTRL_M_MASK; /* set M to 0 (x1) */ - reg_val &= ~CCM_PLL5_CTRL_K_MASK; /* set K to 0 (x1) */ - reg_val &= ~CCM_PLL5_CTRL_N_MASK; /* set N to 0 (x0) */ - reg_val &= ~CCM_PLL5_CTRL_P_MASK; /* set P to 0 (x1) */ -#ifdef CONFIG_OLD_SUNXI_KERNEL_COMPAT - /* Old kernels are hardcoded to P=1 (divide by 2) */ - reg_val |= CCM_PLL5_CTRL_P(1); -#endif - if (clk >= 540 && clk < 552) { - /* dram = 540MHz */ - reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); - reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); - reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(15)); - } else if (clk >= 512 && clk < 528) { - /* dram = 512MHz */ - reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); - reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(4)); - reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(16)); - } else if (clk >= 496 && clk < 504) { - /* dram = 496MHz */ - reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); - reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); - reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(31)); - } else if (clk >= 468 && clk < 480) { - /* dram = 468MHz */ - reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); - reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); - reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(13)); - } else if (clk >= 396 && clk < 408) { - /* dram = 396MHz */ - reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); - reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); - reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(11)); - } else { - /* any other frequency that is a multiple of 24 */ - reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); - reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); - reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(clk / 24)); - } - reg_val &= ~CCM_PLL5_CTRL_VCO_GAIN; /* PLL VCO Gain off */ - reg_val |= CCM_PLL5_CTRL_EN; /* PLL On */ - writel(reg_val, &ccm->pll5_cfg); - udelay(5500); - - setbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_DDR_CLK); - -#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I) - /* reset GPS */ - clrbits_le32(&ccm->gps_clk_cfg, CCM_GPS_CTRL_RESET | CCM_GPS_CTRL_GATE); - setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS); - udelay(1); - clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS); -#endif - - /* setup MBUS clock */ - if (!mbus_clk) - mbus_clk = 300; - - /* PLL5P and PLL6 are the potential clock sources for MBUS */ - pll6x_clk = clock_get_pll6() / 1000000; -#ifdef CONFIG_MACH_SUN7I - pll6x_clk *= 2; /* sun7i uses PLL6*2, sun5i uses just PLL6 */ -#endif - pll5p_clk = clock_get_pll5p() / 1000000; - pll6x_div = DIV_ROUND_UP(pll6x_clk, mbus_clk); - pll5p_div = DIV_ROUND_UP(pll5p_clk, mbus_clk); - pll6x_rate = pll6x_clk / pll6x_div; - pll5p_rate = pll5p_clk / pll5p_div; - - if (pll6x_div <= 16 && pll6x_rate > pll5p_rate) { - /* use PLL6 as the MBUS clock source */ - reg_val = CCM_MBUS_CTRL_GATE | - CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL6) | - CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | - CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll6x_div)); - } else if (pll5p_div <= 16) { - /* use PLL5P as the MBUS clock source */ - reg_val = CCM_MBUS_CTRL_GATE | - CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL5) | - CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | - CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll5p_div)); - } else { - panic("Bad mbus_clk\n"); - } - writel(reg_val, &ccm->mbus_clk_cfg); - - /* - * open DRAMC AHB & DLL register clock - * close it first - */ -#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) - clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); -#else - clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM); -#endif - udelay(22); - - /* then open it */ -#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) - setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); -#else - setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM); -#endif - udelay(22); -} - -/* - * The data from rslrX and rdgrX registers (X=rank) is stored - * in a single 32-bit value using the following format: - * bits [31:26] - DQS gating system latency for byte lane 3 - * bits [25:24] - DQS gating phase select for byte lane 3 - * bits [23:18] - DQS gating system latency for byte lane 2 - * bits [17:16] - DQS gating phase select for byte lane 2 - * bits [15:10] - DQS gating system latency for byte lane 1 - * bits [ 9:8 ] - DQS gating phase select for byte lane 1 - * bits [ 7:2 ] - DQS gating system latency for byte lane 0 - * bits [ 1:0 ] - DQS gating phase select for byte lane 0 - */ -static void mctl_set_dqs_gating_delay(int rank, u32 dqs_gating_delay) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 lane, number_of_lanes = mctl_get_number_of_lanes(); - /* rank0 gating system latency (3 bits per lane: cycles) */ - u32 slr = readl(rank == 0 ? &dram->rslr0 : &dram->rslr1); - /* rank0 gating phase select (2 bits per lane: 90, 180, 270, 360) */ - u32 dgr = readl(rank == 0 ? &dram->rdgr0 : &dram->rdgr1); - for (lane = 0; lane < number_of_lanes; lane++) { - u32 tmp = dqs_gating_delay >> (lane * 8); - slr &= ~(7 << (lane * 3)); - slr |= ((tmp >> 2) & 7) << (lane * 3); - dgr &= ~(3 << (lane * 2)); - dgr |= (tmp & 3) << (lane * 2); - } - writel(slr, rank == 0 ? &dram->rslr0 : &dram->rslr1); - writel(dgr, rank == 0 ? &dram->rdgr0 : &dram->rdgr1); -} - -static int dramc_scan_readpipe(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 reg_val; - - /* data training trigger */ - clrbits_le32(&dram->csr, DRAM_CSR_FAILED); - setbits_le32(&dram->ccr, DRAM_CCR_DATA_TRAINING); - - /* check whether data training process has completed */ - await_bits_clear(&dram->ccr, DRAM_CCR_DATA_TRAINING); - - /* check data training result */ - reg_val = readl(&dram->csr); - if (reg_val & DRAM_CSR_FAILED) - return -1; - - return 0; -} - -static void dramc_clock_output_en(u32 on) -{ -#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - - if (on) - setbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT); - else - clrbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT); -#endif -#ifdef CONFIG_MACH_SUN4I - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - if (on) - setbits_le32(&ccm->dram_clk_cfg, CCM_DRAM_CTRL_DCLK_OUT); - else - clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAM_CTRL_DCLK_OUT); -#endif -} - -/* tRFC in nanoseconds for different densities (from the DDR3 spec) */ -static const u16 tRFC_DDR3_table[6] = { - /* 256Mb 512Mb 1Gb 2Gb 4Gb 8Gb */ - 90, 90, 110, 160, 300, 350 -}; - -static void dramc_set_autorefresh_cycle(u32 clk, u32 density) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 tRFC, tREFI; - - tRFC = (tRFC_DDR3_table[density] * clk + 999) / 1000; - tREFI = (7987 * clk) >> 10; /* <= 7.8us */ - - writel(DRAM_DRR_TREFI(tREFI) | DRAM_DRR_TRFC(tRFC), &dram->drr); -} - -/* Calculate the value for A11, A10, A9 bits in MR0 (write recovery) */ -static u32 ddr3_write_recovery(u32 clk) -{ - u32 twr_ns = 15; /* DDR3 spec says that it is 15ns for all speed bins */ - u32 twr_ck = (twr_ns * clk + 999) / 1000; - if (twr_ck < 5) - return 1; - else if (twr_ck <= 8) - return twr_ck - 4; - else if (twr_ck <= 10) - return 5; - else - return 6; -} - -/* - * If the dram->ppwrsctl (SDR_DPCR) register has the lowest bit set to 1, this - * means that DRAM is currently in self-refresh mode and retaining the old - * data. Since we have no idea what to do in this situation yet, just set this - * register to 0 and initialize DRAM in the same way as on any normal reboot - * (discarding whatever was stored there). - * - * Note: on sun7i hardware, the highest 16 bits need to be set to 0x1651 magic - * value for this write operation to have any effect. On sun5i hadware this - * magic value is not necessary. And on sun4i hardware the writes to this - * register seem to have no effect at all. - */ -static void mctl_disable_power_save(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - writel(0x16510000, &dram->ppwrsctl); -} - -/* - * After the DRAM is powered up or reset, the DDR3 spec requires to wait at - * least 500 us before driving the CKE pin (Clock Enable) high. The dram->idct - * (SDR_IDCR) register appears to configure this delay, which gets applied - * right at the time when the DRAM initialization is activated in the - * 'mctl_ddr3_initialize' function. - */ -static void mctl_set_cke_delay(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - - /* The CKE delay is represented in DRAM clock cycles, multiplied by N - * (where N=2 for sun4i/sun5i and N=3 for sun7i). Here it is set to - * the maximum possible value 0x1ffff, just like in the Allwinner's - * boot0 bootloader. The resulting delay value is somewhere between - * ~0.4 ms (sun5i with 648 MHz DRAM clock speed) and ~1.1 ms (sun7i - * with 360 MHz DRAM clock speed). */ - setbits_le32(&dram->idcr, 0x1ffff); -} - -/* - * This triggers the DRAM initialization. It performs sending the mode registers - * to the DRAM among other things. Very likely the ZQCL command is also getting - * executed (to do the initial impedance calibration on the DRAM side of the - * wire). The memory controller and the PHY must be already configured before - * calling this function. - */ -static void mctl_ddr3_initialize(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - setbits_le32(&dram->ccr, DRAM_CCR_INIT); - await_bits_clear(&dram->ccr, DRAM_CCR_INIT); -} - -/* - * Perform impedance calibration on the DRAM controller side of the wire. - */ -static void mctl_set_impedance(u32 zq, u32 odt_en) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 reg_val; - u32 zprog = zq & 0xFF, zdata = (zq >> 8) & 0xFFFFF; - -#ifndef CONFIG_MACH_SUN7I - /* Appears that some kind of automatically initiated default - * ZQ calibration is already in progress at this point on sun4i/sun5i - * hardware, but not on sun7i. So it is reasonable to wait for its - * completion before doing anything else. */ - await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); -#endif - - /* ZQ calibration is not really useful unless ODT is enabled */ - if (!odt_en) - return; - -#ifdef CONFIG_MACH_SUN7I - /* Enabling ODT in SDR_IOCR on sun7i hardware results in a deadlock - * unless bit 24 is set in SDR_ZQCR1. Not much is known about the - * SDR_ZQCR1 register, but there are hints indicating that it might - * be related to periodic impedance re-calibration. This particular - * magic value is borrowed from the Allwinner boot0 bootloader, and - * using it helps to avoid troubles */ - writel((1 << 24) | (1 << 1), &dram->zqcr1); -#endif - - /* Needed at least for sun5i, because it does not self clear there */ - clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); - - if (zdata) { - /* Set the user supplied impedance data */ - reg_val = DRAM_ZQCR0_ZDEN | zdata; - writel(reg_val, &dram->zqcr0); - /* no need to wait, this takes effect immediately */ - } else { - /* Do the calibration using the external resistor */ - reg_val = DRAM_ZQCR0_ZCAL | DRAM_ZQCR0_IMP_DIV(zprog); - writel(reg_val, &dram->zqcr0); - /* Wait for the new impedance configuration to settle */ - await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); - } - - /* Needed at least for sun5i, because it does not self clear there */ - clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); - - /* Set I/O configure register */ - writel(DRAM_IOCR_ODT_EN(odt_en), &dram->iocr); -} - -static unsigned long dramc_init_helper(struct dram_para *para) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 reg_val; - u32 density; - int ret_val; - - /* - * only single rank DDR3 is supported by this code even though the - * hardware can theoretically support DDR2 and up to two ranks - */ - if (para->type != DRAM_MEMORY_TYPE_DDR3 || para->rank_num != 1) - return 0; - - /* setup DRAM relative clock */ - mctl_setup_dram_clock(para->clock, para->mbus_clock); - - /* Disable any pad power save control */ - mctl_disable_power_save(); - - mctl_set_drive(); - - /* dram clock off */ - dramc_clock_output_en(0); - -#ifdef CONFIG_MACH_SUN4I - /* select dram controller 1 */ - writel(DRAM_CSEL_MAGIC, &dram->csel); -#endif - - mctl_itm_disable(); - mctl_enable_dll0(para->tpr3); - - /* configure external DRAM */ - reg_val = DRAM_DCR_TYPE_DDR3; - reg_val |= DRAM_DCR_IO_WIDTH(para->io_width >> 3); - - if (para->density == 256) - density = DRAM_DCR_CHIP_DENSITY_256M; - else if (para->density == 512) - density = DRAM_DCR_CHIP_DENSITY_512M; - else if (para->density == 1024) - density = DRAM_DCR_CHIP_DENSITY_1024M; - else if (para->density == 2048) - density = DRAM_DCR_CHIP_DENSITY_2048M; - else if (para->density == 4096) - density = DRAM_DCR_CHIP_DENSITY_4096M; - else if (para->density == 8192) - density = DRAM_DCR_CHIP_DENSITY_8192M; - else - density = DRAM_DCR_CHIP_DENSITY_256M; - - reg_val |= DRAM_DCR_CHIP_DENSITY(density); - reg_val |= DRAM_DCR_BUS_WIDTH((para->bus_width >> 3) - 1); - reg_val |= DRAM_DCR_RANK_SEL(para->rank_num - 1); - reg_val |= DRAM_DCR_CMD_RANK_ALL; - reg_val |= DRAM_DCR_MODE(DRAM_DCR_MODE_INTERLEAVE); - writel(reg_val, &dram->dcr); - - dramc_clock_output_en(1); - - mctl_set_impedance(para->zq, para->odt_en); - - mctl_set_cke_delay(); - - mctl_ddr3_reset(); - - udelay(1); - - await_bits_clear(&dram->ccr, DRAM_CCR_INIT); - - mctl_enable_dllx(para->tpr3); - - /* set refresh period */ - dramc_set_autorefresh_cycle(para->clock, density); - - /* set timing parameters */ - writel(para->tpr0, &dram->tpr0); - writel(para->tpr1, &dram->tpr1); - writel(para->tpr2, &dram->tpr2); - - reg_val = DRAM_MR_BURST_LENGTH(0x0); -#if (defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I)) - reg_val |= DRAM_MR_POWER_DOWN; -#endif - reg_val |= DRAM_MR_CAS_LAT(para->cas - 4); - reg_val |= DRAM_MR_WRITE_RECOVERY(ddr3_write_recovery(para->clock)); - writel(reg_val, &dram->mr); - - writel(para->emr1, &dram->emr); - writel(para->emr2, &dram->emr2); - writel(para->emr3, &dram->emr3); - - /* disable drift compensation and set passive DQS window mode */ - clrsetbits_le32(&dram->ccr, DRAM_CCR_DQS_DRIFT_COMP, DRAM_CCR_DQS_GATE); - -#ifdef CONFIG_MACH_SUN7I - /* Command rate timing mode 2T & 1T */ - if (para->tpr4 & 0x1) - setbits_le32(&dram->ccr, DRAM_CCR_COMMAND_RATE_1T); -#endif - /* initialize external DRAM */ - mctl_ddr3_initialize(); - - /* scan read pipe value */ - mctl_itm_enable(); - - /* Hardware DQS gate training */ - ret_val = dramc_scan_readpipe(); - - if (ret_val < 0) - return 0; - - /* allow to override the DQS training results with a custom delay */ - if (para->dqs_gating_delay) - mctl_set_dqs_gating_delay(0, para->dqs_gating_delay); - - /* set the DQS gating window type */ - if (para->active_windowing) - clrbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); - else - setbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); - - mctl_itm_reset(); - - /* configure all host port */ - mctl_configure_hostport(); - - return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE); -} - -unsigned long dramc_init(struct dram_para *para) -{ - unsigned long dram_size, actual_density; - - /* If the dram configuration is not provided, use a default */ - if (!para) - return 0; - - /* if everything is known, then autodetection is not necessary */ - if (para->io_width && para->bus_width && para->density) - return dramc_init_helper(para); - - /* try to autodetect the DRAM bus width and density */ - para->io_width = 16; - para->bus_width = 32; -#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN5I) - /* only A0-A14 address lines on A10/A13, limiting max density to 4096 */ - para->density = 4096; -#else - /* all A0-A15 address lines on A20, which allow density 8192 */ - para->density = 8192; -#endif - - dram_size = dramc_init_helper(para); - if (!dram_size) { - /* if 32-bit bus width failed, try 16-bit bus width instead */ - para->bus_width = 16; - dram_size = dramc_init_helper(para); - if (!dram_size) { - /* if 16-bit bus width also failed, then bail out */ - return dram_size; - } - } - - /* check if we need to adjust the density */ - actual_density = (dram_size >> 17) * para->io_width / para->bus_width; - - if (actual_density != para->density) { - /* update the density and re-initialize DRAM again */ - para->density = actual_density; - dram_size = dramc_init_helper(para); - } - - return dram_size; -} diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun4i.c b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c new file mode 100644 index 0000000..dc9fdb9 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c @@ -0,0 +1,750 @@ +/* + * sunxi DRAM controller initialization + * (C) Copyright 2012 Henrik Nordstrom henrik@henriknordstrom.net + * (C) Copyright 2013 Luke Kenneth Casson Leighton lkcl@lkcl.net + * + * Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c + * and earlier U-Boot Allwiner A10 SPL work + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Unfortunately the only documentation we have on the sun7i DRAM + * controller is Allwinner boot0 + boot1 code, and that code uses + * magic numbers & shifts with no explanations. Hence this code is + * rather undocumented and full of magic. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/timer.h> +#include <asm/arch/sys_proto.h> + +#define CPU_CFG_CHIP_VER(n) ((n) << 6) +#define CPU_CFG_CHIP_VER_MASK CPU_CFG_CHIP_VER(0x3) +#define CPU_CFG_CHIP_REV_A 0x0 +#define CPU_CFG_CHIP_REV_C1 0x1 +#define CPU_CFG_CHIP_REV_C2 0x2 +#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); +} + +/* + * Wait up to 1s for mask to be set in given reg. + */ +static inline void await_bits_set(u32 *reg, u32 mask) +{ + await_completion(reg, mask, mask); +} + +/* + * This performs the external DRAM reset by driving the RESET pin low and + * then high again. According to the DDR3 spec, the RESET pin needs to be + * kept low for at least 200 us. + */ +static void mctl_ddr3_reset(void) +{ + struct sunxi_dram_reg *dram = + (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + +#ifdef CONFIG_MACH_SUN4I + struct sunxi_timer_reg *timer = + (struct sunxi_timer_reg *)SUNXI_TIMER_BASE; + u32 reg_val; + + writel(0, &timer->cpu_cfg); + reg_val = readl(&timer->cpu_cfg); + + if ((reg_val & CPU_CFG_CHIP_VER_MASK) != + CPU_CFG_CHIP_VER(CPU_CFG_CHIP_REV_A)) { + setbits_le32(&dram->mcr, DRAM_MCR_RESET); + udelay(200); + clrbits_le32(&dram->mcr, DRAM_MCR_RESET); + } else +#endif + { + clrbits_le32(&dram->mcr, DRAM_MCR_RESET); + udelay(200); + setbits_le32(&dram->mcr, DRAM_MCR_RESET); + } + /* After the RESET pin is de-asserted, the DDR3 spec requires to wait + * for additional 500 us before driving the CKE pin (Clock Enable) + * high. The duration of this delay can be configured in the SDR_IDCR + * (Initialization Delay Configuration Register) and applied + * automatically by the DRAM controller during the DDR3 initialization + * step. But SDR_IDCR has limited range on sun4i/sun5i hardware and + * can't provide sufficient delay at DRAM clock frequencies higher than + * 524 MHz (while Allwinner A13 supports DRAM clock frequency up to + * 533 MHz according to the datasheet). Additionally, there is no + * official documentation for the SDR_IDCR register anywhere, and + * there is always a chance that we are interpreting it wrong. + * Better be safe than sorry, so add an explicit delay here. */ + udelay(500); +} + +static void mctl_set_drive(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + +#ifdef CONFIG_MACH_SUN7I + clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3) | (0x3 << 28), +#else + clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3), +#endif + DRAM_MCR_MODE_EN(0x3) | + 0xffc); +} + +static void mctl_itm_disable(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrsetbits_le32(&dram->ccr, DRAM_CCR_INIT, DRAM_CCR_ITM_OFF); +} + +static void mctl_itm_enable(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF); +} + +static void mctl_itm_reset(void) +{ + mctl_itm_disable(); + udelay(1); /* ITM reset needs a bit of delay */ + mctl_itm_enable(); + udelay(1); +} + +static void mctl_enable_dll0(u32 phase) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, + ((phase >> 16) & 0x3f) << 6); + clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET, DRAM_DLLCR_DISABLE); + udelay(2); + + clrbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET | DRAM_DLLCR_DISABLE); + udelay(22); + + clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_DISABLE, DRAM_DLLCR_NRESET); + udelay(22); +} + +/* Get the number of DDR byte lanes */ +static u32 mctl_get_number_of_lanes(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + if ((readl(&dram->dcr) & DRAM_DCR_BUS_WIDTH_MASK) == + DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT)) + return 4; + else + return 2; +} + +/* + * Note: This differs from pm/standby in that it checks the bus width + */ +static void mctl_enable_dllx(u32 phase) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 i, number_of_lanes; + + number_of_lanes = mctl_get_number_of_lanes(); + + for (i = 1; i <= number_of_lanes; i++) { + clrsetbits_le32(&dram->dllcr[i], 0xf << 14, + (phase & 0xf) << 14); + clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET, + DRAM_DLLCR_DISABLE); + phase >>= 4; + } + udelay(2); + + for (i = 1; i <= number_of_lanes; i++) + clrbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET | + DRAM_DLLCR_DISABLE); + udelay(22); + + for (i = 1; i <= number_of_lanes; i++) + clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_DISABLE, + DRAM_DLLCR_NRESET); + udelay(22); +} + +static u32 hpcr_value[32] = { +#ifdef CONFIG_MACH_SUN5I + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x1035, + 0x1035, 0x0731, 0x1031, 0, + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0x0301, 0 +#endif +#ifdef CONFIG_MACH_SUN4I + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x5031, + 0x1035, 0x0731, 0x1031, 0x0735, + 0x1035, 0x1031, 0x0731, 0x1035, + 0x1031, 0x0301, 0x0301, 0x0731 +#endif +#ifdef CONFIG_MACH_SUN7I + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0x0301, 0x0301, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x1035, + 0x1035, 0x0731, 0x1031, 0x0735, + 0x1035, 0x1031, 0x0731, 0x1035, + 0x0001, 0x1031, 0, 0x1031 + /* last row differs from boot0 source table + * 0x1031, 0x0301, 0x0301, 0x0731 + * but boot0 code skips #28 and #30, and sets #29 and #31 to the + * value from #28 entry (0x1031) + */ +#endif +}; + +static void mctl_configure_hostport(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 i; + + for (i = 0; i < 32; i++) + writel(hpcr_value[i], &dram->hpcr[i]); +} + +static void mctl_setup_dram_clock(u32 clk, u32 mbus_clk) +{ + u32 reg_val; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + u32 pll5p_clk, pll6x_clk; + u32 pll5p_div, pll6x_div; + u32 pll5p_rate, pll6x_rate; + + /* setup DRAM PLL */ + reg_val = readl(&ccm->pll5_cfg); + reg_val &= ~CCM_PLL5_CTRL_M_MASK; /* set M to 0 (x1) */ + reg_val &= ~CCM_PLL5_CTRL_K_MASK; /* set K to 0 (x1) */ + reg_val &= ~CCM_PLL5_CTRL_N_MASK; /* set N to 0 (x0) */ + reg_val &= ~CCM_PLL5_CTRL_P_MASK; /* set P to 0 (x1) */ +#ifdef CONFIG_OLD_SUNXI_KERNEL_COMPAT + /* Old kernels are hardcoded to P=1 (divide by 2) */ + reg_val |= CCM_PLL5_CTRL_P(1); +#endif + if (clk >= 540 && clk < 552) { + /* dram = 540MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(15)); + } else if (clk >= 512 && clk < 528) { + /* dram = 512MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(4)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(16)); + } else if (clk >= 496 && clk < 504) { + /* dram = 496MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(31)); + } else if (clk >= 468 && clk < 480) { + /* dram = 468MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(13)); + } else if (clk >= 396 && clk < 408) { + /* dram = 396MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(11)); + } else { + /* any other frequency that is a multiple of 24 */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(clk / 24)); + } + reg_val &= ~CCM_PLL5_CTRL_VCO_GAIN; /* PLL VCO Gain off */ + reg_val |= CCM_PLL5_CTRL_EN; /* PLL On */ + writel(reg_val, &ccm->pll5_cfg); + udelay(5500); + + setbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_DDR_CLK); + +#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I) + /* reset GPS */ + clrbits_le32(&ccm->gps_clk_cfg, CCM_GPS_CTRL_RESET | CCM_GPS_CTRL_GATE); + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS); + udelay(1); + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS); +#endif + + /* setup MBUS clock */ + if (!mbus_clk) + mbus_clk = 300; + + /* PLL5P and PLL6 are the potential clock sources for MBUS */ + pll6x_clk = clock_get_pll6() / 1000000; +#ifdef CONFIG_MACH_SUN7I + pll6x_clk *= 2; /* sun7i uses PLL6*2, sun5i uses just PLL6 */ +#endif + pll5p_clk = clock_get_pll5p() / 1000000; + pll6x_div = DIV_ROUND_UP(pll6x_clk, mbus_clk); + pll5p_div = DIV_ROUND_UP(pll5p_clk, mbus_clk); + pll6x_rate = pll6x_clk / pll6x_div; + pll5p_rate = pll5p_clk / pll5p_div; + + if (pll6x_div <= 16 && pll6x_rate > pll5p_rate) { + /* use PLL6 as the MBUS clock source */ + reg_val = CCM_MBUS_CTRL_GATE | + CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL6) | + CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | + CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll6x_div)); + } else if (pll5p_div <= 16) { + /* use PLL5P as the MBUS clock source */ + reg_val = CCM_MBUS_CTRL_GATE | + CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL5) | + CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | + CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll5p_div)); + } else { + panic("Bad mbus_clk\n"); + } + writel(reg_val, &ccm->mbus_clk_cfg); + + /* + * open DRAMC AHB & DLL register clock + * close it first + */ +#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); +#else + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM); +#endif + udelay(22); + + /* then open it */ +#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); +#else + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM); +#endif + udelay(22); +} + +/* + * The data from rslrX and rdgrX registers (X=rank) is stored + * in a single 32-bit value using the following format: + * bits [31:26] - DQS gating system latency for byte lane 3 + * bits [25:24] - DQS gating phase select for byte lane 3 + * bits [23:18] - DQS gating system latency for byte lane 2 + * bits [17:16] - DQS gating phase select for byte lane 2 + * bits [15:10] - DQS gating system latency for byte lane 1 + * bits [ 9:8 ] - DQS gating phase select for byte lane 1 + * bits [ 7:2 ] - DQS gating system latency for byte lane 0 + * bits [ 1:0 ] - DQS gating phase select for byte lane 0 + */ +static void mctl_set_dqs_gating_delay(int rank, u32 dqs_gating_delay) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 lane, number_of_lanes = mctl_get_number_of_lanes(); + /* rank0 gating system latency (3 bits per lane: cycles) */ + u32 slr = readl(rank == 0 ? &dram->rslr0 : &dram->rslr1); + /* rank0 gating phase select (2 bits per lane: 90, 180, 270, 360) */ + u32 dgr = readl(rank == 0 ? &dram->rdgr0 : &dram->rdgr1); + for (lane = 0; lane < number_of_lanes; lane++) { + u32 tmp = dqs_gating_delay >> (lane * 8); + slr &= ~(7 << (lane * 3)); + slr |= ((tmp >> 2) & 7) << (lane * 3); + dgr &= ~(3 << (lane * 2)); + dgr |= (tmp & 3) << (lane * 2); + } + writel(slr, rank == 0 ? &dram->rslr0 : &dram->rslr1); + writel(dgr, rank == 0 ? &dram->rdgr0 : &dram->rdgr1); +} + +static int dramc_scan_readpipe(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + + /* data training trigger */ + clrbits_le32(&dram->csr, DRAM_CSR_FAILED); + setbits_le32(&dram->ccr, DRAM_CCR_DATA_TRAINING); + + /* check whether data training process has completed */ + await_bits_clear(&dram->ccr, DRAM_CCR_DATA_TRAINING); + + /* check data training result */ + reg_val = readl(&dram->csr); + if (reg_val & DRAM_CSR_FAILED) + return -1; + + return 0; +} + +static void dramc_clock_output_en(u32 on) +{ +#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + if (on) + setbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT); + else + clrbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT); +#endif +#ifdef CONFIG_MACH_SUN4I + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + if (on) + setbits_le32(&ccm->dram_clk_cfg, CCM_DRAM_CTRL_DCLK_OUT); + else + clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAM_CTRL_DCLK_OUT); +#endif +} + +/* tRFC in nanoseconds for different densities (from the DDR3 spec) */ +static const u16 tRFC_DDR3_table[6] = { + /* 256Mb 512Mb 1Gb 2Gb 4Gb 8Gb */ + 90, 90, 110, 160, 300, 350 +}; + +static void dramc_set_autorefresh_cycle(u32 clk, u32 density) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 tRFC, tREFI; + + tRFC = (tRFC_DDR3_table[density] * clk + 999) / 1000; + tREFI = (7987 * clk) >> 10; /* <= 7.8us */ + + writel(DRAM_DRR_TREFI(tREFI) | DRAM_DRR_TRFC(tRFC), &dram->drr); +} + +/* Calculate the value for A11, A10, A9 bits in MR0 (write recovery) */ +static u32 ddr3_write_recovery(u32 clk) +{ + u32 twr_ns = 15; /* DDR3 spec says that it is 15ns for all speed bins */ + u32 twr_ck = (twr_ns * clk + 999) / 1000; + if (twr_ck < 5) + return 1; + else if (twr_ck <= 8) + return twr_ck - 4; + else if (twr_ck <= 10) + return 5; + else + return 6; +} + +/* + * If the dram->ppwrsctl (SDR_DPCR) register has the lowest bit set to 1, this + * means that DRAM is currently in self-refresh mode and retaining the old + * data. Since we have no idea what to do in this situation yet, just set this + * register to 0 and initialize DRAM in the same way as on any normal reboot + * (discarding whatever was stored there). + * + * Note: on sun7i hardware, the highest 16 bits need to be set to 0x1651 magic + * value for this write operation to have any effect. On sun5i hadware this + * magic value is not necessary. And on sun4i hardware the writes to this + * register seem to have no effect at all. + */ +static void mctl_disable_power_save(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + writel(0x16510000, &dram->ppwrsctl); +} + +/* + * After the DRAM is powered up or reset, the DDR3 spec requires to wait at + * least 500 us before driving the CKE pin (Clock Enable) high. The dram->idct + * (SDR_IDCR) register appears to configure this delay, which gets applied + * right at the time when the DRAM initialization is activated in the + * 'mctl_ddr3_initialize' function. + */ +static void mctl_set_cke_delay(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + /* The CKE delay is represented in DRAM clock cycles, multiplied by N + * (where N=2 for sun4i/sun5i and N=3 for sun7i). Here it is set to + * the maximum possible value 0x1ffff, just like in the Allwinner's + * boot0 bootloader. The resulting delay value is somewhere between + * ~0.4 ms (sun5i with 648 MHz DRAM clock speed) and ~1.1 ms (sun7i + * with 360 MHz DRAM clock speed). */ + setbits_le32(&dram->idcr, 0x1ffff); +} + +/* + * This triggers the DRAM initialization. It performs sending the mode registers + * to the DRAM among other things. Very likely the ZQCL command is also getting + * executed (to do the initial impedance calibration on the DRAM side of the + * wire). The memory controller and the PHY must be already configured before + * calling this function. + */ +static void mctl_ddr3_initialize(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + setbits_le32(&dram->ccr, DRAM_CCR_INIT); + await_bits_clear(&dram->ccr, DRAM_CCR_INIT); +} + +/* + * Perform impedance calibration on the DRAM controller side of the wire. + */ +static void mctl_set_impedance(u32 zq, u32 odt_en) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + u32 zprog = zq & 0xFF, zdata = (zq >> 8) & 0xFFFFF; + +#ifndef CONFIG_MACH_SUN7I + /* Appears that some kind of automatically initiated default + * ZQ calibration is already in progress at this point on sun4i/sun5i + * hardware, but not on sun7i. So it is reasonable to wait for its + * completion before doing anything else. */ + await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); +#endif + + /* ZQ calibration is not really useful unless ODT is enabled */ + if (!odt_en) + return; + +#ifdef CONFIG_MACH_SUN7I + /* Enabling ODT in SDR_IOCR on sun7i hardware results in a deadlock + * unless bit 24 is set in SDR_ZQCR1. Not much is known about the + * SDR_ZQCR1 register, but there are hints indicating that it might + * be related to periodic impedance re-calibration. This particular + * magic value is borrowed from the Allwinner boot0 bootloader, and + * using it helps to avoid troubles */ + writel((1 << 24) | (1 << 1), &dram->zqcr1); +#endif + + /* Needed at least for sun5i, because it does not self clear there */ + clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); + + if (zdata) { + /* Set the user supplied impedance data */ + reg_val = DRAM_ZQCR0_ZDEN | zdata; + writel(reg_val, &dram->zqcr0); + /* no need to wait, this takes effect immediately */ + } else { + /* Do the calibration using the external resistor */ + reg_val = DRAM_ZQCR0_ZCAL | DRAM_ZQCR0_IMP_DIV(zprog); + writel(reg_val, &dram->zqcr0); + /* Wait for the new impedance configuration to settle */ + await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); + } + + /* Needed at least for sun5i, because it does not self clear there */ + clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); + + /* Set I/O configure register */ + writel(DRAM_IOCR_ODT_EN(odt_en), &dram->iocr); +} + +static unsigned long dramc_init_helper(struct dram_para *para) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + u32 density; + int ret_val; + + /* + * only single rank DDR3 is supported by this code even though the + * hardware can theoretically support DDR2 and up to two ranks + */ + if (para->type != DRAM_MEMORY_TYPE_DDR3 || para->rank_num != 1) + return 0; + + /* setup DRAM relative clock */ + mctl_setup_dram_clock(para->clock, para->mbus_clock); + + /* Disable any pad power save control */ + mctl_disable_power_save(); + + mctl_set_drive(); + + /* dram clock off */ + dramc_clock_output_en(0); + +#ifdef CONFIG_MACH_SUN4I + /* select dram controller 1 */ + writel(DRAM_CSEL_MAGIC, &dram->csel); +#endif + + mctl_itm_disable(); + mctl_enable_dll0(para->tpr3); + + /* configure external DRAM */ + reg_val = DRAM_DCR_TYPE_DDR3; + reg_val |= DRAM_DCR_IO_WIDTH(para->io_width >> 3); + + if (para->density == 256) + density = DRAM_DCR_CHIP_DENSITY_256M; + else if (para->density == 512) + density = DRAM_DCR_CHIP_DENSITY_512M; + else if (para->density == 1024) + density = DRAM_DCR_CHIP_DENSITY_1024M; + else if (para->density == 2048) + density = DRAM_DCR_CHIP_DENSITY_2048M; + else if (para->density == 4096) + density = DRAM_DCR_CHIP_DENSITY_4096M; + else if (para->density == 8192) + density = DRAM_DCR_CHIP_DENSITY_8192M; + else + density = DRAM_DCR_CHIP_DENSITY_256M; + + reg_val |= DRAM_DCR_CHIP_DENSITY(density); + reg_val |= DRAM_DCR_BUS_WIDTH((para->bus_width >> 3) - 1); + reg_val |= DRAM_DCR_RANK_SEL(para->rank_num - 1); + reg_val |= DRAM_DCR_CMD_RANK_ALL; + reg_val |= DRAM_DCR_MODE(DRAM_DCR_MODE_INTERLEAVE); + writel(reg_val, &dram->dcr); + + dramc_clock_output_en(1); + + mctl_set_impedance(para->zq, para->odt_en); + + mctl_set_cke_delay(); + + mctl_ddr3_reset(); + + udelay(1); + + await_bits_clear(&dram->ccr, DRAM_CCR_INIT); + + mctl_enable_dllx(para->tpr3); + + /* set refresh period */ + dramc_set_autorefresh_cycle(para->clock, density); + + /* set timing parameters */ + writel(para->tpr0, &dram->tpr0); + writel(para->tpr1, &dram->tpr1); + writel(para->tpr2, &dram->tpr2); + + reg_val = DRAM_MR_BURST_LENGTH(0x0); +#if (defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I)) + reg_val |= DRAM_MR_POWER_DOWN; +#endif + reg_val |= DRAM_MR_CAS_LAT(para->cas - 4); + reg_val |= DRAM_MR_WRITE_RECOVERY(ddr3_write_recovery(para->clock)); + writel(reg_val, &dram->mr); + + writel(para->emr1, &dram->emr); + writel(para->emr2, &dram->emr2); + writel(para->emr3, &dram->emr3); + + /* disable drift compensation and set passive DQS window mode */ + clrsetbits_le32(&dram->ccr, DRAM_CCR_DQS_DRIFT_COMP, DRAM_CCR_DQS_GATE); + +#ifdef CONFIG_MACH_SUN7I + /* Command rate timing mode 2T & 1T */ + if (para->tpr4 & 0x1) + setbits_le32(&dram->ccr, DRAM_CCR_COMMAND_RATE_1T); +#endif + /* initialize external DRAM */ + mctl_ddr3_initialize(); + + /* scan read pipe value */ + mctl_itm_enable(); + + /* Hardware DQS gate training */ + ret_val = dramc_scan_readpipe(); + + if (ret_val < 0) + return 0; + + /* allow to override the DQS training results with a custom delay */ + if (para->dqs_gating_delay) + mctl_set_dqs_gating_delay(0, para->dqs_gating_delay); + + /* set the DQS gating window type */ + if (para->active_windowing) + clrbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); + else + setbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); + + mctl_itm_reset(); + + /* configure all host port */ + mctl_configure_hostport(); + + return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE); +} + +unsigned long dramc_init(struct dram_para *para) +{ + unsigned long dram_size, actual_density; + + /* If the dram configuration is not provided, use a default */ + if (!para) + return 0; + + /* if everything is known, then autodetection is not necessary */ + if (para->io_width && para->bus_width && para->density) + return dramc_init_helper(para); + + /* try to autodetect the DRAM bus width and density */ + para->io_width = 16; + para->bus_width = 32; +#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN5I) + /* only A0-A14 address lines on A10/A13, limiting max density to 4096 */ + para->density = 4096; +#else + /* all A0-A15 address lines on A20, which allow density 8192 */ + para->density = 8192; +#endif + + dram_size = dramc_init_helper(para); + if (!dram_size) { + /* if 32-bit bus width failed, try 16-bit bus width instead */ + para->bus_width = 16; + dram_size = dramc_init_helper(para); + if (!dram_size) { + /* if 16-bit bus width also failed, then bail out */ + return dram_size; + } + } + + /* check if we need to adjust the density */ + actual_density = (dram_size >> 17) * para->io_width / para->bus_width; + + if (actual_density != para->density) { + /* update the density and re-initialize DRAM again */ + para->density = actual_density; + dram_size = dramc_init_helper(para); + } + + return dram_size; +} diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 1945f75..c58971d 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -14,172 +14,9 @@
#include <linux/types.h>
-struct sunxi_dram_reg { - u32 ccr; /* 0x00 controller configuration register */ - u32 dcr; /* 0x04 dram configuration register */ - u32 iocr; /* 0x08 i/o configuration register */ - u32 csr; /* 0x0c controller status register */ - u32 drr; /* 0x10 dram refresh register */ - u32 tpr0; /* 0x14 dram timing parameters register 0 */ - u32 tpr1; /* 0x18 dram timing parameters register 1 */ - u32 tpr2; /* 0x1c dram timing parameters register 2 */ - u32 gdllcr; /* 0x20 global dll control register */ - u8 res0[0x28]; - u32 rslr0; /* 0x4c rank system latency register */ - u32 rslr1; /* 0x50 rank system latency register */ - u8 res1[0x8]; - u32 rdgr0; /* 0x5c rank dqs gating register */ - u32 rdgr1; /* 0x60 rank dqs gating register */ - u8 res2[0x34]; - u32 odtcr; /* 0x98 odt configuration register */ - u32 dtr0; /* 0x9c data training register 0 */ - u32 dtr1; /* 0xa0 data training register 1 */ - u32 dtar; /* 0xa4 data training address register */ - u32 zqcr0; /* 0xa8 zq control register 0 */ - u32 zqcr1; /* 0xac zq control register 1 */ - u32 zqsr; /* 0xb0 zq status register */ - u32 idcr; /* 0xb4 initializaton delay configure reg */ - u8 res3[0x138]; - u32 mr; /* 0x1f0 mode register */ - u32 emr; /* 0x1f4 extended mode register */ - u32 emr2; /* 0x1f8 extended mode register */ - u32 emr3; /* 0x1fc extended mode register */ - u32 dllctr; /* 0x200 dll control register */ - u32 dllcr[5]; /* 0x204 dll control register 0(byte 0) */ - /* 0x208 dll control register 1(byte 1) */ - /* 0x20c dll control register 2(byte 2) */ - /* 0x210 dll control register 3(byte 3) */ - /* 0x214 dll control register 4(byte 4) */ - u32 dqtr0; /* 0x218 dq timing register */ - u32 dqtr1; /* 0x21c dq timing register */ - u32 dqtr2; /* 0x220 dq timing register */ - u32 dqtr3; /* 0x224 dq timing register */ - u32 dqstr; /* 0x228 dqs timing register */ - u32 dqsbtr; /* 0x22c dqsb timing register */ - u32 mcr; /* 0x230 mode configure register */ - u8 res[0x8]; - u32 ppwrsctl; /* 0x23c pad power save control */ - u32 apr; /* 0x240 arbiter period register */ - u32 pldtr; /* 0x244 priority level data threshold reg */ - u8 res5[0x8]; - u32 hpcr[32]; /* 0x250 host port configure register */ - u8 res6[0x10]; - u32 csel; /* 0x2e0 controller select register */ -}; - -struct dram_para { - u32 clock; - u32 mbus_clock; - u32 type; - u32 rank_num; - u32 density; - u32 io_width; - u32 bus_width; - u32 cas; - u32 zq; - u32 odt_en; - u32 size; - u32 tpr0; - u32 tpr1; - u32 tpr2; - u32 tpr3; - u32 tpr4; - u32 tpr5; - u32 emr1; - u32 emr2; - u32 emr3; - u32 dqs_gating_delay; - u32 active_windowing; -}; - -#define DRAM_CCR_COMMAND_RATE_1T (0x1 << 5) -#define DRAM_CCR_DQS_GATE (0x1 << 14) -#define DRAM_CCR_DQS_DRIFT_COMP (0x1 << 17) -#define DRAM_CCR_ITM_OFF (0x1 << 28) -#define DRAM_CCR_DATA_TRAINING (0x1 << 30) -#define DRAM_CCR_INIT (0x1 << 31) - -#define DRAM_MEMORY_TYPE_DDR1 1 -#define DRAM_MEMORY_TYPE_DDR2 2 -#define DRAM_MEMORY_TYPE_DDR3 3 -#define DRAM_MEMORY_TYPE_LPDDR2 4 -#define DRAM_MEMORY_TYPE_LPDDR 5 -#define DRAM_DCR_TYPE (0x1 << 0) -#define DRAM_DCR_TYPE_DDR2 0x0 -#define DRAM_DCR_TYPE_DDR3 0x1 -#define DRAM_DCR_IO_WIDTH(n) (((n) & 0x3) << 1) -#define DRAM_DCR_IO_WIDTH_MASK DRAM_DCR_IO_WIDTH(0x3) -#define DRAM_DCR_IO_WIDTH_8BIT 0x0 -#define DRAM_DCR_IO_WIDTH_16BIT 0x1 -#define DRAM_DCR_CHIP_DENSITY(n) (((n) & 0x7) << 3) -#define DRAM_DCR_CHIP_DENSITY_MASK DRAM_DCR_CHIP_DENSITY(0x7) -#define DRAM_DCR_CHIP_DENSITY_256M 0x0 -#define DRAM_DCR_CHIP_DENSITY_512M 0x1 -#define DRAM_DCR_CHIP_DENSITY_1024M 0x2 -#define DRAM_DCR_CHIP_DENSITY_2048M 0x3 -#define DRAM_DCR_CHIP_DENSITY_4096M 0x4 -#define DRAM_DCR_CHIP_DENSITY_8192M 0x5 -#define DRAM_DCR_BUS_WIDTH(n) (((n) & 0x7) << 6) -#define DRAM_DCR_BUS_WIDTH_MASK DRAM_DCR_BUS_WIDTH(0x7) -#define DRAM_DCR_BUS_WIDTH_32BIT 0x3 -#define DRAM_DCR_BUS_WIDTH_16BIT 0x1 -#define DRAM_DCR_BUS_WIDTH_8BIT 0x0 -#define DRAM_DCR_RANK_SEL(n) (((n) & 0x3) << 10) -#define DRAM_DCR_RANK_SEL_MASK DRAM_DCR_CMD_RANK(0x3) -#define DRAM_DCR_CMD_RANK_ALL (0x1 << 12) -#define DRAM_DCR_MODE(n) (((n) & 0x3) << 13) -#define DRAM_DCR_MODE_MASK DRAM_DCR_MODE(0x3) -#define DRAM_DCR_MODE_SEQ 0x0 -#define DRAM_DCR_MODE_INTERLEAVE 0x1 - -#define DRAM_CSR_DTERR (0x1 << 20) -#define DRAM_CSR_DTIERR (0x1 << 21) -#define DRAM_CSR_FAILED (DRAM_CSR_DTERR | DRAM_CSR_DTIERR) - -#define DRAM_DRR_TRFC(n) ((n) & 0xff) -#define DRAM_DRR_TREFI(n) (((n) & 0xffff) << 8) -#define DRAM_DRR_BURST(n) ((((n) - 1) & 0xf) << 24) - -#define DRAM_MCR_MODE_NORM(n) (((n) & 0x3) << 0) -#define DRAM_MCR_MODE_NORM_MASK DRAM_MCR_MOD_NORM(0x3) -#define DRAM_MCR_MODE_DQ_OUT(n) (((n) & 0x3) << 2) -#define DRAM_MCR_MODE_DQ_OUT_MASK DRAM_MCR_MODE_DQ_OUT(0x3) -#define DRAM_MCR_MODE_ADDR_OUT(n) (((n) & 0x3) << 4) -#define DRAM_MCR_MODE_ADDR_OUT_MASK DRAM_MCR_MODE_ADDR_OUT(0x3) -#define DRAM_MCR_MODE_DQ_IN_OUT(n) (((n) & 0x3) << 6) -#define DRAM_MCR_MODE_DQ_IN_OUT_MASK DRAM_MCR_MODE_DQ_IN_OUT(0x3) -#define DRAM_MCR_MODE_DQ_TURNON_DELAY(n) (((n) & 0x7) << 8) -#define DRAM_MCR_MODE_DQ_TURNON_DELAY_MASK DRAM_MCR_MODE_DQ_TURNON_DELAY(0x7) -#define DRAM_MCR_MODE_ADDR_IN (0x1 << 11) -#define DRAM_MCR_RESET (0x1 << 12) -#define DRAM_MCR_MODE_EN(n) (((n) & 0x3) << 13) -#define DRAM_MCR_MODE_EN_MASK DRAM_MCR_MOD_EN(0x3) -#define DRAM_MCR_DCLK_OUT (0x1 << 16) - -#define DRAM_DLLCR_NRESET (0x1 << 30) -#define DRAM_DLLCR_DISABLE (0x1 << 31) - -#define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20) -#define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff) -#define DRAM_ZQCR0_ZCAL (1 << 31) /* Starts ZQ calibration when set to 1 */ -#define DRAM_ZQCR0_ZDEN (1 << 28) /* Uses ZDATA instead of doing calibration */ - -#define DRAM_ZQSR_ZDONE (1 << 31) /* ZQ calibration completion flag */ - -#define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0) -#define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3) - -#define DRAM_MR_BURST_LENGTH(n) (((n) & 0x7) << 0) -#define DRAM_MR_BURST_LENGTH_MASK DRAM_MR_BURST_LENGTH(0x7) -#define DRAM_MR_CAS_LAT(n) (((n) & 0x7) << 4) -#define DRAM_MR_CAS_LAT_MASK DRAM_MR_CAS_LAT(0x7) -#define DRAM_MR_WRITE_RECOVERY(n) (((n) & 0x7) << 9) -#define DRAM_MR_WRITE_RECOVERY_MASK DRAM_MR_WRITE_RECOVERY(0x7) -#define DRAM_MR_POWER_DOWN (0x1 << 12) - -#define DRAM_CSEL_MAGIC 0x16237495 +/* dram regs definition */ +#include <asm/arch/dram_sun4i.h>
unsigned long sunxi_dram_init(void); -unsigned long dramc_init(struct dram_para *para);
#endif /* _SUNXI_DRAM_H */ diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun4i.h b/arch/arm/include/asm/arch-sunxi/dram_sun4i.h new file mode 100644 index 0000000..6c1ec5b --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun4i.h @@ -0,0 +1,182 @@ +/* + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * + * Sunxi platform dram register definition. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_DRAM_SUN4I_H +#define _SUNXI_DRAM_SUN4I_H + +struct sunxi_dram_reg { + u32 ccr; /* 0x00 controller configuration register */ + u32 dcr; /* 0x04 dram configuration register */ + u32 iocr; /* 0x08 i/o configuration register */ + u32 csr; /* 0x0c controller status register */ + u32 drr; /* 0x10 dram refresh register */ + u32 tpr0; /* 0x14 dram timing parameters register 0 */ + u32 tpr1; /* 0x18 dram timing parameters register 1 */ + u32 tpr2; /* 0x1c dram timing parameters register 2 */ + u32 gdllcr; /* 0x20 global dll control register */ + u8 res0[0x28]; + u32 rslr0; /* 0x4c rank system latency register */ + u32 rslr1; /* 0x50 rank system latency register */ + u8 res1[0x8]; + u32 rdgr0; /* 0x5c rank dqs gating register */ + u32 rdgr1; /* 0x60 rank dqs gating register */ + u8 res2[0x34]; + u32 odtcr; /* 0x98 odt configuration register */ + u32 dtr0; /* 0x9c data training register 0 */ + u32 dtr1; /* 0xa0 data training register 1 */ + u32 dtar; /* 0xa4 data training address register */ + u32 zqcr0; /* 0xa8 zq control register 0 */ + u32 zqcr1; /* 0xac zq control register 1 */ + u32 zqsr; /* 0xb0 zq status register */ + u32 idcr; /* 0xb4 initializaton delay configure reg */ + u8 res3[0x138]; + u32 mr; /* 0x1f0 mode register */ + u32 emr; /* 0x1f4 extended mode register */ + u32 emr2; /* 0x1f8 extended mode register */ + u32 emr3; /* 0x1fc extended mode register */ + u32 dllctr; /* 0x200 dll control register */ + u32 dllcr[5]; /* 0x204 dll control register 0(byte 0) */ + /* 0x208 dll control register 1(byte 1) */ + /* 0x20c dll control register 2(byte 2) */ + /* 0x210 dll control register 3(byte 3) */ + /* 0x214 dll control register 4(byte 4) */ + u32 dqtr0; /* 0x218 dq timing register */ + u32 dqtr1; /* 0x21c dq timing register */ + u32 dqtr2; /* 0x220 dq timing register */ + u32 dqtr3; /* 0x224 dq timing register */ + u32 dqstr; /* 0x228 dqs timing register */ + u32 dqsbtr; /* 0x22c dqsb timing register */ + u32 mcr; /* 0x230 mode configure register */ + u8 res[0x8]; + u32 ppwrsctl; /* 0x23c pad power save control */ + u32 apr; /* 0x240 arbiter period register */ + u32 pldtr; /* 0x244 priority level data threshold reg */ + u8 res5[0x8]; + u32 hpcr[32]; /* 0x250 host port configure register */ + u8 res6[0x10]; + u32 csel; /* 0x2e0 controller select register */ +}; + +struct dram_para { + u32 clock; + u32 mbus_clock; + u32 type; + u32 rank_num; + u32 density; + u32 io_width; + u32 bus_width; + u32 cas; + u32 zq; + u32 odt_en; + u32 size; + u32 tpr0; + u32 tpr1; + u32 tpr2; + u32 tpr3; + u32 tpr4; + u32 tpr5; + u32 emr1; + u32 emr2; + u32 emr3; + u32 dqs_gating_delay; + u32 active_windowing; +}; + +#define DRAM_CCR_COMMAND_RATE_1T (0x1 << 5) +#define DRAM_CCR_DQS_GATE (0x1 << 14) +#define DRAM_CCR_DQS_DRIFT_COMP (0x1 << 17) +#define DRAM_CCR_ITM_OFF (0x1 << 28) +#define DRAM_CCR_DATA_TRAINING (0x1 << 30) +#define DRAM_CCR_INIT (0x1 << 31) + +#define DRAM_MEMORY_TYPE_DDR1 1 +#define DRAM_MEMORY_TYPE_DDR2 2 +#define DRAM_MEMORY_TYPE_DDR3 3 +#define DRAM_MEMORY_TYPE_LPDDR2 4 +#define DRAM_MEMORY_TYPE_LPDDR 5 +#define DRAM_DCR_TYPE (0x1 << 0) +#define DRAM_DCR_TYPE_DDR2 0x0 +#define DRAM_DCR_TYPE_DDR3 0x1 +#define DRAM_DCR_IO_WIDTH(n) (((n) & 0x3) << 1) +#define DRAM_DCR_IO_WIDTH_MASK DRAM_DCR_IO_WIDTH(0x3) +#define DRAM_DCR_IO_WIDTH_8BIT 0x0 +#define DRAM_DCR_IO_WIDTH_16BIT 0x1 +#define DRAM_DCR_CHIP_DENSITY(n) (((n) & 0x7) << 3) +#define DRAM_DCR_CHIP_DENSITY_MASK DRAM_DCR_CHIP_DENSITY(0x7) +#define DRAM_DCR_CHIP_DENSITY_256M 0x0 +#define DRAM_DCR_CHIP_DENSITY_512M 0x1 +#define DRAM_DCR_CHIP_DENSITY_1024M 0x2 +#define DRAM_DCR_CHIP_DENSITY_2048M 0x3 +#define DRAM_DCR_CHIP_DENSITY_4096M 0x4 +#define DRAM_DCR_CHIP_DENSITY_8192M 0x5 +#define DRAM_DCR_BUS_WIDTH(n) (((n) & 0x7) << 6) +#define DRAM_DCR_BUS_WIDTH_MASK DRAM_DCR_BUS_WIDTH(0x7) +#define DRAM_DCR_BUS_WIDTH_32BIT 0x3 +#define DRAM_DCR_BUS_WIDTH_16BIT 0x1 +#define DRAM_DCR_BUS_WIDTH_8BIT 0x0 +#define DRAM_DCR_RANK_SEL(n) (((n) & 0x3) << 10) +#define DRAM_DCR_RANK_SEL_MASK DRAM_DCR_CMD_RANK(0x3) +#define DRAM_DCR_CMD_RANK_ALL (0x1 << 12) +#define DRAM_DCR_MODE(n) (((n) & 0x3) << 13) +#define DRAM_DCR_MODE_MASK DRAM_DCR_MODE(0x3) +#define DRAM_DCR_MODE_SEQ 0x0 +#define DRAM_DCR_MODE_INTERLEAVE 0x1 + +#define DRAM_CSR_DTERR (0x1 << 20) +#define DRAM_CSR_DTIERR (0x1 << 21) +#define DRAM_CSR_FAILED (DRAM_CSR_DTERR | DRAM_CSR_DTIERR) + +#define DRAM_DRR_TRFC(n) ((n) & 0xff) +#define DRAM_DRR_TREFI(n) (((n) & 0xffff) << 8) +#define DRAM_DRR_BURST(n) ((((n) - 1) & 0xf) << 24) + +#define DRAM_MCR_MODE_NORM(n) (((n) & 0x3) << 0) +#define DRAM_MCR_MODE_NORM_MASK DRAM_MCR_MOD_NORM(0x3) +#define DRAM_MCR_MODE_DQ_OUT(n) (((n) & 0x3) << 2) +#define DRAM_MCR_MODE_DQ_OUT_MASK DRAM_MCR_MODE_DQ_OUT(0x3) +#define DRAM_MCR_MODE_ADDR_OUT(n) (((n) & 0x3) << 4) +#define DRAM_MCR_MODE_ADDR_OUT_MASK DRAM_MCR_MODE_ADDR_OUT(0x3) +#define DRAM_MCR_MODE_DQ_IN_OUT(n) (((n) & 0x3) << 6) +#define DRAM_MCR_MODE_DQ_IN_OUT_MASK DRAM_MCR_MODE_DQ_IN_OUT(0x3) +#define DRAM_MCR_MODE_DQ_TURNON_DELAY(n) (((n) & 0x7) << 8) +#define DRAM_MCR_MODE_DQ_TURNON_DELAY_MASK DRAM_MCR_MODE_DQ_TURNON_DELAY(0x7) +#define DRAM_MCR_MODE_ADDR_IN (0x1 << 11) +#define DRAM_MCR_RESET (0x1 << 12) +#define DRAM_MCR_MODE_EN(n) (((n) & 0x3) << 13) +#define DRAM_MCR_MODE_EN_MASK DRAM_MCR_MOD_EN(0x3) +#define DRAM_MCR_DCLK_OUT (0x1 << 16) + +#define DRAM_DLLCR_NRESET (0x1 << 30) +#define DRAM_DLLCR_DISABLE (0x1 << 31) + +#define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20) +#define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff) +#define DRAM_ZQCR0_ZCAL (1 << 31) /* Starts ZQ calibration when set to 1 */ +#define DRAM_ZQCR0_ZDEN (1 << 28) /* Uses ZDATA instead of doing calibration */ + +#define DRAM_ZQSR_ZDONE (1 << 31) /* ZQ calibration completion flag */ + +#define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0) +#define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3) + +#define DRAM_MR_BURST_LENGTH(n) (((n) & 0x7) << 0) +#define DRAM_MR_BURST_LENGTH_MASK DRAM_MR_BURST_LENGTH(0x7) +#define DRAM_MR_CAS_LAT(n) (((n) & 0x7) << 4) +#define DRAM_MR_CAS_LAT_MASK DRAM_MR_CAS_LAT(0x7) +#define DRAM_MR_WRITE_RECOVERY(n) (((n) & 0x7) << 9) +#define DRAM_MR_WRITE_RECOVERY_MASK DRAM_MR_WRITE_RECOVERY(0x7) +#define DRAM_MR_POWER_DOWN (0x1 << 12) + +#define DRAM_CSEL_MAGIC 0x16237495 + +unsigned long dramc_init(struct dram_para *para); + +#endif /* _SUNXI_DRAM_SUN4I_H */

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
In preparation for adding sun6i dram support.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

Add full support for dram initialization, using a fixed clock and autodetection of the memory organization (numbers of channels, bus-width, etc.).
This is based on dram_sun6i.c and dram.h from u-boot in the Allwinner A31 SDK, extended with extra initialization sequences and the autodetect algorithm from boot0.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 436 +++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/cpu.h | 8 +- arch/arm/include/asm/arch-sunxi/dram.h | 4 + arch/arm/include/asm/arch-sunxi/dram_sun6i.h | 359 ++++++++++++++++++++++ 5 files changed, 805 insertions(+), 3 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun6i.c create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun6i.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 48cca0b..3b6ae47 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -30,6 +30,7 @@ endif ifdef CONFIG_SPL_BUILD 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 ifdef CONFIG_SPL_FEL obj-y += start.o diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c new file mode 100644 index 0000000..d28a07d --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -0,0 +1,436 @@ +/* + * Sun6i platform dram controller init. + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * + * (C) Copyright 2014 Hans de Goede hdegoede@redhat.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#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> + +/* DRAM clk & zq defaults, maybe turn these into Kconfig options ? */ +#define DRAM_CLK_DEFAULT 312000000 +#define DRAM_ZQ_DEFAULT 0x78 + +struct dram_sun6i_para +{ + u8 bus_width; + u8 chan; + u8 rank; + u8 rows; + 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); + + 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); + + writel(MDFS_CLK_DEFAULT, &ccm->mdfs_clk_cfg); + + /* deassert mctl reset */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + + /* enable mctl clock */ + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); +} + +static void mctl_dll_init(int ch_index, struct dram_sun6i_para *para) +{ + struct sunxi_mctl_phy_reg *mctl_phy; + + if (ch_index == 0) + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + else + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE; + + /* disable + reset dlls */ + writel(MCTL_DLLCR_DISABLE, &mctl_phy->acdllcr); + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx0dllcr); + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx1dllcr); + if (para->bus_width == 32) { + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx2dllcr); + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx3dllcr); + } + udelay(2); + + /* enable + reset dlls */ + writel(0, &mctl_phy->acdllcr); + writel(0, &mctl_phy->dx0dllcr); + writel(0, &mctl_phy->dx1dllcr); + if (para->bus_width == 32) { + writel(0, &mctl_phy->dx2dllcr); + writel(0, &mctl_phy->dx3dllcr); + } + udelay(22); + + /* enable and release reset of dlls */ + writel(MCTL_DLLCR_NRESET, &mctl_phy->acdllcr); + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx0dllcr); + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx1dllcr); + if (para->bus_width == 32) { + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx2dllcr); + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx3dllcr); + } + udelay(22); +} + +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); + + return !(readl(gsr0) & err) && !(readl(gsr0 + 0x10) & err); +} + +static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg *mctl_ctl; + struct sunxi_mctl_phy_reg *mctl_phy; + + if (ch_index == 0) { + mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + } else { + mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL1_BASE; + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE; + } + + writel(MCTL_MCMD_NOP, &mctl_ctl->mcmd); + await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0); + + /* PHY initialization */ + writel(MCTL_PGCR, &mctl_phy->pgcr); + writel(MCTL_MR0, &mctl_phy->mr0); + writel(MCTL_MR1, &mctl_phy->mr1); + writel(MCTL_MR2, &mctl_phy->mr2); + writel(MCTL_MR3, &mctl_phy->mr3); + + writel((MCTL_TITMSRST << 18) | (MCTL_TDLLLOCK << 6) | MCTL_TDLLSRST, + &mctl_phy->ptr0); + /* Unknown magic performed by boot0 */ + if ((readl(SUNXI_RTC_BASE + 0x20c) & 3) == 2) + setbits_le32(&mctl_phy->ptr0, 1 << 18); + + writel((MCTL_TDINIT1 << 19) | MCTL_TDINIT0, &mctl_phy->ptr1); + writel((MCTL_TDINIT3 << 17) | MCTL_TDINIT2, &mctl_phy->ptr2); + + writel((MCTL_TCCD << 31) | (MCTL_TRC << 25) | (MCTL_TRRD << 21) | + (MCTL_TRAS << 16) | (MCTL_TRCD << 12) | (MCTL_TRP << 8) | + (MCTL_TWTR << 5) | (MCTL_TRTP << 2) | (MCTL_TMRD << 0), + &mctl_phy->dtpr0); + + writel((MCTL_TDQSCKMAX << 27) | (MCTL_TDQSCK << 24) | + (MCTL_TRFC << 16) | (MCTL_TRTODT << 11) | + ((MCTL_TMOD - 12) << 9) | (MCTL_TFAW << 3) | (0 << 2) | + (MCTL_TAOND << 0), &mctl_phy->dtpr1); + + writel((MCTL_TDLLK << 19) | (MCTL_TCKE << 15) | (MCTL_TXPDLL << 10) | + (MCTL_TEXSR << 0), &mctl_phy->dtpr2); + + writel(1, &mctl_ctl->dfitphyupdtype0); + writel(MCTL_DCR_DDR3, &mctl_phy->dcr); + writel(MCTL_DSGCR, &mctl_phy->dsgcr); + writel(MCTL_DXCCR, &mctl_phy->dxccr); + writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx0gcr); + writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx1gcr); + 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); + + writel(DRAM_ZQ_DEFAULT, &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); + + /* rank detect */ + if (!mctl_rank_detect(&mctl_phy->dx0gsr0, 1)) { + para->rank = 1; + clrbits_le32(&mctl_phy->pgcr, MCTL_PGCR_RANK); + } + + /* + * channel detect, check channel 1 dx0 and dx1 have rank 0, if not + * assume nothing is connected to channel 1. + */ + if (ch_index == 1 && !mctl_rank_detect(&mctl_phy->dx0gsr0, 0)) { + para->chan = 1; + clrbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN); + return; + } + + /* bus width detect, if dx2 and dx3 don't have rank 0, assume 16 bit */ + if (!mctl_rank_detect(&mctl_phy->dx2gsr0, 0)) { + para->bus_width = 16; + para->page_size = 2048; + setbits_le32(&mctl_phy->dx2dllcr, MCTL_DLLCR_DISABLE); + setbits_le32(&mctl_phy->dx3dllcr, MCTL_DLLCR_DISABLE); + clrbits_le32(&mctl_phy->dx2gcr, MCTL_DX_GCR_EN); + clrbits_le32(&mctl_phy->dx3gcr, MCTL_DX_GCR_EN); + } + + 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); + + 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); + + /* Set number of clks per micro-second */ + writel(DRAM_CLK_DEFAULT / 1000000, &mctl_ctl->togcnt1u); + /* Set number of clks per 100 nano-seconds */ + writel(DRAM_CLK_DEFAULT / 10000000, &mctl_ctl->togcnt100n); + /* Set memory timing registers */ + writel(MCTL_TREFI, &mctl_ctl->trefi); + writel(MCTL_TMRD, &mctl_ctl->tmrd); + writel(MCTL_TRFC, &mctl_ctl->trfc); + writel((MCTL_TPREA << 16) | MCTL_TRP, &mctl_ctl->trp); + writel(MCTL_TRTW, &mctl_ctl->trtw); + writel(MCTL_TAL, &mctl_ctl->tal); + writel(MCTL_TCL, &mctl_ctl->tcl); + writel(MCTL_TCWL, &mctl_ctl->tcwl); + writel(MCTL_TRAS, &mctl_ctl->tras); + writel(MCTL_TRC, &mctl_ctl->trc); + writel(MCTL_TRCD, &mctl_ctl->trcd); + writel(MCTL_TRRD, &mctl_ctl->trrd); + writel(MCTL_TRTP, &mctl_ctl->trtp); + writel(MCTL_TWR, &mctl_ctl->twr); + writel(MCTL_TWTR, &mctl_ctl->twtr); + writel(MCTL_TEXSR, &mctl_ctl->texsr); + writel(MCTL_TXP, &mctl_ctl->txp); + writel(MCTL_TXPDLL, &mctl_ctl->txpdll); + writel(MCTL_TZQCS, &mctl_ctl->tzqcs); + writel(MCTL_TZQCSI, &mctl_ctl->tzqcsi); + writel(MCTL_TDQS, &mctl_ctl->tdqs); + writel(MCTL_TCKSRE, &mctl_ctl->tcksre); + writel(MCTL_TCKSRX, &mctl_ctl->tcksrx); + writel(MCTL_TCKE, &mctl_ctl->tcke); + writel(MCTL_TMOD, &mctl_ctl->tmod); + writel(MCTL_TRSTL, &mctl_ctl->trstl); + writel(MCTL_TZQCL, &mctl_ctl->tzqcl); + writel(MCTL_TMRR, &mctl_ctl->tmrr); + writel(MCTL_TCKESR, &mctl_ctl->tckesr); + writel(MCTL_TDPD, &mctl_ctl->tdpd); + + /* Unknown magic performed by boot0 */ + setbits_le32(&mctl_ctl->dfiodtcfg, 1 << 3); + clrbits_le32(&mctl_ctl->dfiodtcfg1, 0x1f); + + /* Select 16/32-bits mode for MCTL */ + if (para->bus_width == 16) + setbits_le32(&mctl_ctl->ppcfg, 1); + + /* Set DFI timing registers */ + writel(MCTL_TCWL, &mctl_ctl->dfitphywrl); + writel(MCTL_TCL - 1, &mctl_ctl->dfitrdden); + writel(MCTL_DFITPHYRDL, &mctl_ctl->dfitphyrdl); + writel(MCTL_DFISTCFG0, &mctl_ctl->dfistcfg0); + + writel(MCTL_MCFG_DDR3, &mctl_ctl->mcfg); + + /* DFI update configuration register */ + writel(MCTL_DFIUPDCFG_UPD, &mctl_ctl->dfiupdcfg); + + /* Move to access state */ + writel(MCTL_SCTL_ACCESS, &mctl_ctl->sctl); + await_completion(&mctl_ctl->sstat, 0x07, 0x03); +} + +static void mctl_com_init(struct dram_sun6i_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy1 = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE; + struct sunxi_prcm_reg * const prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + + writel(MCTL_CR_UNKNOWN | MCTL_CR_CHANNEL(para->chan) | MCTL_CR_DDR3 | + ((para->bus_width == 32) ? MCTL_CR_BUSW32 : MCTL_CR_BUSW16) | + MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_ROW(para->rows) | + MCTL_CR_BANK(1) | MCTL_CR_RANK(para->rank), &mctl_com->cr); + + /* Unknown magic performed by boot0 */ + setbits_le32(&mctl_com->dbgcr, (1 << 6)); + + if (para->chan == 1) { + /* Shutdown channel 1 */ + setbits_le32(&mctl_phy1->aciocr, MCTL_ACIOCR_DISABLE); + setbits_le32(&mctl_phy1->dxccr, MCTL_DXCCR_DISABLE); + clrbits_le32(&mctl_phy1->dsgcr, MCTL_DSGCR_ENABLE); + /* + * CH0 ?? this is what boot0 does. Leave as is until we can + * confirm this. + */ + setbits_le32(&prcm->vdd_sys_pwroff, + PRCM_VDD_SYS_DRAM_CH0_PAD_HOLD_PWROFF); + } +} + +static void mctl_port_cfg(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* enable DRAM AXI clock for CPU access */ + setbits_le32(&ccm->axi_gate, 1 << AXI_GATE_OFFSET_DRAM); + + /* Bunch of magic writes performed by boot0 */ + writel(0x00400302, &mctl_com->rmcr[0]); + writel(0x01000307, &mctl_com->rmcr[1]); + writel(0x00400302, &mctl_com->rmcr[2]); + writel(0x01000307, &mctl_com->rmcr[3]); + writel(0x01000307, &mctl_com->rmcr[4]); + writel(0x01000303, &mctl_com->rmcr[6]); + writel(0x01000303, &mctl_com->mmcr[0]); + writel(0x00400310, &mctl_com->mmcr[1]); + writel(0x01000307, &mctl_com->mmcr[2]); + writel(0x01000303, &mctl_com->mmcr[3]); + writel(0x01800303, &mctl_com->mmcr[4]); + writel(0x01800303, &mctl_com->mmcr[5]); + writel(0x01800303, &mctl_com->mmcr[6]); + writel(0x01800303, &mctl_com->mmcr[7]); + writel(0x01000303, &mctl_com->mmcr[8]); + writel(0x00000002, &mctl_com->mmcr[15]); + writel(0x00000310, &mctl_com->mbagcr[0]); + writel(0x00400310, &mctl_com->mbagcr[1]); + writel(0x00400310, &mctl_com->mbagcr[2]); + writel(0x00000307, &mctl_com->mbagcr[3]); + writel(0x00000317, &mctl_com->mbagcr[4]); + 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 = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + u32 offset; + int bank, bus, columns; + + /* Set initial parameters, these get modified by the autodetect code */ + struct dram_sun6i_para para = { + .bus_width = 32, + .chan = 2, + .rank = 2, + .page_size = 4096, + .rows = 16, + }; + + mctl_sys_init(); + + mctl_dll_init(0, ¶); + mctl_dll_init(1, ¶); + + setbits_le32(&mctl_com->ccr, + MCTL_CCR_MASTER_CLK_EN | + MCTL_CCR_CH0_CLK_EN | + MCTL_CCR_CH1_CLK_EN); + + mctl_channel_init(0, ¶); + mctl_channel_init(1, ¶); + mctl_com_init(¶); + mctl_port_cfg(); + + /* + * Change to 1 ch / sequence / 8192 byte pages / 16 rows / + * 8 bit banks / 1 rank mode. + */ + clrsetbits_le32(&mctl_com->cr, + MCTL_CR_CHANNEL_MASK | MCTL_CR_PAGE_SIZE_MASK | + MCTL_CR_ROW_MASK | MCTL_CR_BANK_MASK | MCTL_CR_RANK_MASK, + MCTL_CR_CHANNEL(1) | MCTL_CR_SEQUENCE | + MCTL_CR_PAGE_SIZE(8192) | MCTL_CR_ROW(16) | + MCTL_CR_BANK(1) | MCTL_CR_RANK(1)); + + /* Detect and set page size */ + for (columns = 7; columns < 20; columns++) { + if (mctl_mem_matches(1 << columns)) + break; + } + bus = (para.bus_width == 32) ? 2 : 1; + columns -= bus; + para.page_size = (1 << columns) * (bus << 1); + clrsetbits_le32(&mctl_com->cr, MCTL_CR_PAGE_SIZE_MASK, + MCTL_CR_PAGE_SIZE(para.page_size)); + + /* Detect and set rows */ + for (para.rows = 11; para.rows < 16; para.rows++) { + offset = 1 << (para.rows + columns + bus); + if (mctl_mem_matches(offset)) + break; + } + clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK, + MCTL_CR_ROW(para.rows)); + + /* Detect bank size */ + offset = 1 << (para.rows + columns + bus + 2); + bank = mctl_mem_matches(offset) ? 0 : 1; + + /* Restore interleave, chan and rank values, set bank size */ + clrsetbits_le32(&mctl_com->cr, + MCTL_CR_CHANNEL_MASK | MCTL_CR_SEQUENCE | + MCTL_CR_BANK_MASK | MCTL_CR_RANK_MASK, + MCTL_CR_CHANNEL(para.chan) | MCTL_CR_BANK(bank) | + MCTL_CR_RANK(para.rank)); + + return 1 << (para.rank + para.rows + bank + columns + para.chan + bus); +} diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index d34690e..6550e50 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -96,9 +96,10 @@ #define SUNXI_GMAC_BASE 0x01c50000
#define SUNXI_DRAM_COM_BASE 0x01c62000 -#define SUNXI_DRAM_CTL_BASE 0x01c63000 -#define SUNXI_DRAM_PHY_CH1_BASE 0x01c65000 -#define SUNXI_DRAM_PHY_CH2_BASE 0x01c66000 +#define SUNXI_DRAM_CTL0_BASE 0x01c63000 +#define SUNXI_DRAM_CTL1_BASE 0x01c64000 +#define SUNXI_DRAM_PHY0_BASE 0x01c65000 +#define SUNXI_DRAM_PHY1_BASE 0x01c66000
/* module sram */ #define SUNXI_SRAM_C_BASE 0x01d00000 @@ -110,6 +111,7 @@ #define SUNXI_MP_BASE 0x01e80000 #define SUNXI_AVG_BASE 0x01ea0000
+#define SUNXI_RTC_BASE 0x01f00000 #define SUNXI_PRCM_BASE 0x01f01400 #define SUN6I_CPUCFG_BASE 0x01f01c00 #define SUNXI_R_UART_BASE 0x01f02800 diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index c58971d..9072e68 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -15,7 +15,11 @@ #include <linux/types.h>
/* dram regs definition */ +#if defined(CONFIG_MACH_SUN6I) +#include <asm/arch/dram_sun6i.h> +#else #include <asm/arch/dram_sun4i.h> +#endif
unsigned long sunxi_dram_init(void);
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun6i.h b/arch/arm/include/asm/arch-sunxi/dram_sun6i.h new file mode 100644 index 0000000..2024bbc --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun6i.h @@ -0,0 +1,359 @@ +/* + * Sun6i platform dram controller register and constant defines + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * + * (C) Copyright 2014 Hans de Goede hdegoede@redhat.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_DRAM_SUN6I_H +#define _SUNXI_DRAM_SUN6I_H + +struct sunxi_mctl_com_reg { + u32 cr; /* 0x00 */ + u32 ccr; /* 0x04 controller configuration register */ + u32 dbgcr; /* 0x08 */ + u32 dbgcr1; /* 0x0c */ + u32 rmcr[8]; /* 0x10 */ + u32 mmcr[16]; /* 0x30 */ + u32 mbagcr[6]; /* 0x70 */ + u32 maer; /* 0x88 */ + u8 res0[0x14]; /* 0x8c */ + u32 mdfscr; /* 0x100 */ + u32 mdfsmer; /* 0x104 */ + u32 mdfsmrmr; /* 0x108 */ + u32 mdfstr0; /* 0x10c */ + u32 mdfstr1; /* 0x110 */ + u32 mdfstr2; /* 0x114 */ + u32 mdfstr3; /* 0x118 */ + u32 mdfsgcr; /* 0x11c */ + u8 res1[0x1c]; /* 0x120 */ + u32 mdfsivr; /* 0x13c */ + u8 res2[0x0c]; /* 0x140 */ + u32 mdfstcr; /* 0x14c */ +}; + +struct sunxi_mctl_ctl_reg { + u8 res0[0x04]; /* 0x00 */ + u32 sctl; /* 0x04 */ + u32 sstat; /* 0x08 */ + u8 res1[0x34]; /* 0x0c */ + u32 mcmd; /* 0x40 */ + u8 res2[0x08]; /* 0x44 */ + u32 cmdstat; /* 0x4c */ + u32 cmdstaten; /* 0x50 */ + u8 res3[0x0c]; /* 0x54 */ + u32 mrrcfg0; /* 0x60 */ + u32 mrrstat0; /* 0x64 */ + u32 mrrstat1; /* 0x68 */ + u8 res4[0x10]; /* 0x6c */ + u32 mcfg1; /* 0x7c */ + u32 mcfg; /* 0x80 */ + u32 ppcfg; /* 0x84 */ + u32 mstat; /* 0x88 */ + u32 lp2zqcfg; /* 0x8c */ + u8 res5[0x04]; /* 0x90 */ + u32 dtustat; /* 0x94 */ + u32 dtuna; /* 0x98 */ + u32 dtune; /* 0x9c */ + u32 dtuprd0; /* 0xa0 */ + u32 dtuprd1; /* 0xa4 */ + u32 dtuprd2; /* 0xa8 */ + u32 dtuprd3; /* 0xac */ + u32 dtuawdt; /* 0xb0 */ + u8 res6[0x0c]; /* 0xb4 */ + u32 togcnt1u; /* 0xc0 */ + u8 res7[0x08]; /* 0xc4 */ + u32 togcnt100n; /* 0xcc */ + u32 trefi; /* 0xd0 */ + u32 tmrd; /* 0xd4 */ + u32 trfc; /* 0xd8 */ + u32 trp; /* 0xdc */ + u32 trtw; /* 0xe0 */ + u32 tal; /* 0xe4 */ + u32 tcl; /* 0xe8 */ + u32 tcwl; /* 0xec */ + u32 tras; /* 0xf0 */ + u32 trc; /* 0xf4 */ + u32 trcd; /* 0xf8 */ + u32 trrd; /* 0xfc */ + u32 trtp; /* 0x100 */ + u32 twr; /* 0x104 */ + u32 twtr; /* 0x108 */ + u32 texsr; /* 0x10c */ + u32 txp; /* 0x110 */ + u32 txpdll; /* 0x114 */ + u32 tzqcs; /* 0x118 */ + u32 tzqcsi; /* 0x11c */ + u32 tdqs; /* 0x120 */ + u32 tcksre; /* 0x124 */ + u32 tcksrx; /* 0x128 */ + u32 tcke; /* 0x12c */ + u32 tmod; /* 0x130 */ + u32 trstl; /* 0x134 */ + u32 tzqcl; /* 0x138 */ + u32 tmrr; /* 0x13c */ + u32 tckesr; /* 0x140 */ + u32 tdpd; /* 0x144 */ + u8 res8[0xb8]; /* 0x148 */ + u32 dtuwactl; /* 0x200 */ + u32 dturactl; /* 0x204 */ + u32 dtucfg; /* 0x208 */ + u32 dtuectl; /* 0x20c */ + u32 dtuwd0; /* 0x210 */ + u32 dtuwd1; /* 0x214 */ + u32 dtuwd2; /* 0x218 */ + u32 dtuwd3; /* 0x21c */ + u32 dtuwdm; /* 0x220 */ + u32 dturd0; /* 0x224 */ + u32 dturd1; /* 0x228 */ + u32 dturd2; /* 0x22c */ + u32 dturd3; /* 0x230 */ + u32 dtulfsrwd; /* 0x234 */ + u32 dtulfsrrd; /* 0x238 */ + u32 dtueaf; /* 0x23c */ + u32 dfitctldly; /* 0x240 */ + u32 dfiodtcfg; /* 0x244 */ + u32 dfiodtcfg1; /* 0x248 */ + u32 dfiodtrmap; /* 0x24c */ + u32 dfitphywrd; /* 0x250 */ + u32 dfitphywrl; /* 0x254 */ + u8 res9[0x08]; /* 0x258 */ + u32 dfitrdden; /* 0x260 */ + u32 dfitphyrdl; /* 0x264 */ + u8 res10[0x08]; /* 0x268 */ + u32 dfitphyupdtype0; /* 0x270 */ + u32 dfitphyupdtype1; /* 0x274 */ + u32 dfitphyupdtype2; /* 0x278 */ + u32 dfitphyupdtype3; /* 0x27c */ + u32 dfitctrlupdmin; /* 0x280 */ + u32 dfitctrlupdmax; /* 0x284 */ + u32 dfitctrlupddly; /* 0x288 */ + u8 res11[4]; /* 0x28c */ + u32 dfiupdcfg; /* 0x290 */ + u32 dfitrefmski; /* 0x294 */ + u32 dfitcrlupdi; /* 0x298 */ + u8 res12[0x10]; /* 0x29c */ + u32 dfitrcfg0; /* 0x2ac */ + u32 dfitrstat0; /* 0x2b0 */ + u32 dfitrwrlvlen; /* 0x2b4 */ + u32 dfitrrdlvlen; /* 0x2b8 */ + u32 dfitrrdlvlgateen; /* 0x2bc */ + u8 res13[0x04]; /* 0x2c0 */ + u32 dfistcfg0; /* 0x2c4 */ + u32 dfistcfg1; /* 0x2c8 */ + u8 res14[0x04]; /* 0x2cc */ + u32 dfitdramclken; /* 0x2d0 */ + u32 dfitdramclkdis; /* 0x2d4 */ + u8 res15[0x18]; /* 0x2d8 */ + u32 dfilpcfg0; /* 0x2f0 */ +}; + +struct sunxi_mctl_phy_reg { + u8 res0[0x04]; /* 0x00 */ + u32 pir; /* 0x04 */ + u32 pgcr; /* 0x08 phy general configuration register */ + u32 pgsr; /* 0x0c */ + u32 dllgcr; /* 0x10 */ + u32 acdllcr; /* 0x14 */ + u32 ptr0; /* 0x18 */ + u32 ptr1; /* 0x1c */ + u32 ptr2; /* 0x20 */ + u32 aciocr; /* 0x24 */ + u32 dxccr; /* 0x28 DATX8 common configuration register */ + u32 dsgcr; /* 0x2c dram system general config register */ + u32 dcr; /* 0x30 */ + u32 dtpr0; /* 0x34 dram timing parameters register 0 */ + u32 dtpr1; /* 0x38 dram timing parameters register 1 */ + u32 dtpr2; /* 0x3c dram timing parameters register 2 */ + u32 mr0; /* 0x40 mode register 0 */ + u32 mr1; /* 0x44 mode register 1 */ + u32 mr2; /* 0x48 mode register 2 */ + u32 mr3; /* 0x4c mode register 3 */ + u32 odtcr; /* 0x50 */ + u32 dtar; /* 0x54 data training address register */ + u32 dtd0; /* 0x58 */ + u32 dtd1; /* 0x5c */ + u8 res1[0x60]; /* 0x60 */ + u32 dcuar; /* 0xc0 */ + u32 dcudr; /* 0xc4 */ + u32 dcurr; /* 0xc8 */ + u32 dculr; /* 0xcc */ + u32 dcugcr; /* 0xd0 */ + u32 dcutpr; /* 0xd4 */ + u32 dcusr0; /* 0xd8 */ + u32 dcusr1; /* 0xdc */ + u8 res2[0x20]; /* 0xe0 */ + u32 bistrr; /* 0x100 */ + u32 bistmskr0; /* 0x104 */ + u32 bistmskr1; /* 0x108 */ + u32 bistwcr; /* 0x10c */ + u32 bistlsr; /* 0x110 */ + u32 bistar0; /* 0x114 */ + u32 bistar1; /* 0x118 */ + u32 bistar2; /* 0x11c */ + u32 bistupdr; /* 0x120 */ + u32 bistgsr; /* 0x124 */ + u32 bistwer; /* 0x128 */ + u32 bistber0; /* 0x12c */ + u32 bistber1; /* 0x130 */ + u32 bistber2; /* 0x134 */ + u32 bistwcsr; /* 0x138 */ + u32 bistfwr0; /* 0x13c */ + u32 bistfwr1; /* 0x140 */ + u8 res3[0x3c]; /* 0x144 */ + u32 zq0cr0; /* 0x180 zq 0 control register 0 */ + u32 zq0cr1; /* 0x184 zq 0 control register 1 */ + u32 zq0sr0; /* 0x188 zq 0 status register 0 */ + u32 zq0sr1; /* 0x18c zq 0 status register 1 */ + u8 res4[0x30]; /* 0x190 */ + u32 dx0gcr; /* 0x1c0 */ + u32 dx0gsr0; /* 0x1c4 */ + u32 dx0gsr1; /* 0x1c8 */ + u32 dx0dllcr; /* 0x1cc */ + u32 dx0dqtr; /* 0x1d0 */ + u32 dx0dqstr; /* 0x1d4 */ + u8 res5[0x28]; /* 0x1d8 */ + u32 dx1gcr; /* 0x200 */ + u32 dx1gsr0; /* 0x204 */ + u32 dx1gsr1; /* 0x208 */ + u32 dx1dllcr; /* 0x20c */ + u32 dx1dqtr; /* 0x210 */ + u32 dx1dqstr; /* 0x214 */ + u8 res6[0x28]; /* 0x218 */ + u32 dx2gcr; /* 0x240 */ + u32 dx2gsr0; /* 0x244 */ + u32 dx2gsr1; /* 0x248 */ + u32 dx2dllcr; /* 0x24c */ + u32 dx2dqtr; /* 0x250 */ + u32 dx2dqstr; /* 0x254 */ + u8 res7[0x28]; /* 0x258 */ + u32 dx3gcr; /* 0x280 */ + u32 dx3gsr0; /* 0x284 */ + u32 dx3gsr1; /* 0x288 */ + u32 dx3dllcr; /* 0x28c */ + u32 dx3dqtr; /* 0x290 */ + u32 dx3dqstr; /* 0x294 */ +}; + +/* + * DRAM common (sunxi_mctl_com_reg) register constants. + */ +#define MCTL_CR_RANK_MASK (3 << 0) +#define MCTL_CR_RANK(x) (((x) - 1) << 0) +#define MCTL_CR_BANK_MASK (3 << 2) +#define MCTL_CR_BANK(x) ((x) << 2) +#define MCTL_CR_ROW_MASK (0xf << 4) +#define MCTL_CR_ROW(x) (((x) - 1) << 4) +#define MCTL_CR_PAGE_SIZE_MASK (0xf << 8) +#define MCTL_CR_PAGE_SIZE(x) ((fls(x) - 4) << 8) +#define MCTL_CR_BUSW_MASK (3 << 12) +#define MCTL_CR_BUSW16 (1 << 12) +#define MCTL_CR_BUSW32 (3 << 12) +#define MCTL_CR_SEQUENCE (1 << 15) +#define MCTL_CR_DDR3 (3 << 16) +#define MCTL_CR_CHANNEL_MASK (1 << 19) +#define MCTL_CR_CHANNEL(x) (((x) - 1) << 19) +#define MCTL_CR_UNKNOWN ((1 << 22) | (1 << 20)) +#define MCTL_CCR_CH0_CLK_EN (1 << 0) +#define MCTL_CCR_CH1_CLK_EN (1 << 1) +#define MCTL_CCR_MASTER_CLK_EN (1 << 2) + +/* + * DRAM control (sunxi_mctl_ctl_reg) register constants. + * Note that we use constant values for a lot of the timings, this is what + * the original boot0 bootloader does. + */ +#define MCTL_SCTL_CONFIG 1 +#define MCTL_SCTL_ACCESS 2 +#define MCTL_MCMD_NOP 0x88000000 +#define MCTL_MCMD_BUSY 0x80000000 +#define MCTL_MCFG_DDR3 0x70061 +#define MCTL_TREFI 78 +#define MCTL_TMRD 4 +#define MCTL_TRFC 115 +#define MCTL_TRP 9 +#define MCTL_TPREA 0 +#define MCTL_TRTW 2 +#define MCTL_TAL 0 +#define MCTL_TCL 9 +#define MCTL_TCWL 8 +#define MCTL_TRAS 18 +#define MCTL_TRC 23 +#define MCTL_TRCD 9 +#define MCTL_TRRD 4 +#define MCTL_TRTP 4 +#define MCTL_TWR 8 +#define MCTL_TWTR 4 +#define MCTL_TEXSR 512 +#define MCTL_TXP 4 +#define MCTL_TXPDLL 14 +#define MCTL_TZQCS 64 +#define MCTL_TZQCSI 0 +#define MCTL_TDQS 1 +#define MCTL_TCKSRE 5 +#define MCTL_TCKSRX 5 +#define MCTL_TCKE 4 +#define MCTL_TMOD 12 +#define MCTL_TRSTL 80 +#define MCTL_TZQCL 512 +#define MCTL_TMRR 2 +#define MCTL_TCKESR 5 +#define MCTL_TDPD 0 +#define MCTL_DFITPHYRDL 15 +#define MCTL_DFIUPDCFG_UPD (1 << 1) +#define MCTL_DFISTCFG0 5 + +/* + * DRAM phy (sunxi_mctl_phy_reg) register values / constants. + */ +#define MCTL_PIR_CLEAR_STATUS (1 << 28) +#define MCTL_PIR_STEP1 0xe9 +#define MCTL_PIR_STEP2 0x81 +#define MCTL_PGCR_RANK (1 << 19) +#define MCTL_PGCR 0x018c0202 +#define MCTL_PGSR_TRAIN_ERR_MASK (3 << 5) +/* constants for both acdllcr as well as dx#dllcr */ +#define MCTL_DLLCR_NRESET (1 << 30) +#define MCTL_DLLCR_DISABLE (1 << 31) +/* ptr constants these are or-ed together to get the final ptr# values */ +#define MCTL_TITMSRST 10 +#define MCTL_TDLLLOCK 2250 +#define MCTL_TDLLSRST 23 +#define MCTL_TDINIT0 217000 +#define MCTL_TDINIT1 160 +#define MCTL_TDINIT2 87000 +#define MCTL_TDINIT3 433 +/* end ptr constants */ +#define MCTL_ACIOCR_DISABLE ((3 << 18) | (1 << 8) | (1 << 3)) +#define MCTL_DXCCR_DISABLE ((1 << 3) | (1 << 2)) +#define MCTL_DXCCR 0x800 +#define MCTL_DSGCR_ENABLE (1 << 28) +#define MCTL_DSGCR 0xf200001b +#define MCTL_DCR_DDR3 0x0b +/* dtpr constants these are or-ed together to get the final dtpr# values */ +#define MCTL_TCCD 0 +#define MCTL_TDQSCKMAX 1 +#define MCTL_TDQSCK 1 +#define MCTL_TRTODT 0 +#define MCTL_TFAW 20 +#define MCTL_TAOND 0 +#define MCTL_TDLLK 512 +/* end dtpr constants */ +#define MCTL_MR0 0x1a50 +#define MCTL_MR1 0x4 +#define MCTL_MR2 ((MCTL_TCWL - 5) << 3) +#define MCTL_MR3 0x0 +#define MCTL_DX_GCR_EN (1 << 0) +#define MCTL_DX_GCR 0x880 +#define MCTL_DX_GSR0_RANK0_TRAIN_DONE (1 << 0) +#define MCTL_DX_GSR0_RANK1_TRAIN_DONE (1 << 1) +#define MCTL_DX_GSR0_RANK0_TRAIN_ERR (1 << 4) +#define MCTL_DX_GSR0_RANK1_TRAIN_ERR (1 << 5) + +#endif /* _SUNXI_DRAM_SUN6I_H */

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Add full support for dram initialization, using a fixed clock and autodetection of the memory organization (numbers of channels, bus-width, etc.).
This is based on dram_sun6i.c and dram.h from u-boot in the Allwinner A31 SDK, extended with extra initialization sequences and the autodetect algorithm from boot0.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
- /* Bunch of magic writes performed by boot0 */
- writel(0x00400302, &mctl_com->rmcr[0]);
[...]
:-/ Have you pinged Allwinner about these/docs for these?
Ian.

Hi,
Thanks for all the reviews!
On 11/08/2014 11:25 AM, Ian Campbell wrote:
On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Add full support for dram initialization, using a fixed clock and autodetection of the memory organization (numbers of channels, bus-width, etc.).
This is based on dram_sun6i.c and dram.h from u-boot in the Allwinner A31 SDK, extended with extra initialization sequences and the autodetect algorithm from boot0.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
- /* Bunch of magic writes performed by boot0 */
- writel(0x00400302, &mctl_com->rmcr[0]);
[...]
:-/ Have you pinged Allwinner about these/docs for these?
No, and neither for the next patch. I'll drop them a mail. In this case in the mean time I think I know what this is, this is *probably* setting up access rights / priorities for all the DRAM masters.
Regards,
Hans

Without this the cache will only work in write-through mode, and as soon as it is put in write-back mode things break.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/board.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 6c812fc..9b3e80c 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -114,6 +114,11 @@ void reset_cpu(ulong addr) /* do some early init */ void s_init(void) { +#if defined CONFIG_SPL_BUILD && defined CONFIG_MACH_SUN6I + /* Magic (undocmented) value taken from boot0, without this DRAM + * access gets messed up (seems cache related) */ + setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800); +#endif #if !defined CONFIG_SPL_BUILD && (defined CONFIG_MACH_SUN7I || \ defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I) /* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Without this the cache will only work in write-through mode, and as soon as it is put in write-back mode things break.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Urk. Have Allwinner been prodded?
Acked-by: Ian Campbell ijc@hellion.org.uk

Enable the SPL now that we've all the necessary bits in place.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- board/sunxi/Kconfig | 1 + configs/Colombus_defconfig | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 5b2d091..6688386 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -16,6 +16,7 @@ config MACH_SUN5I config MACH_SUN6I bool "sun6i (Allwinner A31)" select CPU_V7 + select SUPPORT_SPL
config MACH_SUN7I bool "sun7i (Allwinner A20)" diff --git a/configs/Colombus_defconfig b/configs/Colombus_defconfig index 89291f9..bef568d 100644 --- a/configs/Colombus_defconfig +++ b/configs/Colombus_defconfig @@ -1,5 +1,6 @@ -CONFIG_ARM=y -CONFIG_ARCH_SUNXI=y -CONFIG_MACH_SUN6I=y -CONFIG_TARGET_COLOMBUS=y +CONFIG_SPL=y CONFIG_FDTFILE="sun6i-a31-colombus.dtb" ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN6I=y ++S:CONFIG_TARGET_COLOMBUS=y

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Enable the SPL now that we've all the necessary bits in place.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

Signed-off-by: Hans de Goede hdegoede@redhat.com --- board/sunxi/Kconfig | 4 ++++ board/sunxi/MAINTAINERS | 1 + configs/Mele_M9_defconfig | 15 +++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 configs/Mele_M9_defconfig
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 6688386..5c3b932 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -112,6 +112,10 @@ config TARGET_MELE_M3 bool "MELE_M3" depends on MACH_SUN7I
+config TARGET_MELE_M9 + bool "MELE_M9" + depends on MACH_SUN6I + config TARGET_MINI_X_1GB bool "MINI_X_1GB" depends on MACH_SUN4I diff --git a/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS index b3c77a8..ca03002 100644 --- a/board/sunxi/MAINTAINERS +++ b/board/sunxi/MAINTAINERS @@ -9,6 +9,7 @@ F: configs/Cubieboard_defconfig F: configs/Mele_A1000_defconfig F: configs/Mele_A1000G_defconfig F: configs/Mele_M3_defconfig +F: configs/Mele_M9_defconfig F: configs/Mini-X_defconfig F: configs/Mini-X-1Gb_defconfig F: include/configs/sun5i.h diff --git a/configs/Mele_M9_defconfig b/configs/Mele_M9_defconfig new file mode 100644 index 0000000..3dacb19 --- /dev/null +++ b/configs/Mele_M9_defconfig @@ -0,0 +1,15 @@ +CONFIG_SPL=y +CONFIG_FDTFILE="sun6i-a31-m9.dtb" ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN6I=y ++S:CONFIG_TARGET_MELE_M9=y +# Ethernet phy power ++S:CONFIG_AXP221_DLDO1_VOLT=3300 +# USB hub power ++S:CONFIG_AXP221_DLDO4_VOLT=3300 +# Wifi power ++S:CONFIG_AXP221_ALDO1_VOLT=3300 +# HDMI power ? ++S:CONFIG_AXP221_ALDO2_VOLT=1800 ++S:CONFIG_AXP221_ALDO3_VOLT=3000

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

Add proper Kconfig options to select the usb Vbus gpio-s, besides moving to Kconfig being the right thing to do, an added advantage of this is that it allows for boards without Vbus gpio-s.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- board/sunxi/Kconfig | 14 ++++++++++++++ configs/A10s-OLinuXino-M_defconfig | 3 ++- configs/A13-OLinuXinoM_defconfig | 3 ++- configs/A13-OLinuXino_defconfig | 3 ++- configs/Auxtek-T004_defconfig | 3 ++- configs/ba10_tv_box_defconfig | 3 ++- configs/r7-tv-dongle_defconfig | 3 ++- drivers/usb/host/ehci-sunxi.c | 35 +++++++++++++++++++++++++---------- include/configs/sun4i.h | 7 ------- include/configs/sun7i.h | 7 ------- 10 files changed, 51 insertions(+), 30 deletions(-)
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 5c3b932..b2beea0 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -197,4 +197,18 @@ config MMC_SUNXI_SLOT_EXTRA slot or emmc on mmc1 - mmc3. Setting this to 1, 2 or 3 will enable support for this.
+config USB1_VBUS_PIN + string "Vbus enable pin for usb1 (ehci0)" + default "PH6" if MACH_SUN4I || MACH_SUN7I + ---help--- + Set the Vbus enable pin for usb1 (ehci0, usb0 is the otg). This takes + a string in the format understood by sunxi_name_to_gpio, e.g. + PH1 for pin 1 of port H. + +config USB2_VBUS_PIN + string "Vbus enable pin for usb2 (ehci1)" + default "PH3" if MACH_SUN4I || MACH_SUN7I + ---help--- + See USB1_VBUS_PIN help text. + endif diff --git a/configs/A10s-OLinuXino-M_defconfig b/configs/A10s-OLinuXino-M_defconfig index 6475675..94fafa6 100644 --- a/configs/A10s-OLinuXino-M_defconfig +++ b/configs/A10s-OLinuXino-M_defconfig @@ -1,7 +1,8 @@ CONFIG_SPL=y -CONFIG_SYS_EXTRA_OPTIONS="AXP152_POWER,SUNXI_EMAC,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPB(10)" +CONFIG_SYS_EXTRA_OPTIONS="AXP152_POWER,SUNXI_EMAC,USB_EHCI" CONFIG_FDTFILE="sun5i-a10s-olinuxino-micro.dtb" CONFIG_MMC_SUNXI_SLOT_EXTRA=1 +CONFIG_USB1_VBUS_PIN="PB10" +S:CONFIG_MMC0_CD_PIN="PG1" +S:CONFIG_MMC1_CD_PIN="PG13" +S:CONFIG_ARM=y diff --git a/configs/A13-OLinuXinoM_defconfig b/configs/A13-OLinuXinoM_defconfig index d8b1239..8517203 100644 --- a/configs/A13-OLinuXinoM_defconfig +++ b/configs/A13-OLinuXinoM_defconfig @@ -1,6 +1,7 @@ CONFIG_SPL=y -CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=2,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(11)" +CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=2,USB_EHCI" CONFIG_FDTFILE="sun5i-a13-olinuxino-micro.dtb" +CONFIG_USB1_VBUS_PIN="PG11" +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y +S:CONFIG_MACH_SUN5I=y diff --git a/configs/A13-OLinuXino_defconfig b/configs/A13-OLinuXino_defconfig index 91039df..61f5466 100644 --- a/configs/A13-OLinuXino_defconfig +++ b/configs/A13-OLinuXino_defconfig @@ -1,6 +1,7 @@ CONFIG_SPL=y -CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=2,AXP209_POWER,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(11)" +CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=2,AXP209_POWER,USB_EHCI" CONFIG_FDTFILE="sun5i-a13-olinuxino.dtb" +CONFIG_USB1_VBUS_PIN="PG11" +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y +S:CONFIG_MACH_SUN5I=y diff --git a/configs/Auxtek-T004_defconfig b/configs/Auxtek-T004_defconfig index 5b06ea0..7fe9059 100644 --- a/configs/Auxtek-T004_defconfig +++ b/configs/Auxtek-T004_defconfig @@ -1,6 +1,7 @@ CONFIG_SPL=y -CONFIG_SYS_EXTRA_OPTIONS="AXP152_POWER,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(13)" +CONFIG_SYS_EXTRA_OPTIONS="AXP152_POWER,USB_EHCI" CONFIG_FDTFILE="sun5i-a10s-auxtek-t004.dtb" +CONFIG_USB1_VBUS_PIN="PG13" +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y +S:CONFIG_MACH_SUN5I=y diff --git a/configs/ba10_tv_box_defconfig b/configs/ba10_tv_box_defconfig index 0a1abea..6ca7c57 100644 --- a/configs/ba10_tv_box_defconfig +++ b/configs/ba10_tv_box_defconfig @@ -1,6 +1,7 @@ CONFIG_SPL=y -CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_EMAC,USB_EHCI,SUNXI_USB_VBUS1_GPIO=SUNXI_GPH(12)" +CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_EMAC,USB_EHCI" CONFIG_FDTFILE="sun4i-a10-ba10-tvbox.dtb" +CONFIG_USB1_VBUS_PIN="PH12" +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y +S:CONFIG_MACH_SUN4I=y diff --git a/configs/r7-tv-dongle_defconfig b/configs/r7-tv-dongle_defconfig index 7dbff40..b9fd59c 100644 --- a/configs/r7-tv-dongle_defconfig +++ b/configs/r7-tv-dongle_defconfig @@ -1,6 +1,7 @@ CONFIG_SPL=y -CONFIG_SYS_EXTRA_OPTIONS="AXP152_POWER,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(13)" +CONFIG_SYS_EXTRA_OPTIONS="AXP152_POWER,USB_EHCI" CONFIG_FDTFILE="sun5i-a10s-r7-tv-dongle.dtb" +CONFIG_USB1_VBUS_PIN="PG13" +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y +S:CONFIG_MACH_SUN5I=y diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index 4befd57..193ac43 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -39,7 +39,6 @@ static struct sunxi_ehci_hcd { { .usb_rst_mask = CCM_USB_CTRL_PHY1_RST, .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, - .gpio_vbus = CONFIG_SUNXI_USB_VBUS0_GPIO, .csr = (void *)SUNXI_USB_CSR, .irq = 39, .id = 1, @@ -48,7 +47,6 @@ static struct sunxi_ehci_hcd { { .usb_rst_mask = CCM_USB_CTRL_PHY2_RST, .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, - .gpio_vbus = CONFIG_SUNXI_USB_VBUS1_GPIO, .csr = (void *)SUNXI_USB_CSR, .irq = 40, .id = 2, @@ -68,6 +66,15 @@ static void *get_io_base(int hcd_id) return NULL; }
+static int get_vbus_gpio(int hcd_id) +{ + switch (hcd_id) { + 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_ehci_hcd *sunxi_ehci, int addr, int data, int len) { @@ -143,14 +150,16 @@ static void sunxi_ehci_enable(struct sunxi_ehci_hcd *sunxi_ehci)
sunxi_usb_passby(sunxi_ehci, SUNXI_USB_PASSBY_EN);
- gpio_direction_output(sunxi_ehci->gpio_vbus, 1); + if (sunxi_ehci->gpio_vbus != -1) + gpio_direction_output(sunxi_ehci->gpio_vbus, 1); }
static void sunxi_ehci_disable(struct sunxi_ehci_hcd *sunxi_ehci) { struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- gpio_direction_output(sunxi_ehci->gpio_vbus, 0); + if (sunxi_ehci->gpio_vbus != -1) + gpio_direction_output(sunxi_ehci->gpio_vbus, 0);
sunxi_usb_passby(sunxi_ehci, !SUNXI_USB_PASSBY_EN);
@@ -165,13 +174,17 @@ int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; int err;
+ sunxi_ehci->gpio_vbus = get_vbus_gpio(sunxi_ehci->id); + /* enable common PHY only once */ if (index == 0) setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE);
- err = gpio_request(sunxi_ehci->gpio_vbus, "ehci_vbus"); - if (err) - return err; + if (sunxi_ehci->gpio_vbus != -1) { + err = gpio_request(sunxi_ehci->gpio_vbus, "ehci_vbus"); + if (err) + return err; + }
sunxi_ehci_enable(sunxi_ehci);
@@ -197,9 +210,11 @@ int ehci_hcd_stop(int index)
sunxi_ehci_disable(sunxi_ehci);
- err = gpio_free(sunxi_ehci->gpio_vbus); - if (err) - return err; + if (sunxi_ehci->gpio_vbus != -1) { + err = gpio_free(sunxi_ehci->gpio_vbus); + if (err) + return err; + }
/* disable common PHY only once, for the last enabled hcd */ if (enabled_hcd_count == 1) diff --git a/include/configs/sun4i.h b/include/configs/sun4i.h index e0ec52d..7b85740 100644 --- a/include/configs/sun4i.h +++ b/include/configs/sun4i.h @@ -18,14 +18,7 @@
#ifdef CONFIG_USB_EHCI #define CONFIG_USB_EHCI_SUNXI - #define CONFIG_USB_MAX_CONTROLLER_COUNT 2 -#ifndef CONFIG_SUNXI_USB_VBUS0_GPIO -#define CONFIG_SUNXI_USB_VBUS0_GPIO SUNXI_GPH(6) -#endif -#ifndef CONFIG_SUNXI_USB_VBUS1_GPIO -#define CONFIG_SUNXI_USB_VBUS1_GPIO SUNXI_GPH(3) -#endif #endif
/* diff --git a/include/configs/sun7i.h b/include/configs/sun7i.h index 0193826..ea40790 100644 --- a/include/configs/sun7i.h +++ b/include/configs/sun7i.h @@ -19,14 +19,7 @@
#ifdef CONFIG_USB_EHCI #define CONFIG_USB_EHCI_SUNXI - #define CONFIG_USB_MAX_CONTROLLER_COUNT 2 -#ifndef CONFIG_SUNXI_USB_VBUS0_GPIO -#define CONFIG_SUNXI_USB_VBUS0_GPIO SUNXI_GPH(6) -#endif -#ifndef CONFIG_SUNXI_USB_VBUS1_GPIO -#define CONFIG_SUNXI_USB_VBUS1_GPIO SUNXI_GPH(3) -#endif #endif
#define CONFIG_ARMV7_VIRT 1

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Add proper Kconfig options to select the usb Vbus gpio-s, besides moving to Kconfig being the right thing to do, an added advantage of this is that it allows for boards without Vbus gpio-s.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

Add support for the 2 ehci controllers found on the sun6i (A31) soc.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 3 ++ arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 11 +++++++ arch/arm/include/asm/arch-sunxi/cpu.h | 8 +++++ board/sunxi/Kconfig | 2 ++ configs/Mele_M9_defconfig | 3 ++ drivers/usb/host/ehci-sunxi.c | 45 +++++++++++++++++---------- include/configs/sun6i.h | 5 +++ 7 files changed, 61 insertions(+), 16 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index 90af8e2..9dca800 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -262,5 +262,8 @@ struct sunxi_ccm_reg { #define CCM_USB_CTRL_PHY1_RST (0x1 << 1) #define CCM_USB_CTRL_PHY2_RST (0x1 << 2) #define CCM_USB_CTRL_PHYGATE (0x1 << 8) +/* These 2 are sun6i only, define them as 0 on sun4i */ +#define CCM_USB_CTRL_PHY1_CLK 0 +#define CCM_USB_CTRL_PHY2_CLK 0
#endif /* _SUNXI_CLOCK_SUN4I_H */ diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 4992dbc..e16a764 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -193,6 +193,10 @@ struct sunxi_ccm_reg {
#define AXI_GATE_OFFSET_DRAM 0
+#define AHB_GATE_OFFSET_USB_OHCI1 30 +#define AHB_GATE_OFFSET_USB_OHCI0 29 +#define AHB_GATE_OFFSET_USB_EHCI1 27 +#define AHB_GATE_OFFSET_USB_EHCI0 26 #define AHB_GATE_OFFSET_MCTL 14 #define AHB_GATE_OFFSET_MMC3 11 #define AHB_GATE_OFFSET_MMC2 10 @@ -205,6 +209,13 @@ struct sunxi_ccm_reg {
#define CCM_MMC_CTRL_ENABLE (0x1 << 31)
+#define CCM_USB_CTRL_PHY1_RST (0x1 << 1) +#define CCM_USB_CTRL_PHY2_RST (0x1 << 2) +/* There is no global phy clk gate on sun6i, define as 0 */ +#define CCM_USB_CTRL_PHYGATE 0 +#define CCM_USB_CTRL_PHY1_CLK (0x1 << 9) +#define CCM_USB_CTRL_PHY2_CLK (0x1 << 10) + #define MDFS_CLK_DEFAULT 0x81000002 /* PLL6 / 3 */
#define CCM_DRAMCLK_CFG_DIV0(x) ((x - 1) << 8) diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 6550e50..bdee89e 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -37,16 +37,24 @@ #define SUNXI_MMC1_BASE 0x01c10000 #define SUNXI_MMC2_BASE 0x01c11000 #define SUNXI_MMC3_BASE 0x01c12000 +#ifndef CONFIG_MACH_SUN6I #define SUNXI_USB0_BASE 0x01c13000 #define SUNXI_USB1_BASE 0x01c14000 +#endif #define SUNXI_SS_BASE 0x01c15000 #define SUNXI_HDMI_BASE 0x01c16000 #define SUNXI_SPI2_BASE 0x01c17000 #define SUNXI_SATA_BASE 0x01c18000 +#ifndef CONFIG_MACH_SUN6I #define SUNXI_PATA_BASE 0x01c19000 #define SUNXI_ACE_BASE 0x01c1a000 #define SUNXI_TVE1_BASE 0x01c1b000 #define SUNXI_USB2_BASE 0x01c1c000 +#else +#define SUNXI_USB0_BASE 0x01c19000 +#define SUNXI_USB1_BASE 0x01c1a000 +#define SUNXI_USB2_BASE 0x01c1b000 +#endif #define SUNXI_CSI1_BASE 0x01c1d000 #define SUNXI_TZASC_BASE 0x01c1e000 #define SUNXI_SPI3_BASE 0x01c1f000 diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index b2beea0..c3f865d 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -200,6 +200,7 @@ config MMC_SUNXI_SLOT_EXTRA config USB1_VBUS_PIN string "Vbus enable pin for usb1 (ehci0)" default "PH6" if MACH_SUN4I || MACH_SUN7I + default "PH27" if MACH_SUN6I ---help--- Set the Vbus enable pin for usb1 (ehci0, usb0 is the otg). This takes a string in the format understood by sunxi_name_to_gpio, e.g. @@ -208,6 +209,7 @@ config USB1_VBUS_PIN config USB2_VBUS_PIN string "Vbus enable pin for usb2 (ehci1)" default "PH3" if MACH_SUN4I || MACH_SUN7I + default "PH24" if MACH_SUN6I ---help--- See USB1_VBUS_PIN help text.
diff --git a/configs/Mele_M9_defconfig b/configs/Mele_M9_defconfig index 3dacb19..f46439f 100644 --- a/configs/Mele_M9_defconfig +++ b/configs/Mele_M9_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="USB_EHCI" CONFIG_FDTFILE="sun6i-a31-m9.dtb" +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y @@ -13,3 +14,5 @@ CONFIG_FDTFILE="sun6i-a31-m9.dtb" # HDMI power ? +S:CONFIG_AXP221_ALDO2_VOLT=1800 +S:CONFIG_AXP221_ALDO3_VOLT=3000 +# No Vbus gpio for usb1 ++S:CONFIG_USB1_VBUS_PIN="" diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index 193ac43..cc9a8fa 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -10,16 +10,14 @@ */
#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> #include <asm/gpio.h> #include <asm/io.h> #include <common.h> #include "ehci.h"
-#define SUNXI_USB1_IO_BASE 0x01c14000 -#define SUNXI_USB2_IO_BASE 0x01c1c000 - #define SUNXI_USB_PMU_IRQ_ENABLE 0x800 -#define SUNXI_USB_CSR 0x01c13404 +#define SUNXI_USB_CSR 0x404 #define SUNXI_USB_PASSBY_EN 1
#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) @@ -32,23 +30,28 @@ static struct sunxi_ehci_hcd { int usb_rst_mask; int ahb_clk_mask; int gpio_vbus; - void *csr; int irq; int id; } sunxi_echi_hcd[] = { { - .usb_rst_mask = CCM_USB_CTRL_PHY1_RST, + .usb_rst_mask = CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK, .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, - .csr = (void *)SUNXI_USB_CSR, +#ifndef CONFIG_MACH_SUN6I .irq = 39, +#else + .irq = 72, +#endif .id = 1, }, #if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) { - .usb_rst_mask = CCM_USB_CTRL_PHY2_RST, + .usb_rst_mask = CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK, .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, - .csr = (void *)SUNXI_USB_CSR, +#ifndef CONFIG_MACH_SUN6I .irq = 40, +#else + .irq = 74, +#endif .id = 2, } #endif @@ -58,12 +61,16 @@ static int enabled_hcd_count;
static void *get_io_base(int hcd_id) { - if (hcd_id == 1) - return (void *)SUNXI_USB1_IO_BASE; - else if (hcd_id == 2) - return (void *)SUNXI_USB2_IO_BASE; - else + switch (hcd_id) { + 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 hcd_id) @@ -79,7 +86,7 @@ static void usb_phy_write(struct sunxi_ehci_hcd *sunxi_ehci, int addr, int data, int len) { int j = 0, usbc_bit = 0; - void *dest = sunxi_ehci->csr; + void *dest = get_io_base(0) + SUNXI_USB_CSR;
usbc_bit = 1 << (sunxi_ehci->id * 2); for (j = 0; j < len; j++) { @@ -112,7 +119,7 @@ static void sunxi_usb_phy_init(struct sunxi_ehci_hcd *sunxi_ehci) usb_phy_write(sunxi_ehci, 0x20, 0x14, 5);
/* threshold adjustment disconnect */ -#ifdef CONFIG_MACH_SUN4I +#if defined CONFIG_MACH_SUN4I || defined CONFIG_MACH_SUN6I usb_phy_write(sunxi_ehci, 0x2a, 3, 2); #else usb_phy_write(sunxi_ehci, 0x2a, 2, 2); @@ -145,6 +152,9 @@ static void sunxi_ehci_enable(struct sunxi_ehci_hcd *sunxi_ehci)
setbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); setbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); +#ifdef CONFIG_MACH_SUN6I + setbits_le32(&ccm->ahb_reset0_cfg, sunxi_ehci->ahb_clk_mask); +#endif
sunxi_usb_phy_init(sunxi_ehci);
@@ -163,6 +173,9 @@ static void sunxi_ehci_disable(struct sunxi_ehci_hcd *sunxi_ehci)
sunxi_usb_passby(sunxi_ehci, !SUNXI_USB_PASSBY_EN);
+#ifdef CONFIG_MACH_SUN6I + clrbits_le32(&ccm->ahb_reset0_cfg, sunxi_ehci->ahb_clk_mask); +#endif clrbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); clrbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); } diff --git a/include/configs/sun6i.h b/include/configs/sun6i.h index 9558771..1b73852 100644 --- a/include/configs/sun6i.h +++ b/include/configs/sun6i.h @@ -18,6 +18,11 @@
#define CONFIG_SYS_PROMPT "sun6i# "
+#ifdef CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_SUNXI +#define CONFIG_USB_MAX_CONTROLLER_COUNT 2 +#endif + /* * Include common sunxi configuration where most the settings are */

On Fri, 2014-11-07 at 20:47 +0100, Hans de Goede wrote:
Add support for the 2 ehci controllers found on the sun6i (A31) soc.
Signed-off-by: Hans de Goede hdegoede@redhat.com
You should probably run this by the usb custodian (Marek?) but for my part: Acked-by: Ian Campbell ijc@hellion.org.uk
participants (2)
-
Hans de Goede
-
Ian Campbell