[U-Boot] [PATCH 01/14] sun6i: s/SUNXI_GPL0_R_P2WI/SUN6I_GPL0_R_P2WI/

The p2wi interface is only available on sun6i, adjust the gpio pinmux defines for it to reflect this.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/p2wi.c | 4 ++-- arch/arm/include/asm/arch-sunxi/gpio.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/p2wi.c b/arch/arm/cpu/armv7/sunxi/p2wi.c index 48613bd..52c2c53 100644 --- a/arch/arm/cpu/armv7/sunxi/p2wi.c +++ b/arch/arm/cpu/armv7/sunxi/p2wi.c @@ -31,8 +31,8 @@ void p2wi_init(void) /* Enable p2wi and PIO clk, and de-assert their resets */ prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
- sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUNXI_GPL0_R_P2WI_SCK); - sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUNXI_GPL1_R_P2WI_SDA); + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA);
/* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */ writel(P2WI_CTRL_RESET, &p2wi->ctrl); diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 366c0dc..9f972ce 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -173,8 +173,8 @@ enum sunxi_gpio_number {
#define SUN4I_GPI4_SDC3 2
-#define SUNXI_GPL0_R_P2WI_SCK 3 -#define SUNXI_GPL1_R_P2WI_SDA 3 +#define SUN6I_GPL0_R_P2WI_SCK 3 +#define SUN6I_GPL1_R_P2WI_SDA 3
#define SUN8I_GPL2_R_UART_TX 2 #define SUN8I_GPL3_R_UART_RX 2

sun8i (A23) introduces a new bus for communicating with the pmic, the rsb, the rsb is also used to communicate with the pmic on the A80, and is documented in the A80 user manual.
This commit adds support for this based on the rsb driver from the allwinner u-boot sources.
Note: Not yet ready for upstream, code needs some significant cleanups.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/rsb.c | 158 +++++++++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/cpu.h | 3 +- arch/arm/include/asm/arch-sunxi/gpio.h | 2 + arch/arm/include/asm/arch-sunxi/prcm.h | 3 +- arch/arm/include/asm/arch-sunxi/rsb.h | 55 ++++++++++++ 6 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/rsb.c create mode 100644 arch/arm/include/asm/arch-sunxi/rsb.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 1337b60..3e8975a 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -15,6 +15,7 @@ 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_SUN8I) += rsb.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/rsb.c b/arch/arm/cpu/armv7/sunxi/rsb.c new file mode 100644 index 0000000..b72bb9d --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/rsb.c @@ -0,0 +1,158 @@ +/* + * (C) Copyright 2014 Hans de Goede hdegoede@redhat.com + * + * Based on allwinner u-boot sources rsb code which is: + * (C) Copyright 2007-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * lixiang lixiang@allwinnertech.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/prcm.h> +#include <asm/arch/rsb.h> + +static void rsb_cfg_io(void) +{ + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL0_R_RSB_SCK); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL1_R_RSB_SDA); + sunxi_gpio_set_pull(SUNXI_GPL(0), 1); + sunxi_gpio_set_pull(SUNXI_GPL(1), 1); + sunxi_gpio_set_drv(SUNXI_GPL(0), 2); + sunxi_gpio_set_drv(SUNXI_GPL(1), 2); +} + +static void rsb_set_clk(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + u32 div = 0; + u32 cd_odly = 0; + + /* Source is Hosc24M, set RSB clk to 3Mhz */ + div = 24000000 / 3000000 / 2 - 1; + cd_odly = div >> 1; + if (!cd_odly) + cd_odly = 1; + + writel((cd_odly << 8) | div, &rsb->ccr); +} + +void rsb_init(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + rsb_cfg_io(); + + /* Enable RSB and PIO clk, and de-assert their resets */ + prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB); + + writel(RSB_CTRL_SOFT_RST, &rsb->ctrl); + rsb_set_clk(); +} + +static int rsb_await_trans(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + unsigned long tmo = timer_get_us() + 1000000; + u32 stat; + int ret; + + while (1) { + stat = readl(&rsb->stat); + if (stat & RSB_STAT_LBSY_INT) { + ret = -EBUSY; + break; + } + if (stat & RSB_STAT_TERR_INT) { + ret = -EIO; + break; + } + if (stat & RSB_STAT_TOVER_INT) { + ret = 0; + break; + } + if (timer_get_us() > tmo) { + ret = -ETIME; + break; + } + } + writel(stat, &rsb->stat); /* Clear status bits */ + + return ret; +} + +int rsb_set_device_mode(u32 device_mode_data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + unsigned long tmo = timer_get_us() + 1000000; + + writel(RSB_DMCR_DEVICE_MODE_START | device_mode_data, &rsb->dmcr); + + while (readl(&rsb->dmcr) & RSB_DMCR_DEVICE_MODE_START) { + if (timer_get_us() > tmo) + return -ETIME; + } + + return rsb_await_trans(); +} + +static int rsb_do_trans(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + setbits_le32(&rsb->ctrl, RSB_CTRL_START_TRANS); + return rsb_await_trans(); +} + +int rsb_set_device_address(u16 device_addr, u16 runtime_addr) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_addr) | + RSB_DEVADDR_DEVICE_ADDR(device_addr), &rsb->devaddr); + writel(RSB_CMD_SET_RTSADDR, &rsb->cmd); + + return rsb_do_trans(); +} + +int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); + writel(reg_addr, &rsb->addr); + writel(data, &rsb->data); + writel(RSB_CMD_BYTE_WRITE, &rsb->cmd); + + return rsb_do_trans(); +} + +int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + int ret; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); + writel(reg_addr, &rsb->addr); + writel(RSB_CMD_BYTE_READ, &rsb->cmd); + + ret = rsb_do_trans(); + if (ret) + return ret; + + *data = readl(&rsb->data) & 0xff; + + return 0; +} diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 9500262..49c1614 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -128,7 +128,8 @@ #define SUN6I_CPUCFG_BASE 0x01f01c00 #define SUNXI_R_UART_BASE 0x01f02800 #define SUNXI_R_PIO_BASE 0x01f02c00 -#define SUNXI_P2WI_BASE 0x01f03400 +#define SUNXI_P2WI_BASE 0x01f03400 /* on sun6i */ +#define SUNXI_RSB_BASE 0x01f03400 /* on sun8i */
/* CoreSight Debug Module */ #define SUNXI_CSDM_BASE 0x3f500000 diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 9f972ce..6623f15 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -176,6 +176,8 @@ enum sunxi_gpio_number { #define SUN6I_GPL0_R_P2WI_SCK 3 #define SUN6I_GPL1_R_P2WI_SDA 3
+#define SUN8I_GPL0_R_RSB_SCK 2 +#define SUN8I_GPL1_R_RSB_SDA 2 #define SUN8I_GPL2_R_UART_TX 2 #define SUN8I_GPL3_R_UART_RX 2
diff --git a/arch/arm/include/asm/arch-sunxi/prcm.h b/arch/arm/include/asm/arch-sunxi/prcm.h index 88de1ff..82ed541 100644 --- a/arch/arm/include/asm/arch-sunxi/prcm.h +++ b/arch/arm/include/asm/arch-sunxi/prcm.h @@ -50,7 +50,8 @@ #define PRCM_APB0_GATE_PIO (0x1 << 0) #define PRCM_APB0_GATE_IR (0x1 << 1) #define PRCM_APB0_GATE_TIMER01 (0x1 << 2) -#define PRCM_APB0_GATE_P2WI (0x1 << 3) +#define PRCM_APB0_GATE_P2WI (0x1 << 3) /* sun6i */ +#define PRCM_APB0_GATE_RSB (0x1 << 3) /* sun8i */ #define PRCM_APB0_GATE_UART (0x1 << 4) #define PRCM_APB0_GATE_1WIRE (0x1 << 5) #define PRCM_APB0_GATE_I2C (0x1 << 6) diff --git a/arch/arm/include/asm/arch-sunxi/rsb.h b/arch/arm/include/asm/arch-sunxi/rsb.h new file mode 100644 index 0000000..95a595a --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/rsb.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 2014 Hans de Goede hdegoede@redhat.com + * + * Based on allwinner u-boot sources rsb code which is: + * (C) Copyright 2007-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * lixiang lixiang@allwinnertech.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SUNXI_RSB_H +#define __SUNXI_RSB_H + +#include <common.h> +#include <asm/io.h> + +struct sunxi_rsb_reg { + u32 ctrl; /* 0x00 */ + u32 ccr; /* 0x04 */ + u32 inte; /* 0x08 */ + u32 stat; /* 0x0c */ + u32 addr; /* 0x10 */ + u8 res0[8]; /* 0x14 */ + u32 data; /* 0x1c */ + u8 res1[4]; /* 0x20 */ + u32 lcr; /* 0x24 */ + u32 dmcr; /* 0x28 */ + u32 cmd; /* 0x2c */ + u32 devaddr; /* 0x30 */ +}; + +#define RSB_CTRL_SOFT_RST (1 << 0) +#define RSB_CTRL_START_TRANS (1 << 7) + +#define RSB_STAT_TOVER_INT (1 << 0) +#define RSB_STAT_TERR_INT (1 << 1) +#define RSB_STAT_LBSY_INT (1 << 2) + +#define RSB_DMCR_DEVICE_MODE_START (1 << 31) + +#define RSB_CMD_BYTE_WRITE 0x4e +#define RSB_CMD_BYTE_READ 0x8b +#define RSB_CMD_SET_RTSADDR 0xe8 + +#define RSB_DEVADDR_RUNTIME_ADDR(x) ((x) << 16) +#define RSB_DEVADDR_DEVICE_ADDR(x) ((x) << 0) + +void rsb_init(void); +int rsb_set_device_mode(u32 device_mode_data); +int rsb_set_device_address(u16 device_addr, u16 runtime_addr); +int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data); +int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data); + +#endif

Hi,
On Wed, Dec 17, 2014 at 4:31 AM, Hans de Goede hdegoede@redhat.com wrote:
sun8i (A23) introduces a new bus for communicating with the pmic, the rsb, the rsb is also used to communicate with the pmic on the A80, and is documented in the A80 user manual.
This commit adds support for this based on the rsb driver from the allwinner u-boot sources.
Note: Not yet ready for upstream, code needs some significant cleanups.
Confusing note.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/rsb.c | 158 +++++++++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/cpu.h | 3 +- arch/arm/include/asm/arch-sunxi/gpio.h | 2 + arch/arm/include/asm/arch-sunxi/prcm.h | 3 +- arch/arm/include/asm/arch-sunxi/rsb.h | 55 ++++++++++++ 6 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/rsb.c create mode 100644 arch/arm/include/asm/arch-sunxi/rsb.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 1337b60..3e8975a 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -15,6 +15,7 @@ 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_SUN8I) += rsb.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/rsb.c b/arch/arm/cpu/armv7/sunxi/rsb.c new file mode 100644 index 0000000..b72bb9d --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/rsb.c @@ -0,0 +1,158 @@ +/*
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- Based on allwinner u-boot sources rsb code which is:
- (C) Copyright 2007-2013
- Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- lixiang lixiang@allwinnertech.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <errno.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/prcm.h> +#include <asm/arch/rsb.h>
+static void rsb_cfg_io(void) +{
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL0_R_RSB_SCK);
sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL1_R_RSB_SDA);
sunxi_gpio_set_pull(SUNXI_GPL(0), 1);
sunxi_gpio_set_pull(SUNXI_GPL(1), 1);
sunxi_gpio_set_drv(SUNXI_GPL(0), 2);
sunxi_gpio_set_drv(SUNXI_GPL(1), 2);
+}
+static void rsb_set_clk(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
u32 div = 0;
u32 cd_odly = 0;
/* Source is Hosc24M, set RSB clk to 3Mhz */
div = 24000000 / 3000000 / 2 - 1;
cd_odly = div >> 1;
if (!cd_odly)
cd_odly = 1;
writel((cd_odly << 8) | div, &rsb->ccr);
+}
+void rsb_init(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
rsb_cfg_io();
/* Enable RSB and PIO clk, and de-assert their resets */
prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB);
writel(RSB_CTRL_SOFT_RST, &rsb->ctrl);
Do we need a small delay here for the device to finish reset?
rsb_set_clk();
+}
+static int rsb_await_trans(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
unsigned long tmo = timer_get_us() + 1000000;
u32 stat;
int ret;
while (1) {
stat = readl(&rsb->stat);
if (stat & RSB_STAT_LBSY_INT) {
ret = -EBUSY;
break;
}
if (stat & RSB_STAT_TERR_INT) {
ret = -EIO;
break;
}
if (stat & RSB_STAT_TOVER_INT) {
ret = 0;
break;
}
if (timer_get_us() > tmo) {
ret = -ETIME;
break;
}
}
writel(stat, &rsb->stat); /* Clear status bits */
return ret;
+}
+int rsb_set_device_mode(u32 device_mode_data) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
unsigned long tmo = timer_get_us() + 1000000;
writel(RSB_DMCR_DEVICE_MODE_START | device_mode_data, &rsb->dmcr);
while (readl(&rsb->dmcr) & RSB_DMCR_DEVICE_MODE_START) {
if (timer_get_us() > tmo)
return -ETIME;
}
return rsb_await_trans();
+}
+static int rsb_do_trans(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
setbits_le32(&rsb->ctrl, RSB_CTRL_START_TRANS);
return rsb_await_trans();
+}
+int rsb_set_device_address(u16 device_addr, u16 runtime_addr) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_addr) |
RSB_DEVADDR_DEVICE_ADDR(device_addr), &rsb->devaddr);
writel(RSB_CMD_SET_RTSADDR, &rsb->cmd);
return rsb_do_trans();
+}
+int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr);
writel(reg_addr, &rsb->addr);
writel(data, &rsb->data);
writel(RSB_CMD_BYTE_WRITE, &rsb->cmd);
return rsb_do_trans();
+}
+int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
int ret;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr);
writel(reg_addr, &rsb->addr);
writel(RSB_CMD_BYTE_READ, &rsb->cmd);
ret = rsb_do_trans();
if (ret)
return ret;
*data = readl(&rsb->data) & 0xff;
return 0;
+} diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 9500262..49c1614 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -128,7 +128,8 @@ #define SUN6I_CPUCFG_BASE 0x01f01c00 #define SUNXI_R_UART_BASE 0x01f02800 #define SUNXI_R_PIO_BASE 0x01f02c00 -#define SUNXI_P2WI_BASE 0x01f03400 +#define SUNXI_P2WI_BASE 0x01f03400 /* on sun6i */ +#define SUNXI_RSB_BASE 0x01f03400 /* on sun8i */
Maybe we just use SUN6I_ SUN8I_ prefixes? The base address will be different on sun9i.
/* CoreSight Debug Module */ #define SUNXI_CSDM_BASE 0x3f500000 diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 9f972ce..6623f15 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -176,6 +176,8 @@ enum sunxi_gpio_number { #define SUN6I_GPL0_R_P2WI_SCK 3 #define SUN6I_GPL1_R_P2WI_SDA 3
+#define SUN8I_GPL0_R_RSB_SCK 2 +#define SUN8I_GPL1_R_RSB_SDA 2 #define SUN8I_GPL2_R_UART_TX 2 #define SUN8I_GPL3_R_UART_RX 2
diff --git a/arch/arm/include/asm/arch-sunxi/prcm.h b/arch/arm/include/asm/arch-sunxi/prcm.h index 88de1ff..82ed541 100644 --- a/arch/arm/include/asm/arch-sunxi/prcm.h +++ b/arch/arm/include/asm/arch-sunxi/prcm.h @@ -50,7 +50,8 @@ #define PRCM_APB0_GATE_PIO (0x1 << 0) #define PRCM_APB0_GATE_IR (0x1 << 1) #define PRCM_APB0_GATE_TIMER01 (0x1 << 2) -#define PRCM_APB0_GATE_P2WI (0x1 << 3) +#define PRCM_APB0_GATE_P2WI (0x1 << 3) /* sun6i */ +#define PRCM_APB0_GATE_RSB (0x1 << 3) /* sun8i */ #define PRCM_APB0_GATE_UART (0x1 << 4) #define PRCM_APB0_GATE_1WIRE (0x1 << 5) #define PRCM_APB0_GATE_I2C (0x1 << 6) diff --git a/arch/arm/include/asm/arch-sunxi/rsb.h b/arch/arm/include/asm/arch-sunxi/rsb.h new file mode 100644 index 0000000..95a595a --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/rsb.h @@ -0,0 +1,55 @@ +/*
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- Based on allwinner u-boot sources rsb code which is:
- (C) Copyright 2007-2013
- Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- lixiang lixiang@allwinnertech.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __SUNXI_RSB_H +#define __SUNXI_RSB_H
+#include <common.h> +#include <asm/io.h>
+struct sunxi_rsb_reg {
u32 ctrl; /* 0x00 */
u32 ccr; /* 0x04 */
u32 inte; /* 0x08 */
u32 stat; /* 0x0c */
u32 addr; /* 0x10 */
u8 res0[8]; /* 0x14 */
u32 data; /* 0x1c */
u8 res1[4]; /* 0x20 */
u32 lcr; /* 0x24 */
u32 dmcr; /* 0x28 */
u32 cmd; /* 0x2c */
u32 devaddr; /* 0x30 */
+};
+#define RSB_CTRL_SOFT_RST (1 << 0) +#define RSB_CTRL_START_TRANS (1 << 7)
+#define RSB_STAT_TOVER_INT (1 << 0) +#define RSB_STAT_TERR_INT (1 << 1) +#define RSB_STAT_LBSY_INT (1 << 2)
+#define RSB_DMCR_DEVICE_MODE_START (1 << 31)
+#define RSB_CMD_BYTE_WRITE 0x4e +#define RSB_CMD_BYTE_READ 0x8b +#define RSB_CMD_SET_RTSADDR 0xe8
+#define RSB_DEVADDR_RUNTIME_ADDR(x) ((x) << 16) +#define RSB_DEVADDR_DEVICE_ADDR(x) ((x) << 0)
+void rsb_init(void); +int rsb_set_device_mode(u32 device_mode_data); +int rsb_set_device_address(u16 device_addr, u16 runtime_addr); +int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data); +int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data);
+#endif
2.1.0
The rest looks good. Thanks!
ChenYu

Hi,
On 17-12-14 03:22, Chen-Yu Tsai wrote:
Hi,
On Wed, Dec 17, 2014 at 4:31 AM, Hans de Goede hdegoede@redhat.com wrote:
sun8i (A23) introduces a new bus for communicating with the pmic, the rsb, the rsb is also used to communicate with the pmic on the A80, and is documented in the A80 user manual.
This commit adds support for this based on the rsb driver from the allwinner u-boot sources.
Note: Not yet ready for upstream, code needs some significant cleanups.
Confusing note.
Ugh, I forgot to remove that note after doing the cleanups, I'll remove it from the version of the patch my personal tree :)
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/rsb.c | 158 +++++++++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/cpu.h | 3 +- arch/arm/include/asm/arch-sunxi/gpio.h | 2 + arch/arm/include/asm/arch-sunxi/prcm.h | 3 +- arch/arm/include/asm/arch-sunxi/rsb.h | 55 ++++++++++++ 6 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/rsb.c create mode 100644 arch/arm/include/asm/arch-sunxi/rsb.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 1337b60..3e8975a 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -15,6 +15,7 @@ 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_SUN8I) += rsb.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/rsb.c b/arch/arm/cpu/armv7/sunxi/rsb.c new file mode 100644 index 0000000..b72bb9d --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/rsb.c @@ -0,0 +1,158 @@ +/*
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- Based on allwinner u-boot sources rsb code which is:
- (C) Copyright 2007-2013
- Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- lixiang lixiang@allwinnertech.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <errno.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/prcm.h> +#include <asm/arch/rsb.h>
+static void rsb_cfg_io(void) +{
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL0_R_RSB_SCK);
sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL1_R_RSB_SDA);
sunxi_gpio_set_pull(SUNXI_GPL(0), 1);
sunxi_gpio_set_pull(SUNXI_GPL(1), 1);
sunxi_gpio_set_drv(SUNXI_GPL(0), 2);
sunxi_gpio_set_drv(SUNXI_GPL(1), 2);
+}
+static void rsb_set_clk(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
u32 div = 0;
u32 cd_odly = 0;
/* Source is Hosc24M, set RSB clk to 3Mhz */
div = 24000000 / 3000000 / 2 - 1;
cd_odly = div >> 1;
if (!cd_odly)
cd_odly = 1;
writel((cd_odly << 8) | div, &rsb->ccr);
+}
+void rsb_init(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
rsb_cfg_io();
/* Enable RSB and PIO clk, and de-assert their resets */
prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB);
writel(RSB_CTRL_SOFT_RST, &rsb->ctrl);
Do we need a small delay here for the device to finish reset?
Maybe, this (no delay) is what the allwinner code was doing, and it seems to work fine ...
rsb_set_clk();
+}
+static int rsb_await_trans(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
unsigned long tmo = timer_get_us() + 1000000;
u32 stat;
int ret;
while (1) {
stat = readl(&rsb->stat);
if (stat & RSB_STAT_LBSY_INT) {
ret = -EBUSY;
break;
}
if (stat & RSB_STAT_TERR_INT) {
ret = -EIO;
break;
}
if (stat & RSB_STAT_TOVER_INT) {
ret = 0;
break;
}
if (timer_get_us() > tmo) {
ret = -ETIME;
break;
}
}
writel(stat, &rsb->stat); /* Clear status bits */
return ret;
+}
+int rsb_set_device_mode(u32 device_mode_data) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
unsigned long tmo = timer_get_us() + 1000000;
writel(RSB_DMCR_DEVICE_MODE_START | device_mode_data, &rsb->dmcr);
while (readl(&rsb->dmcr) & RSB_DMCR_DEVICE_MODE_START) {
if (timer_get_us() > tmo)
return -ETIME;
}
return rsb_await_trans();
+}
+static int rsb_do_trans(void) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
setbits_le32(&rsb->ctrl, RSB_CTRL_START_TRANS);
return rsb_await_trans();
+}
+int rsb_set_device_address(u16 device_addr, u16 runtime_addr) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_addr) |
RSB_DEVADDR_DEVICE_ADDR(device_addr), &rsb->devaddr);
writel(RSB_CMD_SET_RTSADDR, &rsb->cmd);
return rsb_do_trans();
+}
+int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr);
writel(reg_addr, &rsb->addr);
writel(data, &rsb->data);
writel(RSB_CMD_BYTE_WRITE, &rsb->cmd);
return rsb_do_trans();
+}
+int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data) +{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
int ret;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr);
writel(reg_addr, &rsb->addr);
writel(RSB_CMD_BYTE_READ, &rsb->cmd);
ret = rsb_do_trans();
if (ret)
return ret;
*data = readl(&rsb->data) & 0xff;
return 0;
+} diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 9500262..49c1614 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -128,7 +128,8 @@ #define SUN6I_CPUCFG_BASE 0x01f01c00 #define SUNXI_R_UART_BASE 0x01f02800 #define SUNXI_R_PIO_BASE 0x01f02c00 -#define SUNXI_P2WI_BASE 0x01f03400 +#define SUNXI_P2WI_BASE 0x01f03400 /* on sun6i */ +#define SUNXI_RSB_BASE 0x01f03400 /* on sun8i */
Maybe we just use SUN6I_ SUN8I_ prefixes? The base address will be different on sun9i.
/* CoreSight Debug Module */ #define SUNXI_CSDM_BASE 0x3f500000 diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 9f972ce..6623f15 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -176,6 +176,8 @@ enum sunxi_gpio_number { #define SUN6I_GPL0_R_P2WI_SCK 3 #define SUN6I_GPL1_R_P2WI_SDA 3
+#define SUN8I_GPL0_R_RSB_SCK 2 +#define SUN8I_GPL1_R_RSB_SDA 2 #define SUN8I_GPL2_R_UART_TX 2 #define SUN8I_GPL3_R_UART_RX 2
diff --git a/arch/arm/include/asm/arch-sunxi/prcm.h b/arch/arm/include/asm/arch-sunxi/prcm.h index 88de1ff..82ed541 100644 --- a/arch/arm/include/asm/arch-sunxi/prcm.h +++ b/arch/arm/include/asm/arch-sunxi/prcm.h @@ -50,7 +50,8 @@ #define PRCM_APB0_GATE_PIO (0x1 << 0) #define PRCM_APB0_GATE_IR (0x1 << 1) #define PRCM_APB0_GATE_TIMER01 (0x1 << 2) -#define PRCM_APB0_GATE_P2WI (0x1 << 3) +#define PRCM_APB0_GATE_P2WI (0x1 << 3) /* sun6i */ +#define PRCM_APB0_GATE_RSB (0x1 << 3) /* sun8i */ #define PRCM_APB0_GATE_UART (0x1 << 4) #define PRCM_APB0_GATE_1WIRE (0x1 << 5) #define PRCM_APB0_GATE_I2C (0x1 << 6) diff --git a/arch/arm/include/asm/arch-sunxi/rsb.h b/arch/arm/include/asm/arch-sunxi/rsb.h new file mode 100644 index 0000000..95a595a --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/rsb.h @@ -0,0 +1,55 @@ +/*
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- Based on allwinner u-boot sources rsb code which is:
- (C) Copyright 2007-2013
- Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- lixiang lixiang@allwinnertech.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __SUNXI_RSB_H +#define __SUNXI_RSB_H
+#include <common.h> +#include <asm/io.h>
+struct sunxi_rsb_reg {
u32 ctrl; /* 0x00 */
u32 ccr; /* 0x04 */
u32 inte; /* 0x08 */
u32 stat; /* 0x0c */
u32 addr; /* 0x10 */
u8 res0[8]; /* 0x14 */
u32 data; /* 0x1c */
u8 res1[4]; /* 0x20 */
u32 lcr; /* 0x24 */
u32 dmcr; /* 0x28 */
u32 cmd; /* 0x2c */
u32 devaddr; /* 0x30 */
+};
+#define RSB_CTRL_SOFT_RST (1 << 0) +#define RSB_CTRL_START_TRANS (1 << 7)
+#define RSB_STAT_TOVER_INT (1 << 0) +#define RSB_STAT_TERR_INT (1 << 1) +#define RSB_STAT_LBSY_INT (1 << 2)
+#define RSB_DMCR_DEVICE_MODE_START (1 << 31)
+#define RSB_CMD_BYTE_WRITE 0x4e +#define RSB_CMD_BYTE_READ 0x8b +#define RSB_CMD_SET_RTSADDR 0xe8
+#define RSB_DEVADDR_RUNTIME_ADDR(x) ((x) << 16) +#define RSB_DEVADDR_DEVICE_ADDR(x) ((x) << 0)
+void rsb_init(void); +int rsb_set_device_mode(u32 device_mode_data); +int rsb_set_device_address(u16 device_addr, u16 runtime_addr); +int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data); +int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data);
+#endif
2.1.0
The rest looks good. Thanks!
Regards,
Hans

On Wed, 2014-12-17 at 13:56 +0100, Hans de Goede wrote:
Hi,
On 17-12-14 03:22, Chen-Yu Tsai wrote:
Hi,
On Wed, Dec 17, 2014 at 4:31 AM, Hans de Goede hdegoede@redhat.com wrote:
sun8i (A23) introduces a new bus for communicating with the pmic, the rsb, the rsb is also used to communicate with the pmic on the A80, and is documented in the A80 user manual.
This commit adds support for this based on the rsb driver from the allwinner u-boot sources.
Note: Not yet ready for upstream, code needs some significant cleanups.
Confusing note.
Ugh, I forgot to remove that note after doing the cleanups, I'll remove it from the version of the patch my personal tree :)
I'll stop ignoring this patch then ;-)
Ian.

On Wed, 2014-12-17 at 13:56 +0100, Hans de Goede wrote:
@@ -128,7 +128,8 @@ #define SUN6I_CPUCFG_BASE 0x01f01c00 #define SUNXI_R_UART_BASE 0x01f02800 #define SUNXI_R_PIO_BASE 0x01f02c00 -#define SUNXI_P2WI_BASE 0x01f03400 +#define SUNXI_P2WI_BASE 0x01f03400 /* on sun6i */ +#define SUNXI_RSB_BASE 0x01f03400 /* on sun8i */
Maybe we just use SUN6I_ SUN8I_ prefixes? The base address will be different on sun9i.
Did you miss this suggestions from Chen-Yu? Seems like a good one.
I don't have any other comments.
Ian.

Hi,
On 18-12-14 19:59, Ian Campbell wrote:
On Wed, 2014-12-17 at 13:56 +0100, Hans de Goede wrote:
@@ -128,7 +128,8 @@ #define SUN6I_CPUCFG_BASE 0x01f01c00 #define SUNXI_R_UART_BASE 0x01f02800 #define SUNXI_R_PIO_BASE 0x01f02c00 -#define SUNXI_P2WI_BASE 0x01f03400 +#define SUNXI_P2WI_BASE 0x01f03400 /* on sun6i */ +#define SUNXI_RSB_BASE 0x01f03400 /* on sun8i */
Maybe we just use SUN6I_ SUN8I_ prefixes? The base address will be different on sun9i.
Did you miss this suggestions from Chen-Yu? Seems like a good one.
Yes I missed that (must have read over it). That will work for P2Wi, but not for RSB, as RSB is used on the A80 too AFAIK, but I guess having P2WI as SUN6I and RSB as SUNXI makes it clear what is used where, so I'll update the patch with that change.
Regards,
Hans

The axp223 appears to be the same as the axp221, except that it uses the rsb to communicate rather then the p2wi. At least all the registers we use are 100% the same.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/cpu_info.c | 2 +- drivers/power/Kconfig | 8 ++-- drivers/power/axp221.c | 88 ++++++++++++++++++++++++++++--------- include/axp221.h | 4 ++ 4 files changed, 76 insertions(+), 26 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/cpu_info.c b/arch/arm/cpu/armv7/sunxi/cpu_info.c index 7a3a4ca..b6cb9de 100644 --- a/arch/arm/cpu/armv7/sunxi/cpu_info.c +++ b/arch/arm/cpu/armv7/sunxi/cpu_info.c @@ -76,7 +76,7 @@ int print_cpuinfo(void)
int sunxi_get_sid(unsigned int *sid) { -#ifdef CONFIG_MACH_SUN6I +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I #ifdef CONFIG_AXP221_POWER return axp221_get_sid(sid); #else diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 1ec7c0e..7373a79 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -1,10 +1,10 @@ config AXP221_POWER - boolean "axp221 pmic support" - depends on MACH_SUN6I + boolean "axp221 / axp223 pmic support" + depends on MACH_SUN6I || MACH_SUN8I default y ---help--- - Say y here to enable support for the axp221 pmic found on most sun6i - (A31) boards. + Say y here to enable support for the axp221 / axp223 pmic found on most + sun6i (A31) / sun8i (A23) boards.
config AXP221_DLDO1_VOLT int "axp221 dldo1 voltage" diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 826567a..717adad 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -1,4 +1,10 @@ /* + * AXP221 and AXP223 driver + * + * IMPORTANT when making changes to this file check that the registers + * used are the same for the axp221 and axp223. + * + * (C) Copyright 2014 Hans de Goede hdegoede@redhat.com * (C) Copyright 2013 Oliver Schinagl oliver@schinagl.nl * * SPDX-License-Identifier: GPL-2.0+ @@ -7,8 +13,50 @@ #include <common.h> #include <errno.h> #include <asm/arch/p2wi.h> +#include <asm/arch/rsb.h> #include <axp221.h>
+/* + * The axp221 uses the p2wi bus, the axp223 is identical (for all registers + * used sofar) but uses the rsb bus. These functions abstract this. + */ +static int pmic_bus_init(void) +{ +#ifdef CONFIG_MACH_SUN6I + p2wi_init(); + return p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR, + AXP221_INIT_DATA); +#else + int ret; + + rsb_init(); + + ret = rsb_set_device_mode(AXP223_DEVICE_MODE_DATA); + if (ret) + return ret; + + return rsb_set_device_address(AXP223_DEVICE_ADDR, AXP223_RUNTIME_ADDR); +#endif +} + +static int pmic_bus_read(const u8 addr, u8 *data) +{ +#ifdef CONFIG_MACH_SUN6I + return p2wi_read(addr, data); +#else + return rsb_read(AXP223_RUNTIME_ADDR, addr, data); +#endif +} + +static int pmic_bus_write(const u8 addr, u8 data) +{ +#ifdef CONFIG_MACH_SUN6I + return p2wi_write(addr, data); +#else + return rsb_write(AXP223_RUNTIME_ADDR, addr, data); +#endif +} + static u8 axp221_mvolt_to_cfg(int mvolt, int min, int max, int div) { if (mvolt < min) @@ -24,12 +72,12 @@ static int axp221_setbits(u8 reg, u8 bits) int ret; u8 val;
- ret = p2wi_read(reg, &val); + ret = pmic_bus_read(reg, &val); if (ret) return ret;
val |= bits; - return p2wi_write(reg, val); + return pmic_bus_write(reg, val); }
int axp221_set_dcdc1(unsigned int mvolt) @@ -37,7 +85,7 @@ 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); + ret = pmic_bus_write(AXP221_DCDC1_CTRL, cfg); if (ret) return ret;
@@ -49,28 +97,28 @@ int axp221_set_dcdc2(unsigned int mvolt) { u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20);
- return p2wi_write(AXP221_DCDC2_CTRL, cfg); + return pmic_bus_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); + return pmic_bus_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); + return pmic_bus_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); + return pmic_bus_write(AXP221_DCDC5_CTRL, cfg); }
int axp221_set_dldo1(unsigned int mvolt) @@ -78,7 +126,7 @@ 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); + ret = pmic_bus_write(AXP221_DLDO1_CTRL, cfg); if (ret) return ret;
@@ -91,7 +139,7 @@ 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); + ret = pmic_bus_write(AXP221_DLDO2_CTRL, cfg); if (ret) return ret;
@@ -104,7 +152,7 @@ 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); + ret = pmic_bus_write(AXP221_DLDO3_CTRL, cfg); if (ret) return ret;
@@ -117,7 +165,7 @@ 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); + ret = pmic_bus_write(AXP221_DLDO4_CTRL, cfg); if (ret) return ret;
@@ -130,7 +178,7 @@ 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); + ret = pmic_bus_write(AXP221_ALDO1_CTRL, cfg); if (ret) return ret;
@@ -143,7 +191,7 @@ 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); + ret = pmic_bus_write(AXP221_ALDO2_CTRL, cfg); if (ret) return ret;
@@ -156,7 +204,7 @@ 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); + ret = pmic_bus_write(AXP221_ALDO3_CTRL, cfg); if (ret) return ret;
@@ -169,13 +217,11 @@ 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); + ret = pmic_bus_init(); if (ret) return ret;
- ret = p2wi_read(AXP221_CHIP_ID, &axp_chip_id); + ret = pmic_bus_read(AXP221_CHIP_ID, &axp_chip_id); if (ret) return ret;
@@ -194,17 +240,17 @@ int axp221_get_sid(unsigned int *sid) if (ret) return ret;
- ret = p2wi_write(AXP221_PAGE, 1); + ret = pmic_bus_write(AXP221_PAGE, 1); if (ret) return ret;
for (i = 0; i < 16; i++) { - ret = p2wi_read(AXP221_SID + i, &dest[i]); + ret = pmic_bus_read(AXP221_SID + i, &dest[i]); if (ret) return ret; }
- p2wi_write(AXP221_PAGE, 0); + pmic_bus_write(AXP221_PAGE, 0);
for (i = 0; i < 4; i++) sid[i] = be32_to_cpu(sid[i]); diff --git a/include/axp221.h b/include/axp221.h index db219c6..10d35e1 100644 --- a/include/axp221.h +++ b/include/axp221.h @@ -10,6 +10,10 @@ #define AXP221_CTRL_ADDR 0x3e #define AXP221_INIT_DATA 0x3e
+#define AXP223_DEVICE_ADDR 0x3a3 +#define AXP223_RUNTIME_ADDR 0x2d +#define AXP223_DEVICE_MODE_DATA 0x7c3e00 + /* Page 0 addresses */ #define AXP221_CHIP_ID 0x03 #define AXP221_OUTPUT_CTRL1 0x10

On Wed, Dec 17, 2014 at 4:31 AM, Hans de Goede hdegoede@redhat.com wrote:
The axp223 appears to be the same as the axp221, except that it uses the rsb to communicate rather then the p2wi. At least all the registers we use are 100% the same.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/cpu_info.c | 2 +- drivers/power/Kconfig | 8 ++-- drivers/power/axp221.c | 88 ++++++++++++++++++++++++++++--------- include/axp221.h | 4 ++ 4 files changed, 76 insertions(+), 26 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/cpu_info.c b/arch/arm/cpu/armv7/sunxi/cpu_info.c index 7a3a4ca..b6cb9de 100644 --- a/arch/arm/cpu/armv7/sunxi/cpu_info.c +++ b/arch/arm/cpu/armv7/sunxi/cpu_info.c @@ -76,7 +76,7 @@ int print_cpuinfo(void)
int sunxi_get_sid(unsigned int *sid) { -#ifdef CONFIG_MACH_SUN6I +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I #ifdef CONFIG_AXP221_POWER return axp221_get_sid(sid); #else diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 1ec7c0e..7373a79 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -1,10 +1,10 @@ config AXP221_POWER
boolean "axp221 pmic support"
depends on MACH_SUN6I
boolean "axp221 / axp223 pmic support"
depends on MACH_SUN6I || MACH_SUN8I default y ---help---
Say y here to enable support for the axp221 pmic found on most sun6i
(A31) boards.
Say y here to enable support for the axp221 / axp223 pmic found on most
sun6i (A31) / sun8i (A23) boards.
config AXP221_DLDO1_VOLT int "axp221 dldo1 voltage" diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 826567a..717adad 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -1,4 +1,10 @@ /*
- AXP221 and AXP223 driver
- IMPORTANT when making changes to this file check that the registers
- used are the same for the axp221 and axp223.
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- (C) Copyright 2013 Oliver Schinagl oliver@schinagl.nl
- SPDX-License-Identifier: GPL-2.0+
@@ -7,8 +13,50 @@ #include <common.h> #include <errno.h> #include <asm/arch/p2wi.h> +#include <asm/arch/rsb.h> #include <axp221.h>
+/*
- The axp221 uses the p2wi bus, the axp223 is identical (for all registers
- used sofar) but uses the rsb bus. These functions abstract this.
- */
+static int pmic_bus_init(void) +{ +#ifdef CONFIG_MACH_SUN6I
p2wi_init();
return p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR,
AXP221_INIT_DATA);
+#else
int ret;
rsb_init();
ret = rsb_set_device_mode(AXP223_DEVICE_MODE_DATA);
if (ret)
return ret;
return rsb_set_device_address(AXP223_DEVICE_ADDR, AXP223_RUNTIME_ADDR);
+#endif +}
+static int pmic_bus_read(const u8 addr, u8 *data) +{ +#ifdef CONFIG_MACH_SUN6I
return p2wi_read(addr, data);
+#else
return rsb_read(AXP223_RUNTIME_ADDR, addr, data);
+#endif +}
+static int pmic_bus_write(const u8 addr, u8 data) +{ +#ifdef CONFIG_MACH_SUN6I
return p2wi_write(addr, data);
+#else
return rsb_write(AXP223_RUNTIME_ADDR, addr, data);
+#endif +}
static u8 axp221_mvolt_to_cfg(int mvolt, int min, int max, int div) { if (mvolt < min) @@ -24,12 +72,12 @@ static int axp221_setbits(u8 reg, u8 bits) int ret; u8 val;
ret = p2wi_read(reg, &val);
ret = pmic_bus_read(reg, &val); if (ret) return ret; val |= bits;
return p2wi_write(reg, val);
return pmic_bus_write(reg, val);
}
int axp221_set_dcdc1(unsigned int mvolt) @@ -37,7 +85,7 @@ 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);
ret = pmic_bus_write(AXP221_DCDC1_CTRL, cfg); if (ret) return ret;
@@ -49,28 +97,28 @@ int axp221_set_dcdc2(unsigned int mvolt) { u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20);
return p2wi_write(AXP221_DCDC2_CTRL, cfg);
return pmic_bus_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);
return pmic_bus_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);
return pmic_bus_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);
return pmic_bus_write(AXP221_DCDC5_CTRL, cfg);
}
int axp221_set_dldo1(unsigned int mvolt) @@ -78,7 +126,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO1_CTRL, cfg); if (ret) return ret;
@@ -91,7 +139,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO2_CTRL, cfg); if (ret) return ret;
@@ -104,7 +152,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO3_CTRL, cfg); if (ret) return ret;
@@ -117,7 +165,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO4_CTRL, cfg); if (ret) return ret;
@@ -130,7 +178,7 @@ 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);
ret = pmic_bus_write(AXP221_ALDO1_CTRL, cfg); if (ret) return ret;
@@ -143,7 +191,7 @@ 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);
ret = pmic_bus_write(AXP221_ALDO2_CTRL, cfg); if (ret) return ret;
@@ -156,7 +204,7 @@ 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);
ret = pmic_bus_write(AXP221_ALDO3_CTRL, cfg); if (ret) return ret;
@@ -169,13 +217,11 @@ 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);
ret = pmic_bus_init(); if (ret) return ret;
ret = p2wi_read(AXP221_CHIP_ID, &axp_chip_id);
ret = pmic_bus_read(AXP221_CHIP_ID, &axp_chip_id); if (ret) return ret;
@@ -194,17 +240,17 @@ int axp221_get_sid(unsigned int *sid) if (ret) return ret;
ret = p2wi_write(AXP221_PAGE, 1);
ret = pmic_bus_write(AXP221_PAGE, 1); if (ret) return ret; for (i = 0; i < 16; i++) {
ret = p2wi_read(AXP221_SID + i, &dest[i]);
ret = pmic_bus_read(AXP221_SID + i, &dest[i]); if (ret) return ret; }
p2wi_write(AXP221_PAGE, 0);
pmic_bus_write(AXP221_PAGE, 0); for (i = 0; i < 4; i++) sid[i] = be32_to_cpu(sid[i]);
diff --git a/include/axp221.h b/include/axp221.h index db219c6..10d35e1 100644 --- a/include/axp221.h +++ b/include/axp221.h @@ -10,6 +10,10 @@ #define AXP221_CTRL_ADDR 0x3e #define AXP221_INIT_DATA 0x3e
+#define AXP223_DEVICE_ADDR 0x3a3 +#define AXP223_RUNTIME_ADDR 0x2d +#define AXP223_DEVICE_MODE_DATA 0x7c3e00
/* Page 0 addresses */ #define AXP221_CHIP_ID 0x03
#define AXP221_OUTPUT_CTRL1 0x10
2.1.0
git am reports:
Applying patch #422068 using 'git am' Description: [U-Boot,03/14] sunxi: axp221: Add axp223 support Applying: sunxi: axp221: Add axp223 support /home/wens/sunxi/u-boot/.git/rebase-apply/patch:50: trailing whitespace. * /home/wens/sunxi/u-boot/.git/rebase-apply/patch:76: trailing whitespace.
warning: 2 lines add whitespace errors.
ChenYu

Hi,
On 17-12-14 03:34, Chen-Yu Tsai wrote:
On Wed, Dec 17, 2014 at 4:31 AM, Hans de Goede hdegoede@redhat.com wrote:
The axp223 appears to be the same as the axp221, except that it uses the rsb to communicate rather then the p2wi. At least all the registers we use are 100% the same.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/cpu_info.c | 2 +- drivers/power/Kconfig | 8 ++-- drivers/power/axp221.c | 88 ++++++++++++++++++++++++++++--------- include/axp221.h | 4 ++ 4 files changed, 76 insertions(+), 26 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/cpu_info.c b/arch/arm/cpu/armv7/sunxi/cpu_info.c index 7a3a4ca..b6cb9de 100644 --- a/arch/arm/cpu/armv7/sunxi/cpu_info.c +++ b/arch/arm/cpu/armv7/sunxi/cpu_info.c @@ -76,7 +76,7 @@ int print_cpuinfo(void)
int sunxi_get_sid(unsigned int *sid) { -#ifdef CONFIG_MACH_SUN6I +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I #ifdef CONFIG_AXP221_POWER return axp221_get_sid(sid); #else diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 1ec7c0e..7373a79 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -1,10 +1,10 @@ config AXP221_POWER
boolean "axp221 pmic support"
depends on MACH_SUN6I
boolean "axp221 / axp223 pmic support"
depends on MACH_SUN6I || MACH_SUN8I default y ---help---
Say y here to enable support for the axp221 pmic found on most sun6i
(A31) boards.
Say y here to enable support for the axp221 / axp223 pmic found on most
sun6i (A31) / sun8i (A23) boards.
config AXP221_DLDO1_VOLT int "axp221 dldo1 voltage"
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 826567a..717adad 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -1,4 +1,10 @@ /*
- AXP221 and AXP223 driver
- IMPORTANT when making changes to this file check that the registers
- used are the same for the axp221 and axp223.
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- (C) Copyright 2013 Oliver Schinagl oliver@schinagl.nl
- SPDX-License-Identifier: GPL-2.0+
@@ -7,8 +13,50 @@ #include <common.h> #include <errno.h> #include <asm/arch/p2wi.h> +#include <asm/arch/rsb.h> #include <axp221.h>
+/*
- The axp221 uses the p2wi bus, the axp223 is identical (for all registers
- used sofar) but uses the rsb bus. These functions abstract this.
- */
+static int pmic_bus_init(void) +{ +#ifdef CONFIG_MACH_SUN6I
p2wi_init();
return p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR,
AXP221_INIT_DATA);
+#else
int ret;
rsb_init();
ret = rsb_set_device_mode(AXP223_DEVICE_MODE_DATA);
if (ret)
return ret;
return rsb_set_device_address(AXP223_DEVICE_ADDR, AXP223_RUNTIME_ADDR);
+#endif +}
+static int pmic_bus_read(const u8 addr, u8 *data) +{ +#ifdef CONFIG_MACH_SUN6I
return p2wi_read(addr, data);
+#else
return rsb_read(AXP223_RUNTIME_ADDR, addr, data);
+#endif +}
+static int pmic_bus_write(const u8 addr, u8 data) +{ +#ifdef CONFIG_MACH_SUN6I
return p2wi_write(addr, data);
+#else
return rsb_write(AXP223_RUNTIME_ADDR, addr, data);
+#endif +}
- static u8 axp221_mvolt_to_cfg(int mvolt, int min, int max, int div) { if (mvolt < min)
@@ -24,12 +72,12 @@ static int axp221_setbits(u8 reg, u8 bits) int ret; u8 val;
ret = p2wi_read(reg, &val);
ret = pmic_bus_read(reg, &val); if (ret) return ret; val |= bits;
return p2wi_write(reg, val);
return pmic_bus_write(reg, val);
}
int axp221_set_dcdc1(unsigned int mvolt)
@@ -37,7 +85,7 @@ 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);
ret = pmic_bus_write(AXP221_DCDC1_CTRL, cfg); if (ret) return ret;
@@ -49,28 +97,28 @@ int axp221_set_dcdc2(unsigned int mvolt) { u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20);
return p2wi_write(AXP221_DCDC2_CTRL, cfg);
return pmic_bus_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);
return pmic_bus_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);
return pmic_bus_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);
return pmic_bus_write(AXP221_DCDC5_CTRL, cfg);
}
int axp221_set_dldo1(unsigned int mvolt)
@@ -78,7 +126,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO1_CTRL, cfg); if (ret) return ret;
@@ -91,7 +139,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO2_CTRL, cfg); if (ret) return ret;
@@ -104,7 +152,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO3_CTRL, cfg); if (ret) return ret;
@@ -117,7 +165,7 @@ 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);
ret = pmic_bus_write(AXP221_DLDO4_CTRL, cfg); if (ret) return ret;
@@ -130,7 +178,7 @@ 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);
ret = pmic_bus_write(AXP221_ALDO1_CTRL, cfg); if (ret) return ret;
@@ -143,7 +191,7 @@ 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);
ret = pmic_bus_write(AXP221_ALDO2_CTRL, cfg); if (ret) return ret;
@@ -156,7 +204,7 @@ 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);
ret = pmic_bus_write(AXP221_ALDO3_CTRL, cfg); if (ret) return ret;
@@ -169,13 +217,11 @@ 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);
ret = pmic_bus_init(); if (ret) return ret;
ret = p2wi_read(AXP221_CHIP_ID, &axp_chip_id);
ret = pmic_bus_read(AXP221_CHIP_ID, &axp_chip_id); if (ret) return ret;
@@ -194,17 +240,17 @@ int axp221_get_sid(unsigned int *sid) if (ret) return ret;
ret = p2wi_write(AXP221_PAGE, 1);
ret = pmic_bus_write(AXP221_PAGE, 1); if (ret) return ret; for (i = 0; i < 16; i++) {
ret = p2wi_read(AXP221_SID + i, &dest[i]);
ret = pmic_bus_read(AXP221_SID + i, &dest[i]); if (ret) return ret; }
p2wi_write(AXP221_PAGE, 0);
pmic_bus_write(AXP221_PAGE, 0); for (i = 0; i < 4; i++) sid[i] = be32_to_cpu(sid[i]);
diff --git a/include/axp221.h b/include/axp221.h index db219c6..10d35e1 100644 --- a/include/axp221.h +++ b/include/axp221.h @@ -10,6 +10,10 @@ #define AXP221_CTRL_ADDR 0x3e #define AXP221_INIT_DATA 0x3e
+#define AXP223_DEVICE_ADDR 0x3a3 +#define AXP223_RUNTIME_ADDR 0x2d +#define AXP223_DEVICE_MODE_DATA 0x7c3e00
- /* Page 0 addresses */ #define AXP221_CHIP_ID 0x03 #define AXP221_OUTPUT_CTRL1 0x10
-- 2.1.0
git am reports:
Applying patch #422068 using 'git am' Description: [U-Boot,03/14] sunxi: axp221: Add axp223 support Applying: sunxi: axp221: Add axp223 support /home/wens/sunxi/u-boot/.git/rebase-apply/patch:50: trailing whitespace.
/home/wens/sunxi/u-boot/.git/rebase-apply/patch:76: trailing whitespace.
warning: 2 lines add whitespace errors.
Fixed in my personal tree.
Regards,
Hans

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The axp223 appears to be the same as the axp221, except that it uses the rsb to communicate rather then the p2wi. At least all the registers we use are 100% the same.
Signed-off-by: Hans de Goede hdegoede@redhat.com
With the whitespace fixed: Acked-by: Ian Campbell ijc@hellion.org.uk

Some of the ldo-s of the axp221 are used in the same way on most boards, add comments to the Kconfig help text to reflect this, and give them defaults matching their typical usage.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- configs/CSQ_CS908_defconfig | 3 --- configs/Colombus_defconfig | 3 --- configs/Hummingbird_A31_defconfig | 4 ---- configs/Mele_M9_defconfig | 3 --- drivers/power/Kconfig | 20 ++++++++++++++------ 5 files changed, 14 insertions(+), 19 deletions(-)
diff --git a/configs/CSQ_CS908_defconfig b/configs/CSQ_CS908_defconfig index 4c9cff6..1b6cdbf 100644 --- a/configs/CSQ_CS908_defconfig +++ b/configs/CSQ_CS908_defconfig @@ -11,9 +11,6 @@ CONFIG_FDTFILE="sun6i-a31s-cs908.dtb" +S:CONFIG_AXP221_DLDO1_VOLT=3300 # Wifi power +S:CONFIG_AXP221_ALDO1_VOLT=3300 -# HDMI power ? -+S:CONFIG_AXP221_ALDO2_VOLT=1800 -+S:CONFIG_AXP221_ALDO3_VOLT=3000 # No Vbus gpio for either usb +S:CONFIG_USB1_VBUS_PIN="" +S:CONFIG_USB2_VBUS_PIN="" diff --git a/configs/Colombus_defconfig b/configs/Colombus_defconfig index b8c5400..f42ae52 100644 --- a/configs/Colombus_defconfig +++ b/configs/Colombus_defconfig @@ -9,8 +9,5 @@ CONFIG_FDTFILE="sun6i-a31-colombus.dtb" +S:CONFIG_DRAM_ZQ=251 # Wifi power +S:CONFIG_AXP221_ALDO1_VOLT=3300 -# 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/configs/Hummingbird_A31_defconfig b/configs/Hummingbird_A31_defconfig index 73855c5..7f59a0d 100644 --- a/configs/Hummingbird_A31_defconfig +++ b/configs/Hummingbird_A31_defconfig @@ -9,10 +9,6 @@ CONFIG_FDTFILE="sun6i-a31-hummingbird.dtb" +S:CONFIG_DRAM_ZQ=251 # Wifi power +S:CONFIG_AXP221_ALDO1_VOLT=3300 -# PM-CPUS GPIO power -+S:CONFIG_AXP221_ALDO2_VOLT=1800 -# SoC IR, PLL and Analog power (must be 3.0V) -+S:CONFIG_AXP221_ALDO3_VOLT=3000 # Vbus gpio for usb1 +S:CONFIG_USB1_VBUS_PIN="PH24" # No Vbus gpio for usb2 diff --git a/configs/Mele_M9_defconfig b/configs/Mele_M9_defconfig index a598254..445cc57 100644 --- a/configs/Mele_M9_defconfig +++ b/configs/Mele_M9_defconfig @@ -13,9 +13,6 @@ CONFIG_FDTFILE="sun6i-a31-m9.dtb" +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 # Vbus gpio for usb1 +S:CONFIG_USB1_VBUS_PIN="PC27" # No Vbus gpio for usb2 diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 7373a79..af66887 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -12,7 +12,9 @@ config AXP221_DLDO1_VOLT default -1 ---help--- Set the voltage (mV) to program the axp221 dldo1 at, set to -1 to - disable dldo1. + disable dldo1. On sun6i (A31) boards with ethernet this is often used + to power the ethernet phy. On sun8i (A23) boards this is often used to + power the wifi.
config AXP221_DLDO4_VOLT int "axp221 dldo4 voltage" @@ -28,20 +30,26 @@ config AXP221_ALDO1_VOLT default -1 ---help--- Set the voltage (mV) to program the axp221 aldo1 at, set to -1 to - disable aldo1. + disable aldo1. On sun6i (A31) boards which have a wifi module this is + often used to power the wifi module.
config AXP221_ALDO2_VOLT int "axp221 aldo2 voltage" depends on AXP221_POWER - default -1 + default 1800 if MACH_SUN6I + default 2500 if MACH_SUN8I ---help--- Set the voltage (mV) to program the axp221 aldo2 at, set to -1 to - disable aldo2. + disable aldo2. On sun6i (A31) boards this is typically connected to + VCC-PM, which powers the port M gpios, and should be set to 1.8V. + On sun8i (A23) this is typically connected to VDD-DLL and must be + set to 2.5V.
config AXP221_ALDO3_VOLT int "axp221 aldo3 voltage" depends on AXP221_POWER - default -1 + default 3000 ---help--- Set the voltage (mV) to program the axp221 aldo3 at, set to -1 to - disable aldo3. + disable aldo3. This is typically connected to VCC-PLL and AVCC and + must be set to 3V.

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
Some of the ldo-s of the axp221 are used in the same way on most boards, add comments to the Kconfig help text to reflect this, and give them defaults matching their typical usage.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

The dcdc1 voltage is typically used as generic 3.3V IO voltage for things like GPIO-s, sdcard interfaces, etc. On most boards this is undervolted to 3.0V to safe battery, but not on all, make it configurable so that we can use the same settings as the original firmware on all boards.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- board/sunxi/board.c | 2 +- configs/Mele_M9_defconfig | 2 ++ drivers/power/Kconfig | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/board/sunxi/board.c b/board/sunxi/board.c index b5dfe95..e2ebf83 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -175,7 +175,7 @@ void sunxi_board_init(void) #endif #ifdef CONFIG_AXP221_POWER power_failed = axp221_init(); - power_failed |= axp221_set_dcdc1(3000); + power_failed |= axp221_set_dcdc1(CONFIG_AXP221_DCDC1_VOLT); power_failed |= axp221_set_dcdc2(1200); power_failed |= axp221_set_dcdc3(1200); power_failed |= axp221_set_dcdc4(1200); diff --git a/configs/Mele_M9_defconfig b/configs/Mele_M9_defconfig index 445cc57..e5ab0ec 100644 --- a/configs/Mele_M9_defconfig +++ b/configs/Mele_M9_defconfig @@ -7,6 +7,8 @@ CONFIG_FDTFILE="sun6i-a31-m9.dtb" +S:CONFIG_TARGET_MELE_M9=y +S:CONFIG_DRAM_CLK=312 +S:CONFIG_DRAM_ZQ=120 +# The Mele M9 uses 3.3V for general IO ++S:CONFIG_AXP221_DCDC1_VOLT=3300 # Ethernet phy power +S:CONFIG_AXP221_DLDO1_VOLT=3300 # USB hub power diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index af66887..e132759 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -6,6 +6,16 @@ config AXP221_POWER Say y here to enable support for the axp221 / axp223 pmic found on most sun6i (A31) / sun8i (A23) boards.
+config AXP221_DCDC1_VOLT + int "axp221 dcdc1 voltage" + depends on AXP221_POWER + default 3000 + ---help--- + Set the voltage (mV) to program the axp221 dcdc1 at, set to 0 to + disable dcdc1. This is typically used as generic 3.3V IO voltage for + things like GPIO-s, sdcard interfaces, etc. On most boards this is + undervolted to 3.0V to safe battery. + config AXP221_DLDO1_VOLT int "axp221 dldo1 voltage" depends on AXP221_POWER

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The dcdc1 voltage is typically used as generic 3.3V IO voltage for things like GPIO-s, sdcard interfaces, etc. On most boards this is undervolted to 3.0V to safe battery, but not on all, make it configurable so that we can use the same settings as the original firmware on all boards.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

Explicitly turn off unused voltages, rather then leaving them as is. Likewise explictly enabled the dcdc convertors, rather then assuming they are already enabled at boot.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- board/sunxi/board.c | 10 ----- drivers/power/Kconfig | 16 ++++---- drivers/power/axp221.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++--- include/axp221.h | 8 +++- 4 files changed, 111 insertions(+), 25 deletions(-)
diff --git a/board/sunxi/board.c b/board/sunxi/board.c index e2ebf83..5bf19b7 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -180,22 +180,12 @@ void sunxi_board_init(void) 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 e132759..ef0c093 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -19,9 +19,9 @@ config AXP221_DCDC1_VOLT config AXP221_DLDO1_VOLT int "axp221 dldo1 voltage" depends on AXP221_POWER - default -1 + default 0 ---help--- - Set the voltage (mV) to program the axp221 dldo1 at, set to -1 to + Set the voltage (mV) to program the axp221 dldo1 at, set to 0 to disable dldo1. On sun6i (A31) boards with ethernet this is often used to power the ethernet phy. On sun8i (A23) boards this is often used to power the wifi. @@ -29,17 +29,17 @@ config AXP221_DLDO1_VOLT config AXP221_DLDO4_VOLT int "axp221 dldo4 voltage" depends on AXP221_POWER - default -1 + default 0 ---help--- - Set the voltage (mV) to program the axp221 dldo4 at, set to -1 to + Set the voltage (mV) to program the axp221 dldo4 at, set to 0 to disable dldo4.
config AXP221_ALDO1_VOLT int "axp221 aldo1 voltage" depends on AXP221_POWER - default -1 + default 0 ---help--- - Set the voltage (mV) to program the axp221 aldo1 at, set to -1 to + Set the voltage (mV) to program the axp221 aldo1 at, set to 0 to disable aldo1. On sun6i (A31) boards which have a wifi module this is often used to power the wifi module.
@@ -49,7 +49,7 @@ config AXP221_ALDO2_VOLT default 1800 if MACH_SUN6I default 2500 if MACH_SUN8I ---help--- - Set the voltage (mV) to program the axp221 aldo2 at, set to -1 to + Set the voltage (mV) to program the axp221 aldo2 at, set to 0 to disable aldo2. On sun6i (A31) boards this is typically connected to VCC-PM, which powers the port M gpios, and should be set to 1.8V. On sun8i (A23) this is typically connected to VDD-DLL and must be @@ -60,6 +60,6 @@ config AXP221_ALDO3_VOLT depends on AXP221_POWER default 3000 ---help--- - Set the voltage (mV) to program the axp221 aldo3 at, set to -1 to + Set the voltage (mV) to program the axp221 aldo3 at, set to 0 to disable aldo3. This is typically connected to VCC-PLL and AVCC and must be set to 3V. diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 717adad..c438849 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -80,45 +80,107 @@ static int axp221_setbits(u8 reg, u8 bits) return pmic_bus_write(reg, val); }
+static int axp221_clrbits(u8 reg, u8 bits) +{ + int ret; + u8 val; + + ret = pmic_bus_read(reg, &val); + if (ret) + return ret; + + val &= ~bits; + return pmic_bus_write(reg, val); +} + int axp221_set_dcdc1(unsigned int mvolt) { int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 1600, 3400, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC1_EN); + ret = pmic_bus_write(AXP221_DCDC1_CTRL, cfg); if (ret) return ret;
- return axp221_setbits(AXP221_OUTPUT_CTRL2, - AXP221_OUTPUT_CTRL2_DCDC1_EN); + ret = axp221_setbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DCDC1SW_EN); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC1_EN); }
int axp221_set_dcdc2(unsigned int mvolt) { + int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20);
- return pmic_bus_write(AXP221_DCDC2_CTRL, cfg); + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC2_EN); + + ret = pmic_bus_write(AXP221_DCDC2_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC2_EN); }
int axp221_set_dcdc3(unsigned int mvolt) { + int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1860, 20);
- return pmic_bus_write(AXP221_DCDC3_CTRL, cfg); + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC3_EN); + + ret = pmic_bus_write(AXP221_DCDC3_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC3_EN); }
int axp221_set_dcdc4(unsigned int mvolt) { + int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20);
- return pmic_bus_write(AXP221_DCDC4_CTRL, cfg); + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC4_EN); + + ret = pmic_bus_write(AXP221_DCDC4_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC4_EN); }
int axp221_set_dcdc5(unsigned int mvolt) { + int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 1000, 2550, 50);
- return pmic_bus_write(AXP221_DCDC5_CTRL, cfg); + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC5_EN); + + ret = pmic_bus_write(AXP221_DCDC5_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC5_EN); }
int axp221_set_dldo1(unsigned int mvolt) @@ -126,6 +188,10 @@ int axp221_set_dldo1(unsigned int mvolt) int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO1_EN); + ret = pmic_bus_write(AXP221_DLDO1_CTRL, cfg); if (ret) return ret; @@ -139,6 +205,10 @@ int axp221_set_dldo2(unsigned int mvolt) int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO2_EN); + ret = pmic_bus_write(AXP221_DLDO2_CTRL, cfg); if (ret) return ret; @@ -152,6 +222,10 @@ int axp221_set_dldo3(unsigned int mvolt) int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO3_EN); + ret = pmic_bus_write(AXP221_DLDO3_CTRL, cfg); if (ret) return ret; @@ -165,6 +239,10 @@ int axp221_set_dldo4(unsigned int mvolt) int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO4_EN); + ret = pmic_bus_write(AXP221_DLDO4_CTRL, cfg); if (ret) return ret; @@ -178,6 +256,10 @@ int axp221_set_aldo1(unsigned int mvolt) int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_ALDO1_EN); + ret = pmic_bus_write(AXP221_ALDO1_CTRL, cfg); if (ret) return ret; @@ -191,6 +273,10 @@ int axp221_set_aldo2(unsigned int mvolt) int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_ALDO2_EN); + ret = pmic_bus_write(AXP221_ALDO2_CTRL, cfg); if (ret) return ret; @@ -204,6 +290,10 @@ int axp221_set_aldo3(unsigned int mvolt) int ret; u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
+ if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL3, + AXP221_OUTPUT_CTRL3_ALDO3_EN); + ret = pmic_bus_write(AXP221_ALDO3_CTRL, cfg); if (ret) return ret; diff --git a/include/axp221.h b/include/axp221.h index 10d35e1..a6e52e3 100644 --- a/include/axp221.h +++ b/include/axp221.h @@ -17,6 +17,12 @@ /* Page 0 addresses */ #define AXP221_CHIP_ID 0x03 #define AXP221_OUTPUT_CTRL1 0x10 +#define AXP221_OUTPUT_CTRL1_DCDC0_EN (1 << 0) +#define AXP221_OUTPUT_CTRL1_DCDC1_EN (1 << 1) +#define AXP221_OUTPUT_CTRL1_DCDC2_EN (1 << 2) +#define AXP221_OUTPUT_CTRL1_DCDC3_EN (1 << 3) +#define AXP221_OUTPUT_CTRL1_DCDC4_EN (1 << 4) +#define AXP221_OUTPUT_CTRL1_DCDC5_EN (1 << 5) #define AXP221_OUTPUT_CTRL1_ALDO1_EN (1 << 6) #define AXP221_OUTPUT_CTRL1_ALDO2_EN (1 << 7) #define AXP221_OUTPUT_CTRL2 0x12 @@ -24,7 +30,7 @@ #define AXP221_OUTPUT_CTRL2_DLDO2_EN (1 << 4) #define AXP221_OUTPUT_CTRL2_DLDO3_EN (1 << 5) #define AXP221_OUTPUT_CTRL2_DLDO4_EN (1 << 6) -#define AXP221_OUTPUT_CTRL2_DCDC1_EN (1 << 7) +#define AXP221_OUTPUT_CTRL2_DCDC1SW_EN (1 << 7) #define AXP221_OUTPUT_CTRL3 0x13 #define AXP221_OUTPUT_CTRL3_ALDO3_EN (1 << 7) #define AXP221_DLDO1_CTRL 0x15

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
Explicitly turn off unused voltages, rather then leaving them as is. Likewise explictly enabled the dcdc convertors, rather then assuming they are already enabled at boot.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

dcdc4 is not used on sun8i, disable it.
While at it also add comments to the other fixed voltages to document what they are used for.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- board/sunxi/board.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 5bf19b7..7d6d075 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -176,10 +176,14 @@ void sunxi_board_init(void) #ifdef CONFIG_AXP221_POWER power_failed = axp221_init(); power_failed |= axp221_set_dcdc1(CONFIG_AXP221_DCDC1_VOLT); - power_failed |= axp221_set_dcdc2(1200); - power_failed |= axp221_set_dcdc3(1200); - power_failed |= axp221_set_dcdc4(1200); - power_failed |= axp221_set_dcdc5(1500); + power_failed |= axp221_set_dcdc2(1200); /* A31:VDD-GPU, A23:VDD-SYS */ + power_failed |= axp221_set_dcdc3(1200); /* VDD-CPU */ +#ifdef CONFIG_MACH_SUN6I + power_failed |= axp221_set_dcdc4(1200); /* A31:VDD-SYS */ +#else + power_failed |= axp221_set_dcdc4(0); /* A23:unused */ +#endif + power_failed |= axp221_set_dcdc5(1500); /* VCC-DRAM */ power_failed |= axp221_set_dldo1(CONFIG_AXP221_DLDO1_VOLT); power_failed |= axp221_set_dldo4(CONFIG_AXP221_DLDO4_VOLT); power_failed |= axp221_set_aldo1(CONFIG_AXP221_ALDO1_VOLT);

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
dcdc4 is not used on sun8i, disable it.
While at it also add comments to the other fixed voltages to document what they are used for.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
(although I'm wondering if it might have been better to make this Kconfigurable...)

The sun8i dram code sometimes wants to enable sigma delta mode, add a parameter to allow this.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 9 +++++++-- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 2 +- arch/arm/include/asm/arch-sunxi/clock.h | 1 - arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 5 +++++ 4 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 8e949c6..193e314 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -144,15 +144,20 @@ void clock_set_pll3(unsigned int clk) &ccm->pll3_cfg); }
-void clock_set_pll5(unsigned int clk) +void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int k = 2; const int m = 1;
+ if (sigma_delta_enable) + writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg); + /* PLL5 rate = 24000000 * n * k / m */ - writel(CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD | + writel(CCM_PLL5_CTRL_EN | + (sigma_delta_enable ? CCM_PLL5_CTRL_SIGMA_DELTA_EN : 0) | + CCM_PLL5_CTRL_UPD | CCM_PLL5_CTRL_N(clk / (24000000 * k / m)) | CCM_PLL5_CTRL_K(k) | CCM_PLL5_CTRL_M(m), &ccm->pll5_cfg);
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index 61bb8d4..bc6428a 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -46,7 +46,7 @@ static void mctl_sys_init(void) (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int dram_clk_div = 2;
- clock_set_pll5(DRAM_CLK * dram_clk_div); + clock_set_pll5(DRAM_CLK * dram_clk_div, false);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST | diff --git a/arch/arm/include/asm/arch-sunxi/clock.h b/arch/arm/include/asm/arch-sunxi/clock.h index 64acff3..505c363 100644 --- a/arch/arm/include/asm/arch-sunxi/clock.h +++ b/arch/arm/include/asm/arch-sunxi/clock.h @@ -26,7 +26,6 @@ int clock_init(void); int clock_twi_onoff(int port, int state); void clock_set_pll1(unsigned int hz); void clock_set_pll3(unsigned int hz); -void clock_set_pll5(unsigned int hz); unsigned int clock_get_pll5p(void); unsigned int clock_get_pll6(void); void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz); diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 3d4fcd1..f807af3 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -185,6 +185,7 @@ struct sunxi_ccm_reg { #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_SIGMA_DELTA_EN (0x1 << 24) #define CCM_PLL5_CTRL_EN (0x1 << 31)
#define PLL6_CFG_DEFAULT 0x90041811 /* 600 MHz */ @@ -274,6 +275,8 @@ struct sunxi_ccm_reg {
#define MBUS_CLK_DEFAULT 0x81000001 /* PLL6 / 2 */
+#define CCM_PLL5_PATTERN 0xd1303333 + /* ahb_reset0 offsets */ #define AHB_RESET_OFFSET_GMAC 17 #define AHB_RESET_OFFSET_MCTL 14 @@ -308,4 +311,6 @@ struct sunxi_ccm_reg { #define CCM_DE_CTRL_PLL10 (5 << 24) #define CCM_DE_CTRL_GATE (1 << 31)
+void clock_set_pll5(unsigned int clk, bool sigma_delta_enable); + #endif /* _SUNXI_CLOCK_SUN6I_H */

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i dram code sometimes wants to enable sigma delta mode, add a parameter to allow this.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

On Tue, 16 Dec 2014 21:31:33 +0100 Hans de Goede hdegoede@redhat.com wrote:
The sun8i dram code sometimes wants to enable sigma delta mode, add a parameter to allow this.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 9 +++++++-- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 2 +- arch/arm/include/asm/arch-sunxi/clock.h | 1 - arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 5 +++++ 4 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 8e949c6..193e314 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -144,15 +144,20 @@ void clock_set_pll3(unsigned int clk) &ccm->pll3_cfg); }
-void clock_set_pll5(unsigned int clk) +void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int k = 2; const int m = 1;
- if (sigma_delta_enable)
writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg);
- /* PLL5 rate = 24000000 * n * k / m */
- writel(CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD |
- writel(CCM_PLL5_CTRL_EN |
(sigma_delta_enable ? CCM_PLL5_CTRL_SIGMA_DELTA_EN : 0) |
CCM_PLL5_CTRL_UPD | CCM_PLL5_CTRL_N(clk / (24000000 * k / m)) | CCM_PLL5_CTRL_K(k) | CCM_PLL5_CTRL_M(m), &ccm->pll5_cfg);
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index 61bb8d4..bc6428a 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -46,7 +46,7 @@ static void mctl_sys_init(void) (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int dram_clk_div = 2;
- clock_set_pll5(DRAM_CLK * dram_clk_div);
- clock_set_pll5(DRAM_CLK * dram_clk_div, false);
Would it make the code a bit more readable to have a named constant here (with SIGMA_DELTA as part of its name) instead of just true/false?
The "foobar(true, true, false, true)" style is not exactly informative.
I'm not insisting on this though.
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST | diff --git a/arch/arm/include/asm/arch-sunxi/clock.h b/arch/arm/include/asm/arch-sunxi/clock.h index 64acff3..505c363 100644 --- a/arch/arm/include/asm/arch-sunxi/clock.h +++ b/arch/arm/include/asm/arch-sunxi/clock.h @@ -26,7 +26,6 @@ int clock_init(void); int clock_twi_onoff(int port, int state); void clock_set_pll1(unsigned int hz); void clock_set_pll3(unsigned int hz); -void clock_set_pll5(unsigned int hz); unsigned int clock_get_pll5p(void); unsigned int clock_get_pll6(void); void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz); diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 3d4fcd1..f807af3 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -185,6 +185,7 @@ struct sunxi_ccm_reg { #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_SIGMA_DELTA_EN (0x1 << 24) #define CCM_PLL5_CTRL_EN (0x1 << 31)
#define PLL6_CFG_DEFAULT 0x90041811 /* 600 MHz */ @@ -274,6 +275,8 @@ struct sunxi_ccm_reg {
#define MBUS_CLK_DEFAULT 0x81000001 /* PLL6 / 2 */
+#define CCM_PLL5_PATTERN 0xd1303333
/* ahb_reset0 offsets */ #define AHB_RESET_OFFSET_GMAC 17 #define AHB_RESET_OFFSET_MCTL 14 @@ -308,4 +311,6 @@ struct sunxi_ccm_reg { #define CCM_DE_CTRL_PLL10 (5 << 24) #define CCM_DE_CTRL_GATE (1 << 31)
+void clock_set_pll5(unsigned int clk, bool sigma_delta_enable);
#endif /* _SUNXI_CLOCK_SUN6I_H */

The A23 (sun8i) requires different values for these then sun6i, so make them function parameters.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 4 +--- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 193e314..8ef19df 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -144,12 +144,10 @@ void clock_set_pll3(unsigned int clk) &ccm->pll3_cfg); }
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - const int k = 2; - const int m = 1;
if (sigma_delta_enable) writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg); diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index bc6428a..a8bbdfd 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -46,7 +46,7 @@ static void mctl_sys_init(void) (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int dram_clk_div = 2;
- clock_set_pll5(DRAM_CLK * dram_clk_div, false); + clock_set_pll5(DRAM_CLK * dram_clk_div, 2, 1, false);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST | diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index f807af3..7d61216 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -311,6 +311,6 @@ struct sunxi_ccm_reg { #define CCM_DE_CTRL_PLL10 (5 << 24) #define CCM_DE_CTRL_GATE (1 << 31)
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable); +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable);
#endif /* _SUNXI_CLOCK_SUN6I_H */

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The A23 (sun8i) requires different values for these then sun6i, so make them function parameters.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

On Tue, 16 Dec 2014 21:31:34 +0100 Hans de Goede hdegoede@redhat.com wrote:
The A23 (sun8i) requires different values for these then sun6i, so make them function parameters.
Signed-off-by: Hans de Goede hdegoede@redhat.com
What happens if A23 does not get these special k and m parameters, but the 'clock_set_pll5' function picks some other values for them (with the same resulting target clock speed)?
And if they are really required for A23, then why exposing them as the function arguments instead of hiding this implementation detail inside of the 'clock_set_pll5' function?
arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 4 +--- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 193e314..8ef19df 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -144,12 +144,10 @@ void clock_set_pll3(unsigned int clk) &ccm->pll3_cfg); }
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int k = 2;
const int m = 1;
if (sigma_delta_enable) writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg);
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index bc6428a..a8bbdfd 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -46,7 +46,7 @@ static void mctl_sys_init(void) (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int dram_clk_div = 2;
- clock_set_pll5(DRAM_CLK * dram_clk_div, false);
clock_set_pll5(DRAM_CLK * dram_clk_div, 2, 1, false);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST |
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index f807af3..7d61216 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -311,6 +311,6 @@ struct sunxi_ccm_reg { #define CCM_DE_CTRL_PLL10 (5 << 24) #define CCM_DE_CTRL_GATE (1 << 31)
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable); +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable);
#endif /* _SUNXI_CLOCK_SUN6I_H */

Hi,
On 19-12-14 11:03, Siarhei Siamashka wrote:
On Tue, 16 Dec 2014 21:31:34 +0100 Hans de Goede hdegoede@redhat.com wrote:
The A23 (sun8i) requires different values for these then sun6i, so make them function parameters.
Signed-off-by: Hans de Goede hdegoede@redhat.com
What happens if A23 does not get these special k and m parameters, but the 'clock_set_pll5' function picks some other values for them (with the same resulting target clock speed)?
The major difference is that on the A23 pll5 must be set to dram_clk / 2, where as on A31 it needs to be set to dram_clk * 2. By codifying k and m so that they do the * 2 on A31 and / 2 on A23 we can make any multiple of 24 MHz as DRAM clk without needing any other code to figure out the optimal k and m. AFAIK using other k and m factors with the same end-result should work fine. But doing things this way follows the KISS principle and I'm a great fan of KISS.
Regards,
Hans
And if they are really required for A23, then why exposing them as the function arguments instead of hiding this implementation detail inside of the 'clock_set_pll5' function?
arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 4 +--- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 193e314..8ef19df 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -144,12 +144,10 @@ void clock_set_pll3(unsigned int clk) &ccm->pll3_cfg); }
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int k = 2;
const int m = 1;
if (sigma_delta_enable) writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg);
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index bc6428a..a8bbdfd 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -46,7 +46,7 @@ static void mctl_sys_init(void) (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int dram_clk_div = 2;
- clock_set_pll5(DRAM_CLK * dram_clk_div, false);
clock_set_pll5(DRAM_CLK * dram_clk_div, 2, 1, false);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST |
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index f807af3..7d61216 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -311,6 +311,6 @@ struct sunxi_ccm_reg { #define CCM_DE_CTRL_PLL10 (5 << 24) #define CCM_DE_CTRL_GATE (1 << 31)
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable); +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable);
#endif /* _SUNXI_CLOCK_SUN6I_H */

On Fri, 19 Dec 2014 17:40:27 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hi,
On 19-12-14 11:03, Siarhei Siamashka wrote:
On Tue, 16 Dec 2014 21:31:34 +0100 Hans de Goede hdegoede@redhat.com wrote:
The A23 (sun8i) requires different values for these then sun6i, so make them function parameters.
Signed-off-by: Hans de Goede hdegoede@redhat.com
What happens if A23 does not get these special k and m parameters, but the 'clock_set_pll5' function picks some other values for them (with the same resulting target clock speed)?
The major difference is that on the A23 pll5 must be set to dram_clk / 2, where as on A31 it needs to be set to dram_clk * 2. By codifying k and m so that they do the * 2 on A31 and / 2 on A23 we can make any multiple of 24 MHz as DRAM clk without needing any other code to figure out the optimal k and m.
Wouldn't such other code in 'clock_set_pll5' just look like this (adds only a few extra instructions with an optimizing compiler):
const int max_n = 32; int k = 1, m = 2; if (clk > 24000000 * k * max_n / m) { m = 1; if (clk > 24000000 * k * max_n / m) k = 2; }
As an additional bonus, you get 12 MHz clock frequency selection granularity on A31 for the DRAM clock speeds up to 384 MHz. And keep the current 24 MHz granularity for anything larger than that.
AFAIK using other k and m factors with the same end-result should work fine.
This statement seems to somewhat contradict with your original commit message. Should the commit message be updated?
But doing things this way follows the KISS principle and I'm a great fan of KISS.
By instead spreading the complexity all across the sources? So that each caller code needs to make assumptions about the valid clock frequency range and statically pick the right multiplier/divisor parameters as an additional burden?
In what way is it simple?
Regards,
Hans
And if they are really required for A23, then why exposing them as the function arguments instead of hiding this implementation detail inside of the 'clock_set_pll5' function?
arch/arm/cpu/armv7/sunxi/clock_sun6i.c | 4 +--- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c index 193e314..8ef19df 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun6i.c @@ -144,12 +144,10 @@ void clock_set_pll3(unsigned int clk) &ccm->pll3_cfg); }
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int k = 2;
const int m = 1;
if (sigma_delta_enable) writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg);
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index bc6428a..a8bbdfd 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -46,7 +46,7 @@ static void mctl_sys_init(void) (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; const int dram_clk_div = 2;
- clock_set_pll5(DRAM_CLK * dram_clk_div, false);
clock_set_pll5(DRAM_CLK * dram_clk_div, 2, 1, false);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST |
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index f807af3..7d61216 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -311,6 +311,6 @@ struct sunxi_ccm_reg { #define CCM_DE_CTRL_PLL10 (5 << 24) #define CCM_DE_CTRL_GATE (1 << 31)
-void clock_set_pll5(unsigned int clk, bool sigma_delta_enable); +void clock_set_pll5(unsigned int clk, int k, int m, bool sigma_delta_enable);
#endif /* _SUNXI_CLOCK_SUN6I_H */

The await_completion helper is already copy pasted between the sun4i and sun6i dram code, and we need it for sun8i too, so lets make it an inline helper in dram.h, rather then adding yet another copy.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/dram_sun4i.c | 17 ++--------------- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 31 +++++++++---------------------- arch/arm/include/asm/arch-sunxi/dram.h | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 37 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun4i.c b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c index ec8aaa7..c736fa3 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun4i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c @@ -36,24 +36,11 @@ #define CPU_CFG_CHIP_REV_B 0x3
/* - * Wait up to 1s for value to be set in given part of reg. - */ -static void await_completion(u32 *reg, u32 mask, u32 val) -{ - unsigned long tmo = timer_get_us() + 1000000; - - while ((readl(reg) & mask) != val) { - if (timer_get_us() > tmo) - panic("Timeout initialising DRAM\n"); - } -} - -/* * Wait up to 1s for mask to be clear in given reg. */ static inline void await_bits_clear(u32 *reg, u32 mask) { - await_completion(reg, mask, 0); + mctl_await_completion(reg, mask, 0); }
/* @@ -61,7 +48,7 @@ static inline void await_bits_clear(u32 *reg, u32 mask) */ static inline void await_bits_set(u32 *reg, u32 mask) { - await_completion(reg, mask, mask); + mctl_await_completion(reg, mask, mask); }
/* diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index a8bbdfd..e1670e5 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -27,19 +27,6 @@ struct dram_sun6i_para { u16 page_size; };
-/* - * Wait up to 1s for value to be set in given part of reg. - */ -static void await_completion(u32 *reg, u32 mask, u32 val) -{ - unsigned long tmo = timer_get_us() + 1000000; - - while ((readl(reg) & mask) != val) { - if (timer_get_us() > tmo) - panic("Timeout initialising DRAM\n"); - } -} - static void mctl_sys_init(void) { struct sunxi_ccm_reg * const ccm = @@ -51,7 +38,7 @@ static void mctl_sys_init(void) clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST | CCM_DRAMCLK_CFG_UPD); - await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0); + mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
writel(MDFS_CLK_DEFAULT, &ccm->mdfs_clk_cfg);
@@ -107,8 +94,8 @@ static bool mctl_rank_detect(u32 *gsr0, int rank) const u32 done = MCTL_DX_GSR0_RANK0_TRAIN_DONE << rank; const u32 err = MCTL_DX_GSR0_RANK0_TRAIN_ERR << rank;
- await_completion(gsr0, done, done); - await_completion(gsr0 + 0x10, done, done); + mctl_await_completion(gsr0, done, done); + mctl_await_completion(gsr0 + 0x10, done, done);
return !(readl(gsr0) & err) && !(readl(gsr0 + 0x10) & err); } @@ -129,7 +116,7 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) }
writel(MCTL_MCMD_NOP, &mctl_ctl->mcmd); - await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0); + mctl_await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0);
/* PHY initialization */ writel(MCTL_PGCR, &mctl_phy->pgcr); @@ -166,14 +153,14 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx2gcr); writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx3gcr);
- await_completion(&mctl_phy->pgsr, 0x03, 0x03); + mctl_await_completion(&mctl_phy->pgsr, 0x03, 0x03);
writel(CONFIG_DRAM_ZQ, &mctl_phy->zq0cr1);
setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); writel(MCTL_PIR_STEP1, &mctl_phy->pir); udelay(10); - await_completion(&mctl_phy->pgsr, 0x1f, 0x1f); + mctl_await_completion(&mctl_phy->pgsr, 0x1f, 0x1f);
/* rank detect */ if (!mctl_rank_detect(&mctl_phy->dx0gsr0, 1)) { @@ -204,14 +191,14 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); writel(MCTL_PIR_STEP2, &mctl_phy->pir); udelay(10); - await_completion(&mctl_phy->pgsr, 0x11, 0x11); + mctl_await_completion(&mctl_phy->pgsr, 0x11, 0x11);
if (readl(&mctl_phy->pgsr) & MCTL_PGSR_TRAIN_ERR_MASK) panic("Training error initialising DRAM\n");
/* Move to configure state */ writel(MCTL_SCTL_CONFIG, &mctl_ctl->sctl); - await_completion(&mctl_ctl->sstat, 0x07, 0x01); + mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x01);
/* Set number of clks per micro-second */ writel(DRAM_CLK / 1000000, &mctl_ctl->togcnt1u); @@ -270,7 +257,7 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para)
/* Move to access state */ writel(MCTL_SCTL_ACCESS, &mctl_ctl->sctl); - await_completion(&mctl_ctl->sstat, 0x07, 0x03); + mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x03); }
static void mctl_com_init(struct dram_sun6i_para *para) diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 9072e68..18924f5 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -12,6 +12,7 @@ #ifndef _SUNXI_DRAM_H #define _SUNXI_DRAM_H
+#include <asm/io.h> #include <linux/types.h>
/* dram regs definition */ @@ -23,4 +24,17 @@
unsigned long sunxi_dram_init(void);
+/* + * Wait up to 1s for value to be set in given part of reg. + */ +static inline void mctl_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"); + } +} + #endif /* _SUNXI_DRAM_H */

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The await_completion helper is already copy pasted between the sun4i and sun6i dram code, and we need it for sun8i too, so lets make it an inline helper in dram.h, rather then adding yet another copy.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

On Tue, 16 Dec 2014 21:31:35 +0100 Hans de Goede hdegoede@redhat.com wrote:
The await_completion helper is already copy pasted between the sun4i and sun6i dram code, and we need it for sun8i too, so lets make it an inline helper in dram.h, rather then adding yet another copy.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Making this function "static inline" and placing it into a header file encourages the compiler to actually inline it. Which is not great for code size:
== Before the patch and using gcc version 4.8.3 ==
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun4i.o text data bss dec hex filename 1731 0 0 1731 6c3
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun6i.o text data bss dec hex filename 1841 0 0 1841 731
== After the patch and using gcc version 4.8.3 ==
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun4i.o text data bss dec hex filename 1763 0 0 1763 6e3
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun6i.o text data bss dec hex filename 1983 0 0 1983 7bf
Could we perhaps just introduce something like a new source file "dram_common.c" or even "dram.c"?
arch/arm/cpu/armv7/sunxi/dram_sun4i.c | 17 ++--------------- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 31 +++++++++---------------------- arch/arm/include/asm/arch-sunxi/dram.h | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 37 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun4i.c b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c index ec8aaa7..c736fa3 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun4i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun4i.c @@ -36,24 +36,11 @@ #define CPU_CFG_CHIP_REV_B 0x3
/*
- Wait up to 1s for value to be set in given part of reg.
- */
-static void await_completion(u32 *reg, u32 mask, u32 val) -{
- unsigned long tmo = timer_get_us() + 1000000;
- while ((readl(reg) & mask) != val) {
if (timer_get_us() > tmo)
panic("Timeout initialising DRAM\n");
- }
-}
-/*
- Wait up to 1s for mask to be clear in given reg.
*/ static inline void await_bits_clear(u32 *reg, u32 mask) {
- await_completion(reg, mask, 0);
- mctl_await_completion(reg, mask, 0);
}
/* @@ -61,7 +48,7 @@ static inline void await_bits_clear(u32 *reg, u32 mask) */ static inline void await_bits_set(u32 *reg, u32 mask) {
- await_completion(reg, mask, mask);
- mctl_await_completion(reg, mask, mask);
}
/* diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index a8bbdfd..e1670e5 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -27,19 +27,6 @@ struct dram_sun6i_para { u16 page_size; };
-/*
- Wait up to 1s for value to be set in given part of reg.
- */
-static void await_completion(u32 *reg, u32 mask, u32 val) -{
- unsigned long tmo = timer_get_us() + 1000000;
- while ((readl(reg) & mask) != val) {
if (timer_get_us() > tmo)
panic("Timeout initialising DRAM\n");
- }
-}
static void mctl_sys_init(void) { struct sunxi_ccm_reg * const ccm = @@ -51,7 +38,7 @@ static void mctl_sys_init(void) clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST | CCM_DRAMCLK_CFG_UPD);
- await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
- mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
As an additional observation, moving await_bits_clear/await_bits_set functions into a common header file and using them in the sun6i dram code might improve readability.
writel(MDFS_CLK_DEFAULT, &ccm->mdfs_clk_cfg);
@@ -107,8 +94,8 @@ static bool mctl_rank_detect(u32 *gsr0, int rank) const u32 done = MCTL_DX_GSR0_RANK0_TRAIN_DONE << rank; const u32 err = MCTL_DX_GSR0_RANK0_TRAIN_ERR << rank;
- await_completion(gsr0, done, done);
- await_completion(gsr0 + 0x10, done, done);
mctl_await_completion(gsr0, done, done);
mctl_await_completion(gsr0 + 0x10, done, done);
return !(readl(gsr0) & err) && !(readl(gsr0 + 0x10) & err);
} @@ -129,7 +116,7 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) }
writel(MCTL_MCMD_NOP, &mctl_ctl->mcmd);
- await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0);
mctl_await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0);
/* PHY initialization */ writel(MCTL_PGCR, &mctl_phy->pgcr);
@@ -166,14 +153,14 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx2gcr); writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx3gcr);
- await_completion(&mctl_phy->pgsr, 0x03, 0x03);
mctl_await_completion(&mctl_phy->pgsr, 0x03, 0x03);
writel(CONFIG_DRAM_ZQ, &mctl_phy->zq0cr1);
setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); writel(MCTL_PIR_STEP1, &mctl_phy->pir); udelay(10);
- await_completion(&mctl_phy->pgsr, 0x1f, 0x1f);
mctl_await_completion(&mctl_phy->pgsr, 0x1f, 0x1f);
/* rank detect */ if (!mctl_rank_detect(&mctl_phy->dx0gsr0, 1)) {
@@ -204,14 +191,14 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); writel(MCTL_PIR_STEP2, &mctl_phy->pir); udelay(10);
- await_completion(&mctl_phy->pgsr, 0x11, 0x11);
mctl_await_completion(&mctl_phy->pgsr, 0x11, 0x11);
if (readl(&mctl_phy->pgsr) & MCTL_PGSR_TRAIN_ERR_MASK) panic("Training error initialising DRAM\n");
/* Move to configure state */ writel(MCTL_SCTL_CONFIG, &mctl_ctl->sctl);
- await_completion(&mctl_ctl->sstat, 0x07, 0x01);
mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x01);
/* Set number of clks per micro-second */ writel(DRAM_CLK / 1000000, &mctl_ctl->togcnt1u);
@@ -270,7 +257,7 @@ static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para)
/* Move to access state */ writel(MCTL_SCTL_ACCESS, &mctl_ctl->sctl);
- await_completion(&mctl_ctl->sstat, 0x07, 0x03);
- mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x03);
}
static void mctl_com_init(struct dram_sun6i_para *para) diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 9072e68..18924f5 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -12,6 +12,7 @@ #ifndef _SUNXI_DRAM_H #define _SUNXI_DRAM_H
+#include <asm/io.h> #include <linux/types.h>
/* dram regs definition */ @@ -23,4 +24,17 @@
unsigned long sunxi_dram_init(void);
+/*
- Wait up to 1s for value to be set in given part of reg.
- */
+static inline void mctl_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");
- }
+}
#endif /* _SUNXI_DRAM_H */

Hi,
On 19-12-14 11:06, Siarhei Siamashka wrote:
On Tue, 16 Dec 2014 21:31:35 +0100 Hans de Goede hdegoede@redhat.com wrote:
The await_completion helper is already copy pasted between the sun4i and sun6i dram code, and we need it for sun8i too, so lets make it an inline helper in dram.h, rather then adding yet another copy.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Making this function "static inline" and placing it into a header file encourages the compiler to actually inline it. Which is not great for code size:
== Before the patch and using gcc version 4.8.3 ==
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun4i.o text data bss dec hex filename 1731 0 0 1731 6c3
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun6i.o text data bss dec hex filename 1841 0 0 1841 731
== After the patch and using gcc version 4.8.3 ==
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun4i.o text data bss dec hex filename 1763 0 0 1763 6e3
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun6i.o text data bss dec hex filename 1983 0 0 1983 7bf
Ah, thanks for catching that, the size increase does not seem to be a problem right now, but it certainly is something to keep in mind.
Could we perhaps just introduce something like a new source file "dram_common.c" or even "dram.c"?
Sounds like a good idea, patches welcome.
As an additional observation, moving await_bits_clear/await_bits_set functions into a common header file and using them in the sun6i dram code might improve readability.
Sounds like another good idea, patches welcome.
Regards,
Hans

On Fri, 19 Dec 2014 17:42:58 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hi,
On 19-12-14 11:06, Siarhei Siamashka wrote:
On Tue, 16 Dec 2014 21:31:35 +0100 Hans de Goede hdegoede@redhat.com wrote:
The await_completion helper is already copy pasted between the sun4i and sun6i dram code, and we need it for sun8i too, so lets make it an inline helper in dram.h, rather then adding yet another copy.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Making this function "static inline" and placing it into a header file encourages the compiler to actually inline it. Which is not great for code size:
== Before the patch and using gcc version 4.8.3 ==
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun4i.o text data bss dec hex filename 1731 0 0 1731 6c3
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun6i.o text data bss dec hex filename 1841 0 0 1841 731
== After the patch and using gcc version 4.8.3 ==
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun4i.o text data bss dec hex filename 1763 0 0 1763 6e3
$ arm-none-linux-gnieabi-size spl/arch/arm/cpu/armv7/sunxi/dram_sun6i.o text data bss dec hex filename 1983 0 0 1983 7bf
Ah, thanks for catching that, the size increase does not seem to be a problem right now, but it certainly is something to keep in mind.
Could we perhaps just introduce something like a new source file "dram_common.c" or even "dram.c"?
Sounds like a good idea, patches welcome.
I would definitely appreciate if we could have avoided having this unnecessary code size increase in the first place.
But if we run out of the code size space, then the oversized static inline functions in the headers are always relatively easy to identify and eliminate in one go. That's only a minor annoyance and I'm not going to waste time on pointless debates.
As an additional observation, moving await_bits_clear/await_bits_set functions into a common header file and using them in the sun6i dram code might improve readability.
Sounds like another good idea, patches welcome.

The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
The new mctl_mem_fill function this introduces is added as an inline helper in dram.h, so that it can be shared with the sun8i dram code.
While at it move mctl_mem_matches to dram.h for re-use in sun8i too.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 15 +-------------- arch/arm/include/asm/arch-sunxi/dram.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index e1670e5..4675c48 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -326,20 +326,6 @@ static void mctl_port_cfg(void) writel(0x00000307, &mctl_com->mbagcr[5]); }
-static bool mctl_mem_matches(u32 offset) -{ - const int match_count = 64; - int i, matches = 0; - - for (i = 0; i < match_count; i++) { - if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) == - readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4)) - matches++; - } - - return matches == match_count; -} - unsigned long sunxi_dram_init(void) { struct sunxi_mctl_com_reg * const mctl_com = @@ -391,6 +377,7 @@ unsigned long sunxi_dram_init(void) MCTL_CR_BANK(1) | MCTL_CR_RANK(1));
/* Detect and set page size */ + mctl_mem_fill(); for (columns = 7; columns < 20; columns++) { if (mctl_mem_matches(1 << columns)) break; diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 18924f5..0bf718c 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -22,6 +22,8 @@ #include <asm/arch/dram_sun4i.h> #endif
+#define MCTL_MEM_FILL_MATCH_COUNT 64 + unsigned long sunxi_dram_init(void);
/* @@ -37,4 +39,31 @@ static inline void mctl_await_completion(u32 *reg, u32 mask, u32 val) } }
+/* + * Fill beginning of DRAM with "random" data for mctl_mem_matches() + */ +static inline void mctl_mem_fill(void) +{ + int i; + + for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++) + writel(0xaa55aa55 + i, CONFIG_SYS_SDRAM_BASE + i * 4); +} + +/* + * Test if memory at offset offset matches memory at begin of DRAM + */ +static inline bool mctl_mem_matches(u32 offset) +{ + int i, matches = 0; + + for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++) { + if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) == + readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4)) + matches++; + } + + return matches == MCTL_MEM_FILL_MATCH_COUNT; +} + #endif /* _SUNXI_DRAM_H */

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round? The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
The new mctl_mem_fill function this introduces is added as an inline helper in dram.h, so that it can be shared with the sun8i dram code.
While at it move mctl_mem_matches to dram.h for re-use in sun8i too.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 15 +-------------- arch/arm/include/asm/arch-sunxi/dram.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index e1670e5..4675c48 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -326,20 +326,6 @@ static void mctl_port_cfg(void) writel(0x00000307, &mctl_com->mbagcr[5]); }
-static bool mctl_mem_matches(u32 offset) -{
- const int match_count = 64;
- int i, matches = 0;
- for (i = 0; i < match_count; i++) {
if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) ==
readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4))
matches++;
- }
- return matches == match_count;
-}
unsigned long sunxi_dram_init(void) { struct sunxi_mctl_com_reg * const mctl_com = @@ -391,6 +377,7 @@ unsigned long sunxi_dram_init(void) MCTL_CR_BANK(1) | MCTL_CR_RANK(1));
/* Detect and set page size */
- mctl_mem_fill(); for (columns = 7; columns < 20; columns++) { if (mctl_mem_matches(1 << columns)) break;
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 18924f5..0bf718c 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -22,6 +22,8 @@ #include <asm/arch/dram_sun4i.h> #endif
+#define MCTL_MEM_FILL_MATCH_COUNT 64
unsigned long sunxi_dram_init(void);
/* @@ -37,4 +39,31 @@ static inline void mctl_await_completion(u32 *reg, u32 mask, u32 val) } }
+/*
- Fill beginning of DRAM with "random" data for mctl_mem_matches()
- */
+static inline void mctl_mem_fill(void) +{
- int i;
- for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++)
writel(0xaa55aa55 + i, CONFIG_SYS_SDRAM_BASE + i * 4);
+}
+/*
- Test if memory at offset offset matches memory at begin of DRAM
- */
+static inline bool mctl_mem_matches(u32 offset) +{
- int i, matches = 0;
- for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++) {
if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) ==
readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4))
matches++;
- }
- return matches == MCTL_MEM_FILL_MATCH_COUNT;
+}
#endif /* _SUNXI_DRAM_H */

On Thu, 18 Dec 2014 19:12:13 +0000 Ian Campbell ijc@hellion.org.uk wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round? The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
It appears that this code is just trying to find the first address line, which is not connected anywhere. If the address line is not connected, then having the corresponding bit set or clear in the memory address does not matter and we are effectively accessing the same location.
Both sun6i and sun8i code is incorrect and only works because of relying on luck.
The sun6i code is incorrect because it is just reading memory without initializing it at all. Probably relying on having some unique garbage there in a natural way.
The newer sun8i code is still incorrect because it is not safeguarding against accidentally encountering the same test pattern at an unrelated memory location. Yes, the probability is extremely low, but still not zero.
It's a good idea to have a look at the 'get_ram_size' function for the inspiration.
The new mctl_mem_fill function this introduces is added as an inline helper in dram.h, so that it can be shared with the sun8i dram code.
While at it move mctl_mem_matches to dram.h for re-use in sun8i too.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 15 +-------------- arch/arm/include/asm/arch-sunxi/dram.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index e1670e5..4675c48 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -326,20 +326,6 @@ static void mctl_port_cfg(void) writel(0x00000307, &mctl_com->mbagcr[5]); }
-static bool mctl_mem_matches(u32 offset) -{
- const int match_count = 64;
- int i, matches = 0;
- for (i = 0; i < match_count; i++) {
if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) ==
readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4))
matches++;
- }
- return matches == match_count;
-}
unsigned long sunxi_dram_init(void) { struct sunxi_mctl_com_reg * const mctl_com = @@ -391,6 +377,7 @@ unsigned long sunxi_dram_init(void) MCTL_CR_BANK(1) | MCTL_CR_RANK(1));
/* Detect and set page size */
- mctl_mem_fill(); for (columns = 7; columns < 20; columns++) { if (mctl_mem_matches(1 << columns)) break;
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 18924f5..0bf718c 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -22,6 +22,8 @@ #include <asm/arch/dram_sun4i.h> #endif
+#define MCTL_MEM_FILL_MATCH_COUNT 64
unsigned long sunxi_dram_init(void);
/* @@ -37,4 +39,31 @@ static inline void mctl_await_completion(u32 *reg, u32 mask, u32 val) } }
+/*
- Fill beginning of DRAM with "random" data for mctl_mem_matches()
- */
+static inline void mctl_mem_fill(void) +{
- int i;
- for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++)
writel(0xaa55aa55 + i, CONFIG_SYS_SDRAM_BASE + i * 4);
+}
+/*
- Test if memory at offset offset matches memory at begin of DRAM
- */
+static inline bool mctl_mem_matches(u32 offset) +{
- int i, matches = 0;
- for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++) {
if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) ==
readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4))
matches++;
- }
- return matches == MCTL_MEM_FILL_MATCH_COUNT;
+}
#endif /* _SUNXI_DRAM_H */

Hi,
On 19-12-14 11:08, Siarhei Siamashka wrote:
On Thu, 18 Dec 2014 19:12:13 +0000 Ian Campbell ijc@hellion.org.uk wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round? The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
It appears that this code is just trying to find the first address line, which is not connected anywhere. If the address line is not connected, then having the corresponding bit set or clear in the memory address does not matter and we are effectively accessing the same location.
Correct.
Both sun6i and sun8i code is incorrect and only works because of relying on luck.
The sun6i code is incorrect because it is just reading memory without initializing it at all. Probably relying on having some unique garbage there in a natural way.
The newer sun8i code is still incorrect because it is not safeguarding against accidentally encountering the same test pattern at an unrelated memory location. Yes, the probability is extremely low, but still not zero.
The probability is so low that it really does not matter, so I've no intention to fix this.
Regards,
Hans

On Fri, 19 Dec 2014 17:56:40 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hi,
On 19-12-14 11:08, Siarhei Siamashka wrote:
On Thu, 18 Dec 2014 19:12:13 +0000 Ian Campbell ijc@hellion.org.uk wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round? The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
It appears that this code is just trying to find the first address line, which is not connected anywhere. If the address line is not connected, then having the corresponding bit set or clear in the memory address does not matter and we are effectively accessing the same location.
Correct.
Both sun6i and sun8i code is incorrect and only works because of relying on luck.
The sun6i code is incorrect because it is just reading memory without initializing it at all. Probably relying on having some unique garbage there in a natural way.
The newer sun8i code is still incorrect because it is not safeguarding against accidentally encountering the same test pattern at an unrelated memory location. Yes, the probability is extremely low, but still not zero.
The probability is so low that it really does not matter, so I've no intention to fix this.
"If anything bad can happen, it probably will" :-)
There are many real life examples, when something supposedly almost impossible has in fact happened. Like the following story: http://ixbtlabs.com/articles2/magia-chisel/ In a nutshell, the designers of some old CD/CD-RW drives just did not bother to ensure that the written user data can be never confused with the service information (sync headers) on disk. The probability of encountering something that resembles a sync header was considered to be way too low. Well, until somebody has encountered a hardware glitch and a sync header fragment got mistakenly read from the drive as part of some file. Now this corrupted file can wreak havoc if written to a CD disk, because the false sync header in it is getting interpreted as an actual sync header. Oops.
Anyway, this is a bug in the sun6i/sun8i dram code. And the submitted patch appears to be only a partial/incomplete fix. A strong NAK from me.

Hi,
On 18-12-14 20:12, Ian Campbell wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round?
Yes, while working on the sun8i code I noticed that it was prefilling memory before doing wrap-around checks like the sun6i code is doing too, and then I realized that the sun6i code was relying on whatever is in DRAM being random enough for its wrap-around tests to work, without ensuring it is random enough.
The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
Yep, it purely works by chance so far.
Regards,
Hans
The new mctl_mem_fill function this introduces is added as an inline helper in dram.h, so that it can be shared with the sun8i dram code.
While at it move mctl_mem_matches to dram.h for re-use in sun8i too.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 15 +-------------- arch/arm/include/asm/arch-sunxi/dram.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c index e1670e5..4675c48 100644 --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c @@ -326,20 +326,6 @@ static void mctl_port_cfg(void) writel(0x00000307, &mctl_com->mbagcr[5]); }
-static bool mctl_mem_matches(u32 offset) -{
- const int match_count = 64;
- int i, matches = 0;
- for (i = 0; i < match_count; i++) {
if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) ==
readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4))
matches++;
- }
- return matches == match_count;
-}
- unsigned long sunxi_dram_init(void) { struct sunxi_mctl_com_reg * const mctl_com =
@@ -391,6 +377,7 @@ unsigned long sunxi_dram_init(void) MCTL_CR_BANK(1) | MCTL_CR_RANK(1));
/* Detect and set page size */
- mctl_mem_fill(); for (columns = 7; columns < 20; columns++) { if (mctl_mem_matches(1 << columns)) break;
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 18924f5..0bf718c 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -22,6 +22,8 @@ #include <asm/arch/dram_sun4i.h> #endif
+#define MCTL_MEM_FILL_MATCH_COUNT 64
unsigned long sunxi_dram_init(void);
/*
@@ -37,4 +39,31 @@ static inline void mctl_await_completion(u32 *reg, u32 mask, u32 val) } }
+/*
- Fill beginning of DRAM with "random" data for mctl_mem_matches()
- */
+static inline void mctl_mem_fill(void) +{
- int i;
- for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++)
writel(0xaa55aa55 + i, CONFIG_SYS_SDRAM_BASE + i * 4);
+}
+/*
- Test if memory at offset offset matches memory at begin of DRAM
- */
+static inline bool mctl_mem_matches(u32 offset) +{
- int i, matches = 0;
- for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++) {
if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) ==
readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4))
matches++;
- }
- return matches == MCTL_MEM_FILL_MATCH_COUNT;
+}
- #endif /* _SUNXI_DRAM_H */

On Fri, 2014-12-19 at 17:55 +0100, Hans de Goede wrote:
Hi,
On 18-12-14 20:12, Ian Campbell wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round?
Yes, while working on the sun8i code I noticed that it was prefilling memory before doing wrap-around checks like the sun6i code is doing too, and then I realized that the sun6i code was relying on whatever is in DRAM being random enough for its wrap-around tests to work, without ensuring it is random enough.
The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
Yep, it purely works by chance so far.
"excellent". Can you mention that in the commit message please. With that: Acked-by: Ian Campbell ijc@hellion.org.uk
Ian.

On Mon, 22 Dec 2014 14:19:20 +0000 Ian Campbell ijc@hellion.org.uk wrote:
On Fri, 2014-12-19 at 17:55 +0100, Hans de Goede wrote:
Hi,
On 18-12-14 20:12, Ian Campbell wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round?
Yes, while working on the sun8i code I noticed that it was prefilling memory before doing wrap-around checks like the sun6i code is doing too, and then I realized that the sun6i code was relying on whatever is in DRAM being random enough for its wrap-around tests to work, without ensuring it is random enough.
The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
Yep, it purely works by chance so far.
"excellent". Can you mention that in the commit message please. With that: Acked-by: Ian Campbell ijc@hellion.org.uk
Ian, are you fine with the fact that it still works by chance even after this "fix"? Albeit with a much higher chance of not running into troubles.

On Mon, 2014-12-22 at 16:32 +0200, Siarhei Siamashka wrote:
On Mon, 22 Dec 2014 14:19:20 +0000 Ian Campbell ijc@hellion.org.uk wrote:
On Fri, 2014-12-19 at 17:55 +0100, Hans de Goede wrote:
Hi,
On 18-12-14 20:12, Ian Campbell wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round?
Yes, while working on the sun8i code I noticed that it was prefilling memory before doing wrap-around checks like the sun6i code is doing too, and then I realized that the sun6i code was relying on whatever is in DRAM being random enough for its wrap-around tests to work, without ensuring it is random enough.
The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
Yep, it purely works by chance so far.
"excellent". Can you mention that in the commit message please. With that: Acked-by: Ian Campbell ijc@hellion.org.uk
Ian, are you fine with the fact that it still works by chance even after this "fix"? Albeit with a much higher chance of not running into troubles.
Yes, it's clearly an improvement even if it isn't perfect.
Ian.

Hi,
On 22-12-14 15:19, Ian Campbell wrote:
On Fri, 2014-12-19 at 17:55 +0100, Hans de Goede wrote:
Hi,
On 18-12-14 20:12, Ian Campbell wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The sun8i boot0 code fills the DRAM with a "random" pattern before comparing it at different offsets to do columns, etc. detection. The sun6i boot0 code does not do it, but it seems like a good idea to do this regardless.
Is this the right way round?
Yes, while working on the sun8i code I noticed that it was prefilling memory before doing wrap-around checks like the sun6i code is doing too, and then I realized that the sun6i code was relying on whatever is in DRAM being random enough for its wrap-around tests to work, without ensuring it is random enough.
The existing sun6i code (which you are moving here) seems to _rely_ on something having written a useful pattern, which I would have assumed to have been boot0. Or else how does it work now? Chance?
Yep, it purely works by chance so far.
"excellent". Can you mention that in the commit message please.
Done.
With that: Acked-by: Ian Campbell ijc@hellion.org.uk
Thanks, the now fully acked sun8i support patch-set has been pushed to u-boot-sunxi/next .
Thanks for all the reviews!
Regards,
Hans

Use memcmp for mctl_mem_matches instead of DIY.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/include/asm/arch-sunxi/dram.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 0bf718c..a8a37d5 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -55,15 +55,9 @@ static inline void mctl_mem_fill(void) */ static inline bool mctl_mem_matches(u32 offset) { - int i, matches = 0; - - for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++) { - if (readl(CONFIG_SYS_SDRAM_BASE + i * 4) == - readl(CONFIG_SYS_SDRAM_BASE + offset + i * 4)) - matches++; - } - - return matches == MCTL_MEM_FILL_MATCH_COUNT; + return memcmp((u32 *)CONFIG_SYS_SDRAM_BASE, + (u32 *)(CONFIG_SYS_SDRAM_BASE + offset), + MCTL_MEM_FILL_MATCH_COUNT * 4) == 0; }
#endif /* _SUNXI_DRAM_H */

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
Use memcmp for mctl_mem_matches instead of DIY.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
Ian.

Based on the register / dram_para headers from the Allwinner u-boot / linux sources + the init sequences from boot0.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/board.c | 3 +- arch/arm/cpu/armv7/sunxi/dram_sun8i.c | 340 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 4 + arch/arm/include/asm/arch-sunxi/dram.h | 2 + arch/arm/include/asm/arch-sunxi/dram_sun8i.h | 266 ++++++++++++++++++++ board/sunxi/Kconfig | 3 +- configs/Ippo_q8h_v5_defconfig | 17 +- include/configs/sun8i.h | 2 + 9 files changed, 631 insertions(+), 7 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun8i.c create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun8i.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 3e8975a..1e89937 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_MACH_SUN4I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN5I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN6I) += dram_sun6i.o obj-$(CONFIG_MACH_SUN7I) += dram_sun4i.o +obj-$(CONFIG_MACH_SUN8I) += dram_sun8i.o ifdef CONFIG_SPL_FEL obj-y += start.o endif diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 9b3e80c..bc98c56 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -114,7 +114,8 @@ void reset_cpu(ulong addr) /* do some early init */ void s_init(void) { -#if defined CONFIG_SPL_BUILD && defined CONFIG_MACH_SUN6I +#if defined CONFIG_SPL_BUILD && \ + (defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I) /* Magic (undocmented) value taken from boot0, without this DRAM * access gets messed up (seems cache related) */ setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800); diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun8i.c b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c new file mode 100644 index 0000000..3736fd1 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c @@ -0,0 +1,340 @@ +/* + * Sun8i platform dram controller init. + * + * (C) Copyright 2014 Hans de Goede hdegoede@redhat.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Note this code uses a lot of magic hex values, that is because this code + * simply replays the init sequence as done by the Allwinner boot0 code, so + * we do not know what these values mean. There are no symbolic constants for + * these magic values, since we do not know how to name them and making up + * names for them is not useful. + */ + +#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h> + +static const struct dram_para dram_para = { + .clock = CONFIG_DRAM_CLK, + .type = 3, + .zq = CONFIG_DRAM_ZQ, + .odt_en = 1, + .para1 = 0, /* not used (only used when tpr13 bit 31 is set */ + .para2 = 0, /* not used (only used when tpr13 bit 31 is set */ + .mr0 = 6736, + .mr1 = 4, + .mr2 = 16, + .mr3 = 0, + /* tpr0 - 10 contain timing constants or-ed together in u32 vals */ + .tpr0 = 0x2ab83def, + .tpr1 = 0x18082356, + .tpr2 = 0x00034156, + .tpr3 = 0x448c5533, + .tpr4 = 0x08010d00, + .tpr5 = 0x0340b20f, + .tpr6 = 0x20d118cc, + .tpr7 = 0x14062485, + .tpr8 = 0x220d1d52, + .tpr9 = 0x1e078c22, + .tpr10 = 0x3c, + .tpr11 = 0, /* not used */ + .tpr12 = 0, /* not used */ + .tpr13 = 0x30000, +}; + +static void mctl_sys_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* enable pll5, note the divide by 2 is deliberate! */ + clock_set_pll5(dram_para.clock * 1000000 / 2, 1, 2, + dram_para.tpr13 & 0x40000); + + /* deassert ahb mctl reset */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + + /* enable ahb mctl clock */ + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); +} + +static void mctl_apply_odt_correction(u32 *reg, int correction) +{ + int val; + + val = (readl(reg) >> 8) & 0xff; + val += correction; + + /* clamp */ + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + + clrsetbits_le32(reg, 0xff00, val << 8); +} + +static void mctl_init(u32 *bus_width) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int correction; + + if (dram_para.tpr13 & 0x20) + writel(0x40b, &mctl_phy->dcr); + else + writel(0x1000040b, &mctl_phy->dcr); + + if (dram_para.clock >= 480) + writel(0x5c000, &mctl_phy->dllgcr); + else + writel(0xdc000, &mctl_phy->dllgcr); + + writel(0x0a003e3f, &mctl_phy->pgcr0); + writel(0x03008421, &mctl_phy->pgcr1); + + writel(dram_para.mr0, &mctl_phy->mr0); + writel(dram_para.mr1, &mctl_phy->mr1); + writel(dram_para.mr2, &mctl_phy->mr2); + writel(dram_para.mr3, &mctl_phy->mr3); + + if (!(dram_para.tpr13 & 0x10000)) { + clrsetbits_le32(&mctl_phy->dx0gcr, 0x3800, 0x2000); + clrsetbits_le32(&mctl_phy->dx1gcr, 0x3800, 0x2000); + } + + /* + * All the masking and shifting below converts what I assume are DDR + * timing constants from Allwinner dram_para tpr format to the actual + * timing registers format. + */ + + writel((dram_para.tpr0 & 0x000fffff), &mctl_phy->ptr2); + writel((dram_para.tpr1 & 0x1fffffff), &mctl_phy->ptr3); + writel((dram_para.tpr0 & 0x3ff00000) >> 2 | + (dram_para.tpr2 & 0x0003ffff), &mctl_phy->ptr4); + + writel(dram_para.tpr3, &mctl_phy->dtpr0); + writel(dram_para.tpr4, &mctl_phy->dtpr2); + + writel(0x01000081, &mctl_phy->dtcr); + + if (dram_para.clock <= 240 || !(dram_para.odt_en & 0x01)) { + clrbits_le32(&mctl_phy->dx0gcr, 0x600); + clrbits_le32(&mctl_phy->dx1gcr, 0x600); + } + if (dram_para.clock <= 240) { + writel(0, &mctl_phy->odtcr); + writel(0, &mctl_ctl->odtmap); + } + + writel(((dram_para.tpr5 & 0x0f00) << 12) | + ((dram_para.tpr5 & 0x00f8) << 9) | + ((dram_para.tpr5 & 0x0007) << 8), + &mctl_ctl->rfshctl0); + + writel(((dram_para.tpr5 & 0x0003f000) << 12) | + ((dram_para.tpr5 & 0x00fc0000) >> 2) | + ((dram_para.tpr5 & 0x3f000000) >> 16) | + ((dram_para.tpr6 & 0x0000003f) >> 0), + &mctl_ctl->dramtmg0); + + writel(((dram_para.tpr6 & 0x000007c0) << 10) | + ((dram_para.tpr6 & 0x0000f800) >> 3) | + ((dram_para.tpr6 & 0x003f0000) >> 16), + &mctl_ctl->dramtmg1); + + writel(((dram_para.tpr6 & 0x0fc00000) << 2) | + ((dram_para.tpr7 & 0x0000001f) << 16) | + ((dram_para.tpr7 & 0x000003e0) << 3) | + ((dram_para.tpr7 & 0x0000fc00) >> 10), + &mctl_ctl->dramtmg2); + + writel(((dram_para.tpr7 & 0x03ff0000) >> 16) | + ((dram_para.tpr6 & 0xf0000000) >> 16), + &mctl_ctl->dramtmg3); + + writel(((dram_para.tpr7 & 0x3c000000) >> 2 ) | + ((dram_para.tpr8 & 0x00000007) << 16) | + ((dram_para.tpr8 & 0x00000038) << 5) | + ((dram_para.tpr8 & 0x000003c0) >> 6), + &mctl_ctl->dramtmg4); + + writel(((dram_para.tpr8 & 0x00003c00) << 14) | + ((dram_para.tpr8 & 0x0003c000) << 2) | + ((dram_para.tpr8 & 0x00fc0000) >> 10) | + ((dram_para.tpr8 & 0x0f000000) >> 24), + &mctl_ctl->dramtmg5); + + writel(0x00000008, &mctl_ctl->dramtmg8); + + writel(((dram_para.tpr8 & 0xf0000000) >> 4) | + ((dram_para.tpr9 & 0x00007c00) << 6) | + ((dram_para.tpr9 & 0x000003e0) << 3) | + ((dram_para.tpr9 & 0x0000001f) >> 0), + &mctl_ctl->pitmg0); + + setbits_le32(&mctl_ctl->pitmg1, 0x80000); + + writel(((dram_para.tpr9 & 0x003f8000) << 9) | 0x2001, + &mctl_ctl->sched); + + writel((dram_para.mr0 << 16) | dram_para.mr1, &mctl_ctl->init3); + writel((dram_para.mr2 << 16) | dram_para.mr3, &mctl_ctl->init4); + + writel(0x00000000, &mctl_ctl->pimisc); + writel(0x80000000, &mctl_ctl->upd0); + + writel(((dram_para.tpr9 & 0xffc00000) >> 22) | + ((dram_para.tpr10 & 0x00000fff) << 16), + &mctl_ctl->rfshtmg); + + if (dram_para.tpr13 & 0x20) + writel(0x01040001, &mctl_ctl->mstr); + else + writel(0x01040401, &mctl_ctl->mstr); + + if (!(dram_para.tpr13 & 0x20000)) { + writel(0x00000002, &mctl_ctl->pwrctl); + writel(0x00008001, &mctl_ctl->pwrtmg); + } + + writel(0x00000001, &mctl_ctl->rfshctl3); + writel(0x00000001, &mctl_ctl->pimisc); + + /* deassert dram_clk_cfg reset */ + setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST); + + setbits_le32(&mctl_com->ccr, 0x80000); + + /* zq stuff */ + writel((dram_para.zq >> 8) & 0xff, &mctl_phy->zqcr1); + + writel(0x00000003, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr0, 0x09, 0x09); + + writel(readl(&mctl_phy->zqsr0) | 0x10000000, &mctl_phy->zqcr2); + writel(dram_para.zq & 0xff, &mctl_phy->zqcr1); + + /* A23-v1.0 SDK uses 0xfdf3, A23-v2.0 SDK uses 0x5f3 */ + writel(0x000005f3, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr0, 0x03, 0x03); + + if (readl(&mctl_phy->dx1gsr0) & 0x1000000) { + *bus_width = 8; + writel(0, &mctl_phy->dx1gcr); + writel(dram_para.zq & 0xff, &mctl_phy->zqcr1); + writel(0x5f3, &mctl_phy->pir); + udelay(10000); + setbits_le32(&mctl_ctl->mstr, 0x1000); + } else + *bus_width = 16; + + correction = (dram_para.odt_en >> 8) & 0xff; + if (correction) { + if (dram_para.odt_en & 0x80000000) + correction = -correction; + + mctl_apply_odt_correction(&mctl_phy->dx0lcdlr1, correction); + mctl_apply_odt_correction(&mctl_phy->dx1lcdlr1, correction); + } + + mctl_await_completion(&mctl_ctl->statr, 0x01, 0x01); + + writel(0x08003e3f, &mctl_phy->pgcr0); + writel(0x00000000, &mctl_ctl->rfshctl3); +} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + const u32 columns = 13; + u32 bus, bus_width, offset, page_size, rows; + + mctl_sys_init(); + mctl_init(&bus_width); + + if (bus_width == 16) { + page_size = 8; + bus = 1; + } else { + page_size = 7; + bus = 0; + } + + if (!(dram_para.tpr13 & 0x80000000)) { + /* Detect and set rows */ + writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size), + &mctl_com->cr); + setbits_le32(&mctl_com->swonr, 0x0003ffff); + mctl_mem_fill(); + for (rows = 11; rows < 16; rows++) { + offset = 1 << (rows + columns + bus); + if (mctl_mem_matches(offset)) + break; + } + clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK, + MCTL_CR_ROW(rows)); + } else { + rows = (dram_para.para1 >> 16) & 0xff; + writel(((dram_para.para2 & 0x000000f0) << 11) | + ((rows - 1) << 4) | + ((dram_para.para1 & 0x0f000000) >> 22) | + 0x31000 | MCTL_CR_PAGE_SIZE(page_size), + &mctl_com->cr); + setbits_le32(&mctl_com->swonr, 0x0003ffff); + } + + /* Setup DRAM master priority? If this is left out things still work */ + writel(0x00000008, &mctl_com->mcr0_0); + writel(0x0001000d, &mctl_com->mcr1_0); + writel(0x00000004, &mctl_com->mcr0_1); + writel(0x00000080, &mctl_com->mcr1_1); + writel(0x00000004, &mctl_com->mcr0_2); + writel(0x00000019, &mctl_com->mcr1_2); + writel(0x00000004, &mctl_com->mcr0_3); + writel(0x00000080, &mctl_com->mcr1_3); + writel(0x00000004, &mctl_com->mcr0_4); + writel(0x01010040, &mctl_com->mcr1_4); + writel(0x00000004, &mctl_com->mcr0_5); + writel(0x0001002f, &mctl_com->mcr1_5); + writel(0x00000004, &mctl_com->mcr0_6); + writel(0x00010020, &mctl_com->mcr1_6); + writel(0x00000004, &mctl_com->mcr0_7); + writel(0x00010020, &mctl_com->mcr1_7); + writel(0x00000008, &mctl_com->mcr0_8); + writel(0x00000001, &mctl_com->mcr1_8); + writel(0x00000008, &mctl_com->mcr0_9); + writel(0x00000005, &mctl_com->mcr1_9); + writel(0x00000008, &mctl_com->mcr0_10); + writel(0x00000003, &mctl_com->mcr1_10); + writel(0x00000008, &mctl_com->mcr0_11); + writel(0x00000005, &mctl_com->mcr1_11); + writel(0x00000008, &mctl_com->mcr0_12); + writel(0x00000003, &mctl_com->mcr1_12); + writel(0x00000008, &mctl_com->mcr0_13); + writel(0x00000004, &mctl_com->mcr1_13); + writel(0x00000008, &mctl_com->mcr0_14); + writel(0x00000002, &mctl_com->mcr1_14); + writel(0x00000008, &mctl_com->mcr0_15); + writel(0x00000003, &mctl_com->mcr1_15); + writel(0x00010138, &mctl_com->bwcr); + + return 1 << (rows + columns + bus); +} diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 7d61216..45a199c 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -273,7 +273,11 @@ struct sunxi_ccm_reg { #define CCM_HDMI_CTRL_DDC_GATE (0x1 << 30) #define CCM_HDMI_CTRL_GATE (0x1 << 31)
+#ifndef CONFIG_MACH_SUN8I #define MBUS_CLK_DEFAULT 0x81000001 /* PLL6 / 2 */ +#else +#define MBUS_CLK_DEFAULT 0x81000003 /* PLL6 / 4 */ +#endif
#define CCM_PLL5_PATTERN 0xd1303333
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index a8a37d5..8d78029 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -18,6 +18,8 @@ /* dram regs definition */ #if defined(CONFIG_MACH_SUN6I) #include <asm/arch/dram_sun6i.h> +#elif defined(CONFIG_MACH_SUN8I) +#include <asm/arch/dram_sun8i.h> #else #include <asm/arch/dram_sun4i.h> #endif diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun8i.h b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h new file mode 100644 index 0000000..425cf37 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h @@ -0,0 +1,266 @@ +/* + * Sun8i platform dram controller register and constant defines + * + * (C) Copyright 2007-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * CPL cplanxy@allwinnertech.com + * Jerry Wang wangflord@allwinnertech.com + * + * (C) Copyright 2014 Hans de Goede hdegoede@redhat.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_DRAM_SUN8I_H +#define _SUNXI_DRAM_SUN8I_H + +struct dram_para { + u32 clock; + u32 type; + u32 zq; + u32 odt_en; + u32 para1; + u32 para2; + u32 mr0; + u32 mr1; + u32 mr2; + u32 mr3; + u32 tpr0; + u32 tpr1; + u32 tpr2; + u32 tpr3; + u32 tpr4; + u32 tpr5; + u32 tpr6; + u32 tpr7; + u32 tpr8; + u32 tpr9; + u32 tpr10; + u32 tpr11; + u32 tpr12; + u32 tpr13; +}; + +struct sunxi_mctl_com_reg { + u32 cr; /* 0x00 */ + u32 ccr; /* 0x04 controller configuration register */ + u32 dbgcr; /* 0x08 */ + u8 res0[0x4]; /* 0x0c */ + u32 mcr0_0; /* 0x10 */ + u32 mcr1_0; /* 0x14 */ + u32 mcr0_1; /* 0x18 */ + u32 mcr1_1; /* 0x1c */ + u32 mcr0_2; /* 0x20 */ + u32 mcr1_2; /* 0x24 */ + u32 mcr0_3; /* 0x28 */ + u32 mcr1_3; /* 0x2c */ + u32 mcr0_4; /* 0x30 */ + u32 mcr1_4; /* 0x34 */ + u32 mcr0_5; /* 0x38 */ + u32 mcr1_5; /* 0x3c */ + u32 mcr0_6; /* 0x40 */ + u32 mcr1_6; /* 0x44 */ + u32 mcr0_7; /* 0x48 */ + u32 mcr1_7; /* 0x4c */ + u32 mcr0_8; /* 0x50 */ + u32 mcr1_8; /* 0x54 */ + u32 mcr0_9; /* 0x58 */ + u32 mcr1_9; /* 0x5c */ + u32 mcr0_10; /* 0x60 */ + u32 mcr1_10; /* 0x64 */ + u32 mcr0_11; /* 0x68 */ + u32 mcr1_11; /* 0x6c */ + u32 mcr0_12; /* 0x70 */ + u32 mcr1_12; /* 0x74 */ + u32 mcr0_13; /* 0x78 */ + u32 mcr1_13; /* 0x7c */ + u32 mcr0_14; /* 0x80 */ + u32 mcr1_14; /* 0x84 */ + u32 mcr0_15; /* 0x88 */ + u32 mcr1_15; /* 0x8c */ + u32 bwcr; /* 0x90 */ + u32 maer; /* 0x94 */ + u8 res1[0x4]; /* 0x98 */ + u32 mcgcr; /* 0x9c */ + u32 bwctr; /* 0xa0 */ + u8 res2[0x4]; /* 0xa4 */ + u32 swonr; /* 0xa8 */ + u32 swoffr; /* 0xac */ +}; + +struct sunxi_mctl_ctl_reg { + u32 mstr; /* 0x00 */ + u32 statr; /* 0x04 */ + u8 res0[0x08]; /* 0x08 */ + u32 mrctrl0; /* 0x10 */ + u32 mrctrl1; /* 0x14 */ + u32 mrstatr; /* 0x18 */ + u8 res1[0x04]; /* 0x1c */ + u32 derateen; /* 0x20 */ + u32 deratenint; /* 0x24 */ + u8 res2[0x08]; /* 0x28 */ + u32 pwrctl; /* 0x30 */ + u32 pwrtmg; /* 0x34 */ + u8 res3[0x18]; /* 0x38 */ + u32 rfshctl0; /* 0x50 */ + u32 rfshctl1; /* 0x54 */ + u8 res4[0x8]; /* 0x58 */ + u32 rfshctl3; /* 0x60 */ + u32 rfshtmg; /* 0x64 */ + u8 res6[0x68]; /* 0x68 */ + u32 init0; /* 0xd0 */ + u32 init1; /* 0xd4 */ + u32 init2; /* 0xd8 */ + u32 init3; /* 0xdc */ + u32 init4; /* 0xe0 */ + u32 init5; /* 0xe4 */ + u8 res7[0x0c]; /* 0xe8 */ + u32 rankctl; /* 0xf4 */ + u8 res8[0x08]; /* 0xf8 */ + u32 dramtmg0; /* 0x100 */ + u32 dramtmg1; /* 0x104 */ + u32 dramtmg2; /* 0x108 */ + u32 dramtmg3; /* 0x10c */ + u32 dramtmg4; /* 0x110 */ + u32 dramtmg5; /* 0x114 */ + u32 dramtmg6; /* 0x118 */ + u32 dramtmg7; /* 0x11c */ + u32 dramtmg8; /* 0x120 */ + u8 res9[0x5c]; /* 0x124 */ + u32 zqctl0; /* 0x180 */ + u32 zqctl1; /* 0x184 */ + u32 zqctl2; /* 0x188 */ + u32 zqstat; /* 0x18c */ + u32 pitmg0; /* 0x190 */ + u32 pitmg1; /* 0x194 */ + u32 plpcfg0; /* 0x198 */ + u8 res10[0x04]; /* 0x19c */ + u32 upd0; /* 0x1a0 */ + u32 upd1; /* 0x1a4 */ + u32 upd2; /* 0x1a8 */ + u32 upd3; /* 0x1ac */ + u32 pimisc; /* 0x1b0 */ + u8 res11[0x1c]; /* 0x1b4 */ + u32 trainctl0; /* 0x1d0 */ + u32 trainctl1; /* 0x1d4 */ + u32 trainctl2; /* 0x1d8 */ + u32 trainstat; /* 0x1dc */ + u8 res12[0x60]; /* 0x1e0 */ + u32 odtcfg; /* 0x240 */ + u32 odtmap; /* 0x244 */ + u8 res13[0x08]; /* 0x248 */ + u32 sched; /* 0x250 */ + u8 res14[0x04]; /* 0x254 */ + u32 perfshpr0; /* 0x258 */ + u32 perfshpr1; /* 0x25c */ + u32 perflpr0; /* 0x260 */ + u32 perflpr1; /* 0x264 */ + u32 perfwr0; /* 0x268 */ + u32 perfwr1; /* 0x26c */ +}; + +struct sunxi_mctl_phy_reg { + u8 res0[0x04]; /* 0x00 */ + u32 pir; /* 0x04 */ + u32 pgcr0; /* 0x08 phy general configuration register */ + u32 pgcr1; /* 0x0c phy general configuration register */ + u32 pgsr0; /* 0x10 */ + u32 pgsr1; /* 0x14 */ + u32 dllgcr; /* 0x18 */ + u32 ptr0; /* 0x1c */ + u32 ptr1; /* 0x20 */ + u32 ptr2; /* 0x24 */ + u32 ptr3; /* 0x28 */ + u32 ptr4; /* 0x2c */ + u32 acmdlr; /* 0x30 */ + u32 acbdlr; /* 0x34 */ + u32 aciocr; /* 0x38 */ + u32 dxccr; /* 0x3c DATX8 common configuration register */ + u32 dsgcr; /* 0x40 dram system general config register */ + u32 dcr; /* 0x44 */ + u32 dtpr0; /* 0x48 dram timing parameters register 0 */ + u32 dtpr1; /* 0x4c dram timing parameters register 1 */ + u32 dtpr2; /* 0x50 dram timing parameters register 2 */ + u32 mr0; /* 0x54 mode register 0 */ + u32 mr1; /* 0x58 mode register 1 */ + u32 mr2; /* 0x5c mode register 2 */ + u32 mr3; /* 0x60 mode register 3 */ + u32 odtcr; /* 0x64 */ + u32 dtcr; /* 0x68 */ + u32 dtar0; /* 0x6c data training address register 0 */ + u32 dtar1; /* 0x70 data training address register 1 */ + u32 dtar2; /* 0x74 data training address register 2 */ + u32 dtar3; /* 0x78 data training address register 3 */ + u32 dtdr0; /* 0x7c */ + u32 dtdr1; /* 0x80 */ + u32 dtedr0; /* 0x84 */ + u32 dtedr1; /* 0x88 */ + u32 pgcr2; /* 0x8c */ + u8 res1[0x70]; /* 0x90 */ + u32 bistrr; /* 0x100 */ + u32 bistwcr; /* 0x104 */ + u32 bistmskr0; /* 0x108 */ + u32 bistmskr1; /* 0x10c */ + u32 bistmskr2; /* 0x110 */ + u32 bistlsr; /* 0x114 */ + u32 bistar0; /* 0x118 */ + u32 bistar1; /* 0x11c */ + u32 bistar2; /* 0x120 */ + u32 bistupdr; /* 0x124 */ + u32 bistgsr; /* 0x128 */ + u32 bistwer; /* 0x12c */ + u32 bistber0; /* 0x130 */ + u32 bistber1; /* 0x134 */ + u32 bistber2; /* 0x138 */ + u32 bistber3; /* 0x13c */ + u32 bistwcsr; /* 0x140 */ + u32 bistfwr0; /* 0x144 */ + u32 bistfwr1; /* 0x148 */ + u32 bistfwr2; /* 0x14c */ + u8 res2[0x30]; /* 0x150 */ + u32 zqcr0; /* 0x180 zq control register 0 */ + u32 zqcr1; /* 0x184 zq control register 1 */ + u32 zqsr0; /* 0x188 zq status register 0 */ + u32 zqsr1; /* 0x18c zq status register 1 */ + u32 zqcr2; /* 0x190 zq control register 2 */ + u8 res3[0x2c]; /* 0x194 */ + u32 dx0gcr; /* 0x1c0 */ + u32 dx0gsr0; /* 0x1c4 */ + u32 dx0gsr1; /* 0x1c8 */ + u32 dx0bdlr0; /* 0x1cc */ + u32 dx0bdlr1; /* 0x1d0 */ + u32 dx0bdlr2; /* 0x1d4 */ + u32 dx0bdlr3; /* 0x1d8 */ + u32 dx0bdlr4; /* 0x1dc */ + u32 dx0lcdlr0; /* 0x1e0 */ + u32 dx0lcdlr1; /* 0x1e4 */ + u32 dx0lcdlr2; /* 0x1e8 */ + u32 dx0mdlr; /* 0x1ec */ + u32 dx0gtr; /* 0x1f0 */ + u32 dx0gsr2; /* 0x1f4 */ + u8 res4[0x08]; /* 0x1f8 */ + u32 dx1gcr; /* 0x200 */ + u32 dx1gsr0; /* 0x204 */ + u32 dx1gsr1; /* 0x208 */ + u32 dx1bdlr0; /* 0x20c */ + u32 dx1bdlr1; /* 0x210 */ + u32 dx1bdlr2; /* 0x214 */ + u32 dx1bdlr3; /* 0x218 */ + u32 dx1bdlr4; /* 0x21c */ + u32 dx1lcdlr0; /* 0x220 */ + u32 dx1lcdlr1; /* 0x224 */ + u32 dx1lcdlr2; /* 0x228 */ + u32 dx1mdlr; /* 0x22c */ + u32 dx1gtr; /* 0x230 */ + u32 dx1gsr2; /* 0x234 */ +}; + +/* + * DRAM common (sunxi_mctl_com_reg) register constants. + */ +#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) ((x) << 8) + +#endif /* _SUNXI_DRAM_SUN8I_H */ diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 5bb2f58..5a88ba0 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -29,10 +29,11 @@ config MACH_SUN7I config MACH_SUN8I bool "sun8i (Allwinner A23)" select CPU_V7 + select SUPPORT_SPL
endchoice
-if MACH_SUN6I +if MACH_SUN6I || MACH_SUN8I
config DRAM_CLK int "sun6i dram clock speed" diff --git a/configs/Ippo_q8h_v5_defconfig b/configs/Ippo_q8h_v5_defconfig index 50c2f93..37aa46d 100644 --- a/configs/Ippo_q8h_v5_defconfig +++ b/configs/Ippo_q8h_v5_defconfig @@ -1,8 +1,15 @@ +CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" -CONFIG_ARM=y -CONFIG_ARCH_SUNXI=y -CONFIG_MACH_SUN8I=y -CONFIG_TARGET_IPPO_Q8H_V5=y -CONFIG_DEFAULT_DEVICE_TREE="sun8i-a23-ippo-q8h-v5.dtb" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v5.dtb" CONFIG_VIDEO=n CONFIG_USB_KEYBOARD=n ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN8I=y ++S:CONFIG_DRAM_CLK=480 +# zq = 0xf777 ++S:CONFIG_DRAM_ZQ=63351 +# Wifi power ++S:CONFIG_AXP221_DLDO1_VOLT=3300 +# aldo1 is connected to VCC-IO, VCC-PD, VCC-USB and VCC-HP ++S:CONFIG_AXP221_ALDO1_VOLT=3000 diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 6f1fc48..792422d 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -12,6 +12,8 @@ /* * A23 specific configuration */ +#define CONFIG_CLK_FULL_SPEED 1008000000 + #define CONFIG_SYS_PROMPT "sun8i# "
/*

On Wed, Dec 17, 2014 at 4:31 AM, Hans de Goede hdegoede@redhat.com wrote:
Based on the register / dram_para headers from the Allwinner u-boot / linux sources + the init sequences from boot0.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/board.c | 3 +- arch/arm/cpu/armv7/sunxi/dram_sun8i.c | 340 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 4 + arch/arm/include/asm/arch-sunxi/dram.h | 2 + arch/arm/include/asm/arch-sunxi/dram_sun8i.h | 266 ++++++++++++++++++++ board/sunxi/Kconfig | 3 +- configs/Ippo_q8h_v5_defconfig | 17 +- include/configs/sun8i.h | 2 + 9 files changed, 631 insertions(+), 7 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun8i.c create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun8i.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 3e8975a..1e89937 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_MACH_SUN4I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN5I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN6I) += dram_sun6i.o obj-$(CONFIG_MACH_SUN7I) += dram_sun4i.o +obj-$(CONFIG_MACH_SUN8I) += dram_sun8i.o ifdef CONFIG_SPL_FEL obj-y += start.o endif diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 9b3e80c..bc98c56 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -114,7 +114,8 @@ void reset_cpu(ulong addr) /* do some early init */ void s_init(void) { -#if defined CONFIG_SPL_BUILD && defined CONFIG_MACH_SUN6I +#if defined CONFIG_SPL_BUILD && \
(defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I) /* Magic (undocmented) value taken from boot0, without this DRAM * access gets messed up (seems cache related) */ setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800);
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun8i.c b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c new file mode 100644 index 0000000..3736fd1 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c @@ -0,0 +1,340 @@ +/*
- Sun8i platform dram controller init.
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- SPDX-License-Identifier: GPL-2.0+
- */
+/*
- Note this code uses a lot of magic hex values, that is because this code
- simply replays the init sequence as done by the Allwinner boot0 code, so
- we do not know what these values mean. There are no symbolic constants for
- these magic values, since we do not know how to name them and making up
- names for them is not useful.
- */
+#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h>
+static const struct dram_para dram_para = {
.clock = CONFIG_DRAM_CLK,
.type = 3,
.zq = CONFIG_DRAM_ZQ,
.odt_en = 1,
.para1 = 0, /* not used (only used when tpr13 bit 31 is set */
.para2 = 0, /* not used (only used when tpr13 bit 31 is set */
.mr0 = 6736,
.mr1 = 4,
.mr2 = 16,
.mr3 = 0,
/* tpr0 - 10 contain timing constants or-ed together in u32 vals */
.tpr0 = 0x2ab83def,
.tpr1 = 0x18082356,
.tpr2 = 0x00034156,
.tpr3 = 0x448c5533,
.tpr4 = 0x08010d00,
.tpr5 = 0x0340b20f,
.tpr6 = 0x20d118cc,
.tpr7 = 0x14062485,
.tpr8 = 0x220d1d52,
.tpr9 = 0x1e078c22,
.tpr10 = 0x3c,
.tpr11 = 0, /* not used */
.tpr12 = 0, /* not used */
.tpr13 = 0x30000,
+};
+static void mctl_sys_init(void) +{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* enable pll5, note the divide by 2 is deliberate! */
clock_set_pll5(dram_para.clock * 1000000 / 2, 1, 2,
dram_para.tpr13 & 0x40000);
/* deassert ahb mctl reset */
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
/* enable ahb mctl clock */
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+}
+static void mctl_apply_odt_correction(u32 *reg, int correction) +{
int val;
val = (readl(reg) >> 8) & 0xff;
val += correction;
/* clamp */
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
clrsetbits_le32(reg, 0xff00, val << 8);
+}
+static void mctl_init(u32 *bus_width) +{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
int correction;
if (dram_para.tpr13 & 0x20)
writel(0x40b, &mctl_phy->dcr);
else
writel(0x1000040b, &mctl_phy->dcr);
if (dram_para.clock >= 480)
writel(0x5c000, &mctl_phy->dllgcr);
else
writel(0xdc000, &mctl_phy->dllgcr);
writel(0x0a003e3f, &mctl_phy->pgcr0);
writel(0x03008421, &mctl_phy->pgcr1);
writel(dram_para.mr0, &mctl_phy->mr0);
writel(dram_para.mr1, &mctl_phy->mr1);
writel(dram_para.mr2, &mctl_phy->mr2);
writel(dram_para.mr3, &mctl_phy->mr3);
if (!(dram_para.tpr13 & 0x10000)) {
clrsetbits_le32(&mctl_phy->dx0gcr, 0x3800, 0x2000);
clrsetbits_le32(&mctl_phy->dx1gcr, 0x3800, 0x2000);
}
/*
* All the masking and shifting below converts what I assume are DDR
* timing constants from Allwinner dram_para tpr format to the actual
* timing registers format.
*/
writel((dram_para.tpr0 & 0x000fffff), &mctl_phy->ptr2);
writel((dram_para.tpr1 & 0x1fffffff), &mctl_phy->ptr3);
writel((dram_para.tpr0 & 0x3ff00000) >> 2 |
(dram_para.tpr2 & 0x0003ffff), &mctl_phy->ptr4);
writel(dram_para.tpr3, &mctl_phy->dtpr0);
writel(dram_para.tpr4, &mctl_phy->dtpr2);
writel(0x01000081, &mctl_phy->dtcr);
if (dram_para.clock <= 240 || !(dram_para.odt_en & 0x01)) {
clrbits_le32(&mctl_phy->dx0gcr, 0x600);
clrbits_le32(&mctl_phy->dx1gcr, 0x600);
}
if (dram_para.clock <= 240) {
writel(0, &mctl_phy->odtcr);
writel(0, &mctl_ctl->odtmap);
}
writel(((dram_para.tpr5 & 0x0f00) << 12) |
((dram_para.tpr5 & 0x00f8) << 9) |
((dram_para.tpr5 & 0x0007) << 8),
&mctl_ctl->rfshctl0);
writel(((dram_para.tpr5 & 0x0003f000) << 12) |
((dram_para.tpr5 & 0x00fc0000) >> 2) |
((dram_para.tpr5 & 0x3f000000) >> 16) |
((dram_para.tpr6 & 0x0000003f) >> 0),
&mctl_ctl->dramtmg0);
writel(((dram_para.tpr6 & 0x000007c0) << 10) |
((dram_para.tpr6 & 0x0000f800) >> 3) |
((dram_para.tpr6 & 0x003f0000) >> 16),
&mctl_ctl->dramtmg1);
writel(((dram_para.tpr6 & 0x0fc00000) << 2) |
((dram_para.tpr7 & 0x0000001f) << 16) |
((dram_para.tpr7 & 0x000003e0) << 3) |
((dram_para.tpr7 & 0x0000fc00) >> 10),
&mctl_ctl->dramtmg2);
writel(((dram_para.tpr7 & 0x03ff0000) >> 16) |
((dram_para.tpr6 & 0xf0000000) >> 16),
&mctl_ctl->dramtmg3);
writel(((dram_para.tpr7 & 0x3c000000) >> 2 ) |
((dram_para.tpr8 & 0x00000007) << 16) |
((dram_para.tpr8 & 0x00000038) << 5) |
((dram_para.tpr8 & 0x000003c0) >> 6),
&mctl_ctl->dramtmg4);
writel(((dram_para.tpr8 & 0x00003c00) << 14) |
((dram_para.tpr8 & 0x0003c000) << 2) |
((dram_para.tpr8 & 0x00fc0000) >> 10) |
((dram_para.tpr8 & 0x0f000000) >> 24),
&mctl_ctl->dramtmg5);
writel(0x00000008, &mctl_ctl->dramtmg8);
writel(((dram_para.tpr8 & 0xf0000000) >> 4) |
((dram_para.tpr9 & 0x00007c00) << 6) |
((dram_para.tpr9 & 0x000003e0) << 3) |
((dram_para.tpr9 & 0x0000001f) >> 0),
&mctl_ctl->pitmg0);
setbits_le32(&mctl_ctl->pitmg1, 0x80000);
writel(((dram_para.tpr9 & 0x003f8000) << 9) | 0x2001,
&mctl_ctl->sched);
writel((dram_para.mr0 << 16) | dram_para.mr1, &mctl_ctl->init3);
writel((dram_para.mr2 << 16) | dram_para.mr3, &mctl_ctl->init4);
writel(0x00000000, &mctl_ctl->pimisc);
writel(0x80000000, &mctl_ctl->upd0);
writel(((dram_para.tpr9 & 0xffc00000) >> 22) |
((dram_para.tpr10 & 0x00000fff) << 16),
&mctl_ctl->rfshtmg);
if (dram_para.tpr13 & 0x20)
writel(0x01040001, &mctl_ctl->mstr);
else
writel(0x01040401, &mctl_ctl->mstr);
if (!(dram_para.tpr13 & 0x20000)) {
writel(0x00000002, &mctl_ctl->pwrctl);
writel(0x00008001, &mctl_ctl->pwrtmg);
}
writel(0x00000001, &mctl_ctl->rfshctl3);
writel(0x00000001, &mctl_ctl->pimisc);
/* deassert dram_clk_cfg reset */
setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
setbits_le32(&mctl_com->ccr, 0x80000);
/* zq stuff */
writel((dram_para.zq >> 8) & 0xff, &mctl_phy->zqcr1);
writel(0x00000003, &mctl_phy->pir);
udelay(10);
mctl_await_completion(&mctl_phy->pgsr0, 0x09, 0x09);
writel(readl(&mctl_phy->zqsr0) | 0x10000000, &mctl_phy->zqcr2);
writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
/* A23-v1.0 SDK uses 0xfdf3, A23-v2.0 SDK uses 0x5f3 */
writel(0x000005f3, &mctl_phy->pir);
udelay(10);
mctl_await_completion(&mctl_phy->pgsr0, 0x03, 0x03);
if (readl(&mctl_phy->dx1gsr0) & 0x1000000) {
*bus_width = 8;
writel(0, &mctl_phy->dx1gcr);
writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
writel(0x5f3, &mctl_phy->pir);
udelay(10000);
setbits_le32(&mctl_ctl->mstr, 0x1000);
} else
*bus_width = 16;
correction = (dram_para.odt_en >> 8) & 0xff;
if (correction) {
if (dram_para.odt_en & 0x80000000)
correction = -correction;
mctl_apply_odt_correction(&mctl_phy->dx0lcdlr1, correction);
mctl_apply_odt_correction(&mctl_phy->dx1lcdlr1, correction);
}
mctl_await_completion(&mctl_ctl->statr, 0x01, 0x01);
writel(0x08003e3f, &mctl_phy->pgcr0);
writel(0x00000000, &mctl_ctl->rfshctl3);
+}
+unsigned long sunxi_dram_init(void) +{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
const u32 columns = 13;
u32 bus, bus_width, offset, page_size, rows;
mctl_sys_init();
mctl_init(&bus_width);
if (bus_width == 16) {
page_size = 8;
bus = 1;
} else {
page_size = 7;
bus = 0;
}
if (!(dram_para.tpr13 & 0x80000000)) {
/* Detect and set rows */
writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
mctl_mem_fill();
for (rows = 11; rows < 16; rows++) {
offset = 1 << (rows + columns + bus);
if (mctl_mem_matches(offset))
break;
}
clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK,
MCTL_CR_ROW(rows));
} else {
rows = (dram_para.para1 >> 16) & 0xff;
writel(((dram_para.para2 & 0x000000f0) << 11) |
((rows - 1) << 4) |
((dram_para.para1 & 0x0f000000) >> 22) |
0x31000 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
}
/* Setup DRAM master priority? If this is left out things still work */
writel(0x00000008, &mctl_com->mcr0_0);
writel(0x0001000d, &mctl_com->mcr1_0);
writel(0x00000004, &mctl_com->mcr0_1);
writel(0x00000080, &mctl_com->mcr1_1);
writel(0x00000004, &mctl_com->mcr0_2);
writel(0x00000019, &mctl_com->mcr1_2);
writel(0x00000004, &mctl_com->mcr0_3);
writel(0x00000080, &mctl_com->mcr1_3);
writel(0x00000004, &mctl_com->mcr0_4);
writel(0x01010040, &mctl_com->mcr1_4);
writel(0x00000004, &mctl_com->mcr0_5);
writel(0x0001002f, &mctl_com->mcr1_5);
writel(0x00000004, &mctl_com->mcr0_6);
writel(0x00010020, &mctl_com->mcr1_6);
writel(0x00000004, &mctl_com->mcr0_7);
writel(0x00010020, &mctl_com->mcr1_7);
writel(0x00000008, &mctl_com->mcr0_8);
writel(0x00000001, &mctl_com->mcr1_8);
writel(0x00000008, &mctl_com->mcr0_9);
writel(0x00000005, &mctl_com->mcr1_9);
writel(0x00000008, &mctl_com->mcr0_10);
writel(0x00000003, &mctl_com->mcr1_10);
writel(0x00000008, &mctl_com->mcr0_11);
writel(0x00000005, &mctl_com->mcr1_11);
writel(0x00000008, &mctl_com->mcr0_12);
writel(0x00000003, &mctl_com->mcr1_12);
writel(0x00000008, &mctl_com->mcr0_13);
writel(0x00000004, &mctl_com->mcr1_13);
writel(0x00000008, &mctl_com->mcr0_14);
writel(0x00000002, &mctl_com->mcr1_14);
writel(0x00000008, &mctl_com->mcr0_15);
writel(0x00000003, &mctl_com->mcr1_15);
writel(0x00010138, &mctl_com->bwcr);
return 1 << (rows + columns + bus);
+} diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 7d61216..45a199c 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -273,7 +273,11 @@ struct sunxi_ccm_reg { #define CCM_HDMI_CTRL_DDC_GATE (0x1 << 30) #define CCM_HDMI_CTRL_GATE (0x1 << 31)
+#ifndef CONFIG_MACH_SUN8I #define MBUS_CLK_DEFAULT 0x81000001 /* PLL6 / 2 */ +#else +#define MBUS_CLK_DEFAULT 0x81000003 /* PLL6 / 4 */ +#endif
#define CCM_PLL5_PATTERN 0xd1303333
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index a8a37d5..8d78029 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -18,6 +18,8 @@ /* dram regs definition */ #if defined(CONFIG_MACH_SUN6I) #include <asm/arch/dram_sun6i.h> +#elif defined(CONFIG_MACH_SUN8I) +#include <asm/arch/dram_sun8i.h> #else #include <asm/arch/dram_sun4i.h> #endif diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun8i.h b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h new file mode 100644 index 0000000..425cf37 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h @@ -0,0 +1,266 @@ +/*
- Sun8i platform dram controller register and constant defines
- (C) Copyright 2007-2013
- Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- Jerry Wang wangflord@allwinnertech.com
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef _SUNXI_DRAM_SUN8I_H +#define _SUNXI_DRAM_SUN8I_H
+struct dram_para {
u32 clock;
u32 type;
u32 zq;
u32 odt_en;
u32 para1;
u32 para2;
u32 mr0;
u32 mr1;
u32 mr2;
u32 mr3;
u32 tpr0;
u32 tpr1;
u32 tpr2;
u32 tpr3;
u32 tpr4;
u32 tpr5;
u32 tpr6;
u32 tpr7;
u32 tpr8;
u32 tpr9;
u32 tpr10;
u32 tpr11;
u32 tpr12;
u32 tpr13;
+};
+struct sunxi_mctl_com_reg {
u32 cr; /* 0x00 */
u32 ccr; /* 0x04 controller configuration register */
u32 dbgcr; /* 0x08 */
u8 res0[0x4]; /* 0x0c */
u32 mcr0_0; /* 0x10 */
u32 mcr1_0; /* 0x14 */
u32 mcr0_1; /* 0x18 */
u32 mcr1_1; /* 0x1c */
u32 mcr0_2; /* 0x20 */
u32 mcr1_2; /* 0x24 */
u32 mcr0_3; /* 0x28 */
u32 mcr1_3; /* 0x2c */
u32 mcr0_4; /* 0x30 */
u32 mcr1_4; /* 0x34 */
u32 mcr0_5; /* 0x38 */
u32 mcr1_5; /* 0x3c */
u32 mcr0_6; /* 0x40 */
u32 mcr1_6; /* 0x44 */
u32 mcr0_7; /* 0x48 */
u32 mcr1_7; /* 0x4c */
u32 mcr0_8; /* 0x50 */
u32 mcr1_8; /* 0x54 */
u32 mcr0_9; /* 0x58 */
u32 mcr1_9; /* 0x5c */
u32 mcr0_10; /* 0x60 */
u32 mcr1_10; /* 0x64 */
u32 mcr0_11; /* 0x68 */
u32 mcr1_11; /* 0x6c */
u32 mcr0_12; /* 0x70 */
u32 mcr1_12; /* 0x74 */
u32 mcr0_13; /* 0x78 */
u32 mcr1_13; /* 0x7c */
u32 mcr0_14; /* 0x80 */
u32 mcr1_14; /* 0x84 */
u32 mcr0_15; /* 0x88 */
u32 mcr1_15; /* 0x8c */
u32 bwcr; /* 0x90 */
u32 maer; /* 0x94 */
u8 res1[0x4]; /* 0x98 */
u32 mcgcr; /* 0x9c */
u32 bwctr; /* 0xa0 */
u8 res2[0x4]; /* 0xa4 */
u32 swonr; /* 0xa8 */
u32 swoffr; /* 0xac */
+};
+struct sunxi_mctl_ctl_reg {
u32 mstr; /* 0x00 */
u32 statr; /* 0x04 */
u8 res0[0x08]; /* 0x08 */
u32 mrctrl0; /* 0x10 */
u32 mrctrl1; /* 0x14 */
u32 mrstatr; /* 0x18 */
u8 res1[0x04]; /* 0x1c */
u32 derateen; /* 0x20 */
u32 deratenint; /* 0x24 */
u8 res2[0x08]; /* 0x28 */
u32 pwrctl; /* 0x30 */
u32 pwrtmg; /* 0x34 */
u8 res3[0x18]; /* 0x38 */
u32 rfshctl0; /* 0x50 */
u32 rfshctl1; /* 0x54 */
u8 res4[0x8]; /* 0x58 */
u32 rfshctl3; /* 0x60 */
u32 rfshtmg; /* 0x64 */
u8 res6[0x68]; /* 0x68 */
u32 init0; /* 0xd0 */
u32 init1; /* 0xd4 */
u32 init2; /* 0xd8 */
u32 init3; /* 0xdc */
u32 init4; /* 0xe0 */
u32 init5; /* 0xe4 */
u8 res7[0x0c]; /* 0xe8 */
u32 rankctl; /* 0xf4 */
u8 res8[0x08]; /* 0xf8 */
u32 dramtmg0; /* 0x100 */
u32 dramtmg1; /* 0x104 */
u32 dramtmg2; /* 0x108 */
u32 dramtmg3; /* 0x10c */
u32 dramtmg4; /* 0x110 */
u32 dramtmg5; /* 0x114 */
u32 dramtmg6; /* 0x118 */
u32 dramtmg7; /* 0x11c */
u32 dramtmg8; /* 0x120 */
u8 res9[0x5c]; /* 0x124 */
u32 zqctl0; /* 0x180 */
u32 zqctl1; /* 0x184 */
u32 zqctl2; /* 0x188 */
u32 zqstat; /* 0x18c */
u32 pitmg0; /* 0x190 */
u32 pitmg1; /* 0x194 */
u32 plpcfg0; /* 0x198 */
u8 res10[0x04]; /* 0x19c */
u32 upd0; /* 0x1a0 */
u32 upd1; /* 0x1a4 */
u32 upd2; /* 0x1a8 */
u32 upd3; /* 0x1ac */
u32 pimisc; /* 0x1b0 */
u8 res11[0x1c]; /* 0x1b4 */
u32 trainctl0; /* 0x1d0 */
u32 trainctl1; /* 0x1d4 */
u32 trainctl2; /* 0x1d8 */
u32 trainstat; /* 0x1dc */
u8 res12[0x60]; /* 0x1e0 */
u32 odtcfg; /* 0x240 */
u32 odtmap; /* 0x244 */
u8 res13[0x08]; /* 0x248 */
u32 sched; /* 0x250 */
u8 res14[0x04]; /* 0x254 */
u32 perfshpr0; /* 0x258 */
u32 perfshpr1; /* 0x25c */
u32 perflpr0; /* 0x260 */
u32 perflpr1; /* 0x264 */
u32 perfwr0; /* 0x268 */
u32 perfwr1; /* 0x26c */
+};
+struct sunxi_mctl_phy_reg {
u8 res0[0x04]; /* 0x00 */
u32 pir; /* 0x04 */
u32 pgcr0; /* 0x08 phy general configuration register */
u32 pgcr1; /* 0x0c phy general configuration register */
u32 pgsr0; /* 0x10 */
u32 pgsr1; /* 0x14 */
u32 dllgcr; /* 0x18 */
u32 ptr0; /* 0x1c */
u32 ptr1; /* 0x20 */
u32 ptr2; /* 0x24 */
u32 ptr3; /* 0x28 */
u32 ptr4; /* 0x2c */
u32 acmdlr; /* 0x30 */
u32 acbdlr; /* 0x34 */
u32 aciocr; /* 0x38 */
u32 dxccr; /* 0x3c DATX8 common configuration register */
u32 dsgcr; /* 0x40 dram system general config register */
u32 dcr; /* 0x44 */
u32 dtpr0; /* 0x48 dram timing parameters register 0 */
u32 dtpr1; /* 0x4c dram timing parameters register 1 */
u32 dtpr2; /* 0x50 dram timing parameters register 2 */
u32 mr0; /* 0x54 mode register 0 */
u32 mr1; /* 0x58 mode register 1 */
u32 mr2; /* 0x5c mode register 2 */
u32 mr3; /* 0x60 mode register 3 */
u32 odtcr; /* 0x64 */
u32 dtcr; /* 0x68 */
u32 dtar0; /* 0x6c data training address register 0 */
u32 dtar1; /* 0x70 data training address register 1 */
u32 dtar2; /* 0x74 data training address register 2 */
u32 dtar3; /* 0x78 data training address register 3 */
u32 dtdr0; /* 0x7c */
u32 dtdr1; /* 0x80 */
u32 dtedr0; /* 0x84 */
u32 dtedr1; /* 0x88 */
u32 pgcr2; /* 0x8c */
u8 res1[0x70]; /* 0x90 */
u32 bistrr; /* 0x100 */
u32 bistwcr; /* 0x104 */
u32 bistmskr0; /* 0x108 */
u32 bistmskr1; /* 0x10c */
u32 bistmskr2; /* 0x110 */
u32 bistlsr; /* 0x114 */
u32 bistar0; /* 0x118 */
u32 bistar1; /* 0x11c */
u32 bistar2; /* 0x120 */
u32 bistupdr; /* 0x124 */
u32 bistgsr; /* 0x128 */
u32 bistwer; /* 0x12c */
u32 bistber0; /* 0x130 */
u32 bistber1; /* 0x134 */
u32 bistber2; /* 0x138 */
u32 bistber3; /* 0x13c */
u32 bistwcsr; /* 0x140 */
u32 bistfwr0; /* 0x144 */
u32 bistfwr1; /* 0x148 */
u32 bistfwr2; /* 0x14c */
u8 res2[0x30]; /* 0x150 */
u32 zqcr0; /* 0x180 zq control register 0 */
u32 zqcr1; /* 0x184 zq control register 1 */
u32 zqsr0; /* 0x188 zq status register 0 */
u32 zqsr1; /* 0x18c zq status register 1 */
u32 zqcr2; /* 0x190 zq control register 2 */
u8 res3[0x2c]; /* 0x194 */
u32 dx0gcr; /* 0x1c0 */
u32 dx0gsr0; /* 0x1c4 */
u32 dx0gsr1; /* 0x1c8 */
u32 dx0bdlr0; /* 0x1cc */
u32 dx0bdlr1; /* 0x1d0 */
u32 dx0bdlr2; /* 0x1d4 */
u32 dx0bdlr3; /* 0x1d8 */
u32 dx0bdlr4; /* 0x1dc */
u32 dx0lcdlr0; /* 0x1e0 */
u32 dx0lcdlr1; /* 0x1e4 */
u32 dx0lcdlr2; /* 0x1e8 */
u32 dx0mdlr; /* 0x1ec */
u32 dx0gtr; /* 0x1f0 */
u32 dx0gsr2; /* 0x1f4 */
u8 res4[0x08]; /* 0x1f8 */
u32 dx1gcr; /* 0x200 */
u32 dx1gsr0; /* 0x204 */
u32 dx1gsr1; /* 0x208 */
u32 dx1bdlr0; /* 0x20c */
u32 dx1bdlr1; /* 0x210 */
u32 dx1bdlr2; /* 0x214 */
u32 dx1bdlr3; /* 0x218 */
u32 dx1bdlr4; /* 0x21c */
u32 dx1lcdlr0; /* 0x220 */
u32 dx1lcdlr1; /* 0x224 */
u32 dx1lcdlr2; /* 0x228 */
u32 dx1mdlr; /* 0x22c */
u32 dx1gtr; /* 0x230 */
u32 dx1gsr2; /* 0x234 */
+};
+/*
- DRAM common (sunxi_mctl_com_reg) register constants.
- */
+#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) ((x) << 8)
+#endif /* _SUNXI_DRAM_SUN8I_H */ diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 5bb2f58..5a88ba0 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -29,10 +29,11 @@ config MACH_SUN7I config MACH_SUN8I bool "sun8i (Allwinner A23)" select CPU_V7
select SUPPORT_SPL
endchoice
-if MACH_SUN6I +if MACH_SUN6I || MACH_SUN8I
config DRAM_CLK int "sun6i dram clock speed" diff --git a/configs/Ippo_q8h_v5_defconfig b/configs/Ippo_q8h_v5_defconfig index 50c2f93..37aa46d 100644 --- a/configs/Ippo_q8h_v5_defconfig +++ b/configs/Ippo_q8h_v5_defconfig @@ -1,8 +1,15 @@ +CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" -CONFIG_ARM=y -CONFIG_ARCH_SUNXI=y -CONFIG_MACH_SUN8I=y -CONFIG_TARGET_IPPO_Q8H_V5=y -CONFIG_DEFAULT_DEVICE_TREE="sun8i-a23-ippo-q8h-v5.dtb" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v5.dtb" CONFIG_VIDEO=n CONFIG_USB_KEYBOARD=n ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN8I=y ++S:CONFIG_DRAM_CLK=480 +# zq = 0xf777 ++S:CONFIG_DRAM_ZQ=63351 +# Wifi power ++S:CONFIG_AXP221_DLDO1_VOLT=3300 +# aldo1 is connected to VCC-IO, VCC-PD, VCC-USB and VCC-HP ++S:CONFIG_AXP221_ALDO1_VOLT=3000 diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 6f1fc48..792422d 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -12,6 +12,8 @@ /*
- A23 specific configuration
*/ +#define CONFIG_CLK_FULL_SPEED 1008000000
#define CONFIG_SYS_PROMPT "sun8i# "
/*
2.1.0
git am reports:
Applying patch #422080 using 'git am' Description: [U-Boot,13/14] sun8i: Add dram initialization support Applying: sun8i: Add dram initialization support /home/wens/sunxi/u-boot/.git/rebase-apply/patch:201: trailing whitespace. writel(((dram_para.tpr6 & 0x000007c0) << 10) | /home/wens/sunxi/u-boot/.git/rebase-apply/patch:455: space before tab in indent. u32 tpr6; warning: 2 lines add whitespace errors.
---
I forgot to send out this one. Anyway, I've tested the series on my A23 tablet and it's working fine. Something in linux-next broke my system though.
ChenYu

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
Based on the register / dram_para headers from the Allwinner u-boot / linux sources + the init sequences from boot0.
Signed-off-by: Hans de Goede hdegoede@redhat.com +/*
- Note this code uses a lot of magic hex values, that is because this code
- simply replays the init sequence as done by the Allwinner boot0 code, so
- we do not know what these values mean. There are no symbolic constants for
- these magic values, since we do not know how to name them and making up
- names for them is not useful.
On that basis I've only really given this a quick glance. I've no problem with it.
Couple of queries about the defconfig changes:
diff --git a/configs/Ippo_q8h_v5_defconfig b/configs/Ippo_q8h_v5_defconfig index 50c2f93..37aa46d 100644 --- a/configs/Ippo_q8h_v5_defconfig +++ b/configs/Ippo_q8h_v5_defconfig @@ -1,8 +1,15 @@ +CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" -CONFIG_ARM=y -CONFIG_ARCH_SUNXI=y -CONFIG_MACH_SUN8I=y -CONFIG_TARGET_IPPO_Q8H_V5=y
Not replaced with a S: variant? I know you want CONFIG_TARGET to go, but I don't think that was part of what you intended in this patch.
-CONFIG_DEFAULT_DEVICE_TREE="sun8i-a23-ippo-q8h-v5.dtb" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v5.dtb"
The switch from CONFIG_DEFAULT_DEVICE_TREE to CONFIG_FDTFILE conversion seems a little out of place too.
Ian.

Hi,
On 18-12-14 20:17, Ian Campbell wrote:
On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
Based on the register / dram_para headers from the Allwinner u-boot / linux sources + the init sequences from boot0.
Signed-off-by: Hans de Goede hdegoede@redhat.com +/*
- Note this code uses a lot of magic hex values, that is because this code
- simply replays the init sequence as done by the Allwinner boot0 code, so
- we do not know what these values mean. There are no symbolic constants for
- these magic values, since we do not know how to name them and making up
- names for them is not useful.
On that basis I've only really given this a quick glance. I've no problem with it.
Couple of queries about the defconfig changes:
diff --git a/configs/Ippo_q8h_v5_defconfig b/configs/Ippo_q8h_v5_defconfig index 50c2f93..37aa46d 100644 --- a/configs/Ippo_q8h_v5_defconfig +++ b/configs/Ippo_q8h_v5_defconfig @@ -1,8 +1,15 @@ +CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" -CONFIG_ARM=y -CONFIG_ARCH_SUNXI=y -CONFIG_MACH_SUN8I=y -CONFIG_TARGET_IPPO_Q8H_V5=y
Not replaced with a S: variant? I know you want CONFIG_TARGET to go, but I don't think that was part of what you intended in this patch.
Right, I'll add that back.
-CONFIG_DEFAULT_DEVICE_TREE="sun8i-a23-ippo-q8h-v5.dtb" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v5.dtb"
The switch from CONFIG_DEFAULT_DEVICE_TREE to CONFIG_FDTFILE conversion seems a little out of place too.
That was deliberate, the CONFIG_DEFAULT_DEVICE_TREE is the wrong CONFIG define to use to set the dtb for the kernel. I'll split the Ippo_q8h_v5_defconfig changes out into a separate patch and mention this in the commit message.
Regards,
Hans

On Tue, 16 Dec 2014 21:31:38 +0100 Hans de Goede hdegoede@redhat.com wrote:
Based on the register / dram_para headers from the Allwinner u-boot / linux sources + the init sequences from boot0.
Can the commit message have more detailed information about the precise location of the original Allwinner code, which was used as the reference?
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/board.c | 3 +- arch/arm/cpu/armv7/sunxi/dram_sun8i.c | 340 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 4 + arch/arm/include/asm/arch-sunxi/dram.h | 2 + arch/arm/include/asm/arch-sunxi/dram_sun8i.h | 266 ++++++++++++++++++++ board/sunxi/Kconfig | 3 +- configs/Ippo_q8h_v5_defconfig | 17 +- include/configs/sun8i.h | 2 + 9 files changed, 631 insertions(+), 7 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun8i.c create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun8i.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 3e8975a..1e89937 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_MACH_SUN4I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN5I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN6I) += dram_sun6i.o obj-$(CONFIG_MACH_SUN7I) += dram_sun4i.o +obj-$(CONFIG_MACH_SUN8I) += dram_sun8i.o ifdef CONFIG_SPL_FEL obj-y += start.o endif diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 9b3e80c..bc98c56 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -114,7 +114,8 @@ void reset_cpu(ulong addr) /* do some early init */ void s_init(void) { -#if defined CONFIG_SPL_BUILD && defined CONFIG_MACH_SUN6I +#if defined CONFIG_SPL_BUILD && \
/* Magic (undocmented) value taken from boot0, without this DRAM(defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I)
setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800);
- access gets messed up (seems cache related) */
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun8i.c b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c new file mode 100644 index 0000000..3736fd1 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c @@ -0,0 +1,340 @@ +/*
- Sun8i platform dram controller init.
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
Is Allwinner copyright really not necessary here?
- SPDX-License-Identifier: GPL-2.0+
- */
+/*
- Note this code uses a lot of magic hex values, that is because this code
- simply replays the init sequence as done by the Allwinner boot0 code, so
- we do not know what these values mean. There are no symbolic constants for
- these magic values, since we do not know how to name them and making up
- names for them is not useful.
- */
You are well aware of this documentation since a long time ago, right? http://www.ti.com/lit/ug/spruhn7a/spruhn7a.pdf
Please open it and find "Table4-2 DDR3 PHY Registers". Then have a look at the "sunxi_mctl_phy_reg" struct from your patch. Compare the register names and their offsets. Looks like we are reasonably lucky again.
Just as an example, I have commented about a few hardware registers at different locations in the code.
+#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h>
+static const struct dram_para dram_para = {
- .clock = CONFIG_DRAM_CLK,
- .type = 3,
- .zq = CONFIG_DRAM_ZQ,
- .odt_en = 1,
- .para1 = 0, /* not used (only used when tpr13 bit 31 is set */
- .para2 = 0, /* not used (only used when tpr13 bit 31 is set */
- .mr0 = 6736,
- .mr1 = 4,
- .mr2 = 16,
- .mr3 = 0,
- /* tpr0 - 10 contain timing constants or-ed together in u32 vals */
- .tpr0 = 0x2ab83def,
- .tpr1 = 0x18082356,
- .tpr2 = 0x00034156,
- .tpr3 = 0x448c5533,
- .tpr4 = 0x08010d00,
- .tpr5 = 0x0340b20f,
- .tpr6 = 0x20d118cc,
- .tpr7 = 0x14062485,
- .tpr8 = 0x220d1d52,
- .tpr9 = 0x1e078c22,
- .tpr10 = 0x3c,
- .tpr11 = 0, /* not used */
- .tpr12 = 0, /* not used */
- .tpr13 = 0x30000,
+};
+static void mctl_sys_init(void) +{
- struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- /* enable pll5, note the divide by 2 is deliberate! */
- clock_set_pll5(dram_para.clock * 1000000 / 2, 1, 2,
dram_para.tpr13 & 0x40000);
Is it really necessary to have the PLL5 setup so complicated and full of magic?
- /* deassert ahb mctl reset */
- setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
- /* enable ahb mctl clock */
- setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+}
+static void mctl_apply_odt_correction(u32 *reg, int correction) +{
- int val;
- val = (readl(reg) >> 8) & 0xff;
- val += correction;
- /* clamp */
- if (val < 0)
val = 0;
- else if (val > 255)
val = 255;
- clrsetbits_le32(reg, 0xff00, val << 8);
+}
+static void mctl_init(u32 *bus_width) +{
- struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
- struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
- struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
- int correction;
- if (dram_para.tpr13 & 0x20)
writel(0x40b, &mctl_phy->dcr);
The bits 2-0 are DDRMR (DDR Mode), and the value 3 means DDR3. And 0x40b is actually the default reset value of this register according to the TI Keystone2 documentation.
- else
writel(0x1000040b, &mctl_phy->dcr);
The bit 28 is marked as reserved and "Reads return zeros" in the TI Keystone2 documentation. It's a nice chance to test whether this write has any effect and whether it does modify the register.
- if (dram_para.clock >= 480)
writel(0x5c000, &mctl_phy->dllgcr);
0x5c000 = (1 << 18) | (0xE << 13)
- else
writel(0xdc000, &mctl_phy->dllgcr);
0xdc000 = (3 << 18) | (0xE << 13)
The 'dllgcr' has the same offset as "4.40 PLL Control Register (PLLCR)" in the TI Keystone2 documentation.
Bits 16-13 are CPPC (Charge Pump Proportional Current Control) with the default value 0xE, which matches what we see here.
Bits 19-18 are FRQSEL: 01 = PLL reference clock (ctl_clk/REF_CLK) ranges from 225MHz to 385MHz 11 = PLL reference clock (ctl_clk/REF_CLK) ranges from 166MHz to 275MHz
- writel(0x0a003e3f, &mctl_phy->pgcr0);
- writel(0x03008421, &mctl_phy->pgcr1);
- writel(dram_para.mr0, &mctl_phy->mr0);
- writel(dram_para.mr1, &mctl_phy->mr1);
- writel(dram_para.mr2, &mctl_phy->mr2);
- writel(dram_para.mr3, &mctl_phy->mr3);
- if (!(dram_para.tpr13 & 0x10000)) {
clrsetbits_le32(&mctl_phy->dx0gcr, 0x3800, 0x2000);
clrsetbits_le32(&mctl_phy->dx1gcr, 0x3800, 0x2000);
- }
- /*
* All the masking and shifting below converts what I assume are DDR
* timing constants from Allwinner dram_para tpr format to the actual
* timing registers format.
*/
- writel((dram_para.tpr0 & 0x000fffff), &mctl_phy->ptr2);
- writel((dram_para.tpr1 & 0x1fffffff), &mctl_phy->ptr3);
- writel((dram_para.tpr0 & 0x3ff00000) >> 2 |
(dram_para.tpr2 & 0x0003ffff), &mctl_phy->ptr4);
- writel(dram_para.tpr3, &mctl_phy->dtpr0);
- writel(dram_para.tpr4, &mctl_phy->dtpr2);
- writel(0x01000081, &mctl_phy->dtcr);
- if (dram_para.clock <= 240 || !(dram_para.odt_en & 0x01)) {
clrbits_le32(&mctl_phy->dx0gcr, 0x600);
clrbits_le32(&mctl_phy->dx1gcr, 0x600);
- }
- if (dram_para.clock <= 240) {
writel(0, &mctl_phy->odtcr);
writel(0, &mctl_ctl->odtmap);
- }
- writel(((dram_para.tpr5 & 0x0f00) << 12) |
((dram_para.tpr5 & 0x00f8) << 9) |
((dram_para.tpr5 & 0x0007) << 8),
&mctl_ctl->rfshctl0);
- writel(((dram_para.tpr5 & 0x0003f000) << 12) |
((dram_para.tpr5 & 0x00fc0000) >> 2) |
((dram_para.tpr5 & 0x3f000000) >> 16) |
((dram_para.tpr6 & 0x0000003f) >> 0),
&mctl_ctl->dramtmg0);
- writel(((dram_para.tpr6 & 0x000007c0) << 10) |
((dram_para.tpr6 & 0x0000f800) >> 3) |
((dram_para.tpr6 & 0x003f0000) >> 16),
&mctl_ctl->dramtmg1);
- writel(((dram_para.tpr6 & 0x0fc00000) << 2) |
((dram_para.tpr7 & 0x0000001f) << 16) |
((dram_para.tpr7 & 0x000003e0) << 3) |
((dram_para.tpr7 & 0x0000fc00) >> 10),
&mctl_ctl->dramtmg2);
- writel(((dram_para.tpr7 & 0x03ff0000) >> 16) |
((dram_para.tpr6 & 0xf0000000) >> 16),
&mctl_ctl->dramtmg3);
- writel(((dram_para.tpr7 & 0x3c000000) >> 2 ) |
((dram_para.tpr8 & 0x00000007) << 16) |
((dram_para.tpr8 & 0x00000038) << 5) |
((dram_para.tpr8 & 0x000003c0) >> 6),
&mctl_ctl->dramtmg4);
- writel(((dram_para.tpr8 & 0x00003c00) << 14) |
((dram_para.tpr8 & 0x0003c000) << 2) |
((dram_para.tpr8 & 0x00fc0000) >> 10) |
((dram_para.tpr8 & 0x0f000000) >> 24),
&mctl_ctl->dramtmg5);
- writel(0x00000008, &mctl_ctl->dramtmg8);
- writel(((dram_para.tpr8 & 0xf0000000) >> 4) |
((dram_para.tpr9 & 0x00007c00) << 6) |
((dram_para.tpr9 & 0x000003e0) << 3) |
((dram_para.tpr9 & 0x0000001f) >> 0),
&mctl_ctl->pitmg0);
Could there be any purpose for this other than obfuscation? Looks really strange.
- setbits_le32(&mctl_ctl->pitmg1, 0x80000);
- writel(((dram_para.tpr9 & 0x003f8000) << 9) | 0x2001,
&mctl_ctl->sched);
- writel((dram_para.mr0 << 16) | dram_para.mr1, &mctl_ctl->init3);
- writel((dram_para.mr2 << 16) | dram_para.mr3, &mctl_ctl->init4);
- writel(0x00000000, &mctl_ctl->pimisc);
- writel(0x80000000, &mctl_ctl->upd0);
- writel(((dram_para.tpr9 & 0xffc00000) >> 22) |
((dram_para.tpr10 & 0x00000fff) << 16),
&mctl_ctl->rfshtmg);
- if (dram_para.tpr13 & 0x20)
writel(0x01040001, &mctl_ctl->mstr);
- else
writel(0x01040401, &mctl_ctl->mstr);
- if (!(dram_para.tpr13 & 0x20000)) {
writel(0x00000002, &mctl_ctl->pwrctl);
writel(0x00008001, &mctl_ctl->pwrtmg);
- }
- writel(0x00000001, &mctl_ctl->rfshctl3);
- writel(0x00000001, &mctl_ctl->pimisc);
- /* deassert dram_clk_cfg reset */
- setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
- setbits_le32(&mctl_com->ccr, 0x80000);
- /* zq stuff */
- writel((dram_para.zq >> 8) & 0xff, &mctl_phy->zqcr1);
4.59 Impedance Control Register 1 (ZQnCR1)
Bits 7-0 - ZPROG (Impedance Divide Ratio), reset value 0x7B
- writel(0x00000003, &mctl_phy->pir);
4.34 PHY Initialization Register (PIR)
Bit 0 - INIT Bit 1 - ZCAL
- udelay(10);
- mctl_await_completion(&mctl_phy->pgsr0, 0x09, 0x09);
4.38 PHY General Status Register 0 (PGSR0)
Bit 0 - IDONE (Initialization Done) Bit 3 - ZCDONE (Impedance Calibration Done)
- writel(readl(&mctl_phy->zqsr0) | 0x10000000, &mctl_phy->zqcr2);
- writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
Overwriting ZPROG again? Is it doing something reasonable? Or might it be an attempt to hide data from the tools like a10-meminfo (which can dump the DRAM setting via /dev/mem)?
- /* A23-v1.0 SDK uses 0xfdf3, A23-v2.0 SDK uses 0x5f3 */
- writel(0x000005f3, &mctl_phy->pir);
- udelay(10);
- mctl_await_completion(&mctl_phy->pgsr0, 0x03, 0x03);
- if (readl(&mctl_phy->dx1gsr0) & 0x1000000) {
*bus_width = 8;
writel(0, &mctl_phy->dx1gcr);
writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
writel(0x5f3, &mctl_phy->pir);
udelay(10000);
setbits_le32(&mctl_ctl->mstr, 0x1000);
- } else
*bus_width = 16;
- correction = (dram_para.odt_en >> 8) & 0xff;
- if (correction) {
if (dram_para.odt_en & 0x80000000)
correction = -correction;
mctl_apply_odt_correction(&mctl_phy->dx0lcdlr1, correction);
mctl_apply_odt_correction(&mctl_phy->dx1lcdlr1, correction);
- }
- mctl_await_completion(&mctl_ctl->statr, 0x01, 0x01);
- writel(0x08003e3f, &mctl_phy->pgcr0);
- writel(0x00000000, &mctl_ctl->rfshctl3);
+}
+unsigned long sunxi_dram_init(void) +{
- struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
- const u32 columns = 13;
- u32 bus, bus_width, offset, page_size, rows;
- mctl_sys_init();
- mctl_init(&bus_width);
- if (bus_width == 16) {
page_size = 8;
bus = 1;
- } else {
page_size = 7;
bus = 0;
- }
- if (!(dram_para.tpr13 & 0x80000000)) {
/* Detect and set rows */
writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
mctl_mem_fill();
for (rows = 11; rows < 16; rows++) {
offset = 1 << (rows + columns + bus);
if (mctl_mem_matches(offset))
break;
}
clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK,
MCTL_CR_ROW(rows));
- } else {
rows = (dram_para.para1 >> 16) & 0xff;
writel(((dram_para.para2 & 0x000000f0) << 11) |
((rows - 1) << 4) |
((dram_para.para1 & 0x0f000000) >> 22) |
0x31000 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
- }
- /* Setup DRAM master priority? If this is left out things still work */
- writel(0x00000008, &mctl_com->mcr0_0);
- writel(0x0001000d, &mctl_com->mcr1_0);
- writel(0x00000004, &mctl_com->mcr0_1);
- writel(0x00000080, &mctl_com->mcr1_1);
- writel(0x00000004, &mctl_com->mcr0_2);
- writel(0x00000019, &mctl_com->mcr1_2);
- writel(0x00000004, &mctl_com->mcr0_3);
- writel(0x00000080, &mctl_com->mcr1_3);
- writel(0x00000004, &mctl_com->mcr0_4);
- writel(0x01010040, &mctl_com->mcr1_4);
- writel(0x00000004, &mctl_com->mcr0_5);
- writel(0x0001002f, &mctl_com->mcr1_5);
- writel(0x00000004, &mctl_com->mcr0_6);
- writel(0x00010020, &mctl_com->mcr1_6);
- writel(0x00000004, &mctl_com->mcr0_7);
- writel(0x00010020, &mctl_com->mcr1_7);
- writel(0x00000008, &mctl_com->mcr0_8);
- writel(0x00000001, &mctl_com->mcr1_8);
- writel(0x00000008, &mctl_com->mcr0_9);
- writel(0x00000005, &mctl_com->mcr1_9);
- writel(0x00000008, &mctl_com->mcr0_10);
- writel(0x00000003, &mctl_com->mcr1_10);
- writel(0x00000008, &mctl_com->mcr0_11);
- writel(0x00000005, &mctl_com->mcr1_11);
- writel(0x00000008, &mctl_com->mcr0_12);
- writel(0x00000003, &mctl_com->mcr1_12);
- writel(0x00000008, &mctl_com->mcr0_13);
- writel(0x00000004, &mctl_com->mcr1_13);
- writel(0x00000008, &mctl_com->mcr0_14);
- writel(0x00000002, &mctl_com->mcr1_14);
- writel(0x00000008, &mctl_com->mcr0_15);
- writel(0x00000003, &mctl_com->mcr1_15);
- writel(0x00010138, &mctl_com->bwcr);
- return 1 << (rows + columns + bus);
+} diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 7d61216..45a199c 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -273,7 +273,11 @@ struct sunxi_ccm_reg { #define CCM_HDMI_CTRL_DDC_GATE (0x1 << 30) #define CCM_HDMI_CTRL_GATE (0x1 << 31)
+#ifndef CONFIG_MACH_SUN8I #define MBUS_CLK_DEFAULT 0x81000001 /* PLL6 / 2 */ +#else +#define MBUS_CLK_DEFAULT 0x81000003 /* PLL6 / 4 */ +#endif
#define CCM_PLL5_PATTERN 0xd1303333
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index a8a37d5..8d78029 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -18,6 +18,8 @@ /* dram regs definition */ #if defined(CONFIG_MACH_SUN6I) #include <asm/arch/dram_sun6i.h> +#elif defined(CONFIG_MACH_SUN8I) +#include <asm/arch/dram_sun8i.h> #else #include <asm/arch/dram_sun4i.h> #endif diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun8i.h b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h new file mode 100644 index 0000000..425cf37 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h @@ -0,0 +1,266 @@ +/*
- Sun8i platform dram controller register and constant defines
- (C) Copyright 2007-2013
- Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- Jerry Wang wangflord@allwinnertech.com
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef _SUNXI_DRAM_SUN8I_H +#define _SUNXI_DRAM_SUN8I_H
+struct dram_para {
- u32 clock;
- u32 type;
- u32 zq;
- u32 odt_en;
- u32 para1;
- u32 para2;
- u32 mr0;
- u32 mr1;
- u32 mr2;
- u32 mr3;
- u32 tpr0;
- u32 tpr1;
- u32 tpr2;
- u32 tpr3;
- u32 tpr4;
- u32 tpr5;
- u32 tpr6;
- u32 tpr7;
- u32 tpr8;
- u32 tpr9;
- u32 tpr10;
- u32 tpr11;
- u32 tpr12;
- u32 tpr13;
+};
+struct sunxi_mctl_com_reg {
- u32 cr; /* 0x00 */
- u32 ccr; /* 0x04 controller configuration register */
- u32 dbgcr; /* 0x08 */
- u8 res0[0x4]; /* 0x0c */
- u32 mcr0_0; /* 0x10 */
- u32 mcr1_0; /* 0x14 */
- u32 mcr0_1; /* 0x18 */
- u32 mcr1_1; /* 0x1c */
- u32 mcr0_2; /* 0x20 */
- u32 mcr1_2; /* 0x24 */
- u32 mcr0_3; /* 0x28 */
- u32 mcr1_3; /* 0x2c */
- u32 mcr0_4; /* 0x30 */
- u32 mcr1_4; /* 0x34 */
- u32 mcr0_5; /* 0x38 */
- u32 mcr1_5; /* 0x3c */
- u32 mcr0_6; /* 0x40 */
- u32 mcr1_6; /* 0x44 */
- u32 mcr0_7; /* 0x48 */
- u32 mcr1_7; /* 0x4c */
- u32 mcr0_8; /* 0x50 */
- u32 mcr1_8; /* 0x54 */
- u32 mcr0_9; /* 0x58 */
- u32 mcr1_9; /* 0x5c */
- u32 mcr0_10; /* 0x60 */
- u32 mcr1_10; /* 0x64 */
- u32 mcr0_11; /* 0x68 */
- u32 mcr1_11; /* 0x6c */
- u32 mcr0_12; /* 0x70 */
- u32 mcr1_12; /* 0x74 */
- u32 mcr0_13; /* 0x78 */
- u32 mcr1_13; /* 0x7c */
- u32 mcr0_14; /* 0x80 */
- u32 mcr1_14; /* 0x84 */
- u32 mcr0_15; /* 0x88 */
- u32 mcr1_15; /* 0x8c */
- u32 bwcr; /* 0x90 */
- u32 maer; /* 0x94 */
- u8 res1[0x4]; /* 0x98 */
- u32 mcgcr; /* 0x9c */
- u32 bwctr; /* 0xa0 */
- u8 res2[0x4]; /* 0xa4 */
- u32 swonr; /* 0xa8 */
- u32 swoffr; /* 0xac */
+};
+struct sunxi_mctl_ctl_reg {
- u32 mstr; /* 0x00 */
- u32 statr; /* 0x04 */
- u8 res0[0x08]; /* 0x08 */
- u32 mrctrl0; /* 0x10 */
- u32 mrctrl1; /* 0x14 */
- u32 mrstatr; /* 0x18 */
- u8 res1[0x04]; /* 0x1c */
- u32 derateen; /* 0x20 */
- u32 deratenint; /* 0x24 */
- u8 res2[0x08]; /* 0x28 */
- u32 pwrctl; /* 0x30 */
- u32 pwrtmg; /* 0x34 */
- u8 res3[0x18]; /* 0x38 */
- u32 rfshctl0; /* 0x50 */
- u32 rfshctl1; /* 0x54 */
- u8 res4[0x8]; /* 0x58 */
- u32 rfshctl3; /* 0x60 */
- u32 rfshtmg; /* 0x64 */
- u8 res6[0x68]; /* 0x68 */
- u32 init0; /* 0xd0 */
- u32 init1; /* 0xd4 */
- u32 init2; /* 0xd8 */
- u32 init3; /* 0xdc */
- u32 init4; /* 0xe0 */
- u32 init5; /* 0xe4 */
- u8 res7[0x0c]; /* 0xe8 */
- u32 rankctl; /* 0xf4 */
- u8 res8[0x08]; /* 0xf8 */
- u32 dramtmg0; /* 0x100 */
- u32 dramtmg1; /* 0x104 */
- u32 dramtmg2; /* 0x108 */
- u32 dramtmg3; /* 0x10c */
- u32 dramtmg4; /* 0x110 */
- u32 dramtmg5; /* 0x114 */
- u32 dramtmg6; /* 0x118 */
- u32 dramtmg7; /* 0x11c */
- u32 dramtmg8; /* 0x120 */
- u8 res9[0x5c]; /* 0x124 */
- u32 zqctl0; /* 0x180 */
- u32 zqctl1; /* 0x184 */
- u32 zqctl2; /* 0x188 */
- u32 zqstat; /* 0x18c */
- u32 pitmg0; /* 0x190 */
- u32 pitmg1; /* 0x194 */
- u32 plpcfg0; /* 0x198 */
- u8 res10[0x04]; /* 0x19c */
- u32 upd0; /* 0x1a0 */
- u32 upd1; /* 0x1a4 */
- u32 upd2; /* 0x1a8 */
- u32 upd3; /* 0x1ac */
- u32 pimisc; /* 0x1b0 */
- u8 res11[0x1c]; /* 0x1b4 */
- u32 trainctl0; /* 0x1d0 */
- u32 trainctl1; /* 0x1d4 */
- u32 trainctl2; /* 0x1d8 */
- u32 trainstat; /* 0x1dc */
- u8 res12[0x60]; /* 0x1e0 */
- u32 odtcfg; /* 0x240 */
- u32 odtmap; /* 0x244 */
- u8 res13[0x08]; /* 0x248 */
- u32 sched; /* 0x250 */
- u8 res14[0x04]; /* 0x254 */
- u32 perfshpr0; /* 0x258 */
- u32 perfshpr1; /* 0x25c */
- u32 perflpr0; /* 0x260 */
- u32 perflpr1; /* 0x264 */
- u32 perfwr0; /* 0x268 */
- u32 perfwr1; /* 0x26c */
+};
+struct sunxi_mctl_phy_reg {
- u8 res0[0x04]; /* 0x00 */
- u32 pir; /* 0x04 */
- u32 pgcr0; /* 0x08 phy general configuration register */
- u32 pgcr1; /* 0x0c phy general configuration register */
- u32 pgsr0; /* 0x10 */
- u32 pgsr1; /* 0x14 */
- u32 dllgcr; /* 0x18 */
- u32 ptr0; /* 0x1c */
- u32 ptr1; /* 0x20 */
- u32 ptr2; /* 0x24 */
- u32 ptr3; /* 0x28 */
- u32 ptr4; /* 0x2c */
- u32 acmdlr; /* 0x30 */
- u32 acbdlr; /* 0x34 */
- u32 aciocr; /* 0x38 */
- u32 dxccr; /* 0x3c DATX8 common configuration register */
- u32 dsgcr; /* 0x40 dram system general config register */
- u32 dcr; /* 0x44 */
- u32 dtpr0; /* 0x48 dram timing parameters register 0 */
- u32 dtpr1; /* 0x4c dram timing parameters register 1 */
- u32 dtpr2; /* 0x50 dram timing parameters register 2 */
- u32 mr0; /* 0x54 mode register 0 */
- u32 mr1; /* 0x58 mode register 1 */
- u32 mr2; /* 0x5c mode register 2 */
- u32 mr3; /* 0x60 mode register 3 */
- u32 odtcr; /* 0x64 */
- u32 dtcr; /* 0x68 */
- u32 dtar0; /* 0x6c data training address register 0 */
- u32 dtar1; /* 0x70 data training address register 1 */
- u32 dtar2; /* 0x74 data training address register 2 */
- u32 dtar3; /* 0x78 data training address register 3 */
- u32 dtdr0; /* 0x7c */
- u32 dtdr1; /* 0x80 */
- u32 dtedr0; /* 0x84 */
- u32 dtedr1; /* 0x88 */
- u32 pgcr2; /* 0x8c */
- u8 res1[0x70]; /* 0x90 */
- u32 bistrr; /* 0x100 */
- u32 bistwcr; /* 0x104 */
- u32 bistmskr0; /* 0x108 */
- u32 bistmskr1; /* 0x10c */
- u32 bistmskr2; /* 0x110 */
- u32 bistlsr; /* 0x114 */
- u32 bistar0; /* 0x118 */
- u32 bistar1; /* 0x11c */
- u32 bistar2; /* 0x120 */
- u32 bistupdr; /* 0x124 */
- u32 bistgsr; /* 0x128 */
- u32 bistwer; /* 0x12c */
- u32 bistber0; /* 0x130 */
- u32 bistber1; /* 0x134 */
- u32 bistber2; /* 0x138 */
- u32 bistber3; /* 0x13c */
- u32 bistwcsr; /* 0x140 */
- u32 bistfwr0; /* 0x144 */
- u32 bistfwr1; /* 0x148 */
- u32 bistfwr2; /* 0x14c */
- u8 res2[0x30]; /* 0x150 */
- u32 zqcr0; /* 0x180 zq control register 0 */
- u32 zqcr1; /* 0x184 zq control register 1 */
- u32 zqsr0; /* 0x188 zq status register 0 */
- u32 zqsr1; /* 0x18c zq status register 1 */
- u32 zqcr2; /* 0x190 zq control register 2 */
- u8 res3[0x2c]; /* 0x194 */
- u32 dx0gcr; /* 0x1c0 */
- u32 dx0gsr0; /* 0x1c4 */
- u32 dx0gsr1; /* 0x1c8 */
- u32 dx0bdlr0; /* 0x1cc */
- u32 dx0bdlr1; /* 0x1d0 */
- u32 dx0bdlr2; /* 0x1d4 */
- u32 dx0bdlr3; /* 0x1d8 */
- u32 dx0bdlr4; /* 0x1dc */
- u32 dx0lcdlr0; /* 0x1e0 */
- u32 dx0lcdlr1; /* 0x1e4 */
- u32 dx0lcdlr2; /* 0x1e8 */
- u32 dx0mdlr; /* 0x1ec */
- u32 dx0gtr; /* 0x1f0 */
- u32 dx0gsr2; /* 0x1f4 */
- u8 res4[0x08]; /* 0x1f8 */
- u32 dx1gcr; /* 0x200 */
- u32 dx1gsr0; /* 0x204 */
- u32 dx1gsr1; /* 0x208 */
- u32 dx1bdlr0; /* 0x20c */
- u32 dx1bdlr1; /* 0x210 */
- u32 dx1bdlr2; /* 0x214 */
- u32 dx1bdlr3; /* 0x218 */
- u32 dx1bdlr4; /* 0x21c */
- u32 dx1lcdlr0; /* 0x220 */
- u32 dx1lcdlr1; /* 0x224 */
- u32 dx1lcdlr2; /* 0x228 */
- u32 dx1mdlr; /* 0x22c */
- u32 dx1gtr; /* 0x230 */
- u32 dx1gsr2; /* 0x234 */
+};
+/*
- DRAM common (sunxi_mctl_com_reg) register constants.
- */
+#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) ((x) << 8)
+#endif /* _SUNXI_DRAM_SUN8I_H */ diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 5bb2f58..5a88ba0 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -29,10 +29,11 @@ config MACH_SUN7I config MACH_SUN8I bool "sun8i (Allwinner A23)" select CPU_V7
- select SUPPORT_SPL
endchoice
-if MACH_SUN6I +if MACH_SUN6I || MACH_SUN8I
config DRAM_CLK int "sun6i dram clock speed" diff --git a/configs/Ippo_q8h_v5_defconfig b/configs/Ippo_q8h_v5_defconfig index 50c2f93..37aa46d 100644 --- a/configs/Ippo_q8h_v5_defconfig +++ b/configs/Ippo_q8h_v5_defconfig @@ -1,8 +1,15 @@ +CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" -CONFIG_ARM=y -CONFIG_ARCH_SUNXI=y -CONFIG_MACH_SUN8I=y -CONFIG_TARGET_IPPO_Q8H_V5=y -CONFIG_DEFAULT_DEVICE_TREE="sun8i-a23-ippo-q8h-v5.dtb" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v5.dtb" CONFIG_VIDEO=n CONFIG_USB_KEYBOARD=n ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN8I=y ++S:CONFIG_DRAM_CLK=480 +# zq = 0xf777 ++S:CONFIG_DRAM_ZQ=63351 +# Wifi power ++S:CONFIG_AXP221_DLDO1_VOLT=3300 +# aldo1 is connected to VCC-IO, VCC-PD, VCC-USB and VCC-HP ++S:CONFIG_AXP221_ALDO1_VOLT=3000 diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 6f1fc48..792422d 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -12,6 +12,8 @@ /*
- A23 specific configuration
*/ +#define CONFIG_CLK_FULL_SPEED 1008000000
#define CONFIG_SYS_PROMPT "sun8i# "
/*

Hi,
On 19-12-14 11:20, Siarhei Siamashka wrote:
On Tue, 16 Dec 2014 21:31:38 +0100 Hans de Goede hdegoede@redhat.com wrote:
Based on the register / dram_para headers from the Allwinner u-boot / linux sources + the init sequences from boot0.
Can the commit message have more detailed information about the precise location of the original Allwinner code, which was used as the reference?
No, it cannot because no code was referenced, I traced the boot0 binary to figure out what was needed to get dram going on the A23.
Signed-off-by: Hans de Goede hdegoede@redhat.com
arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/board.c | 3 +- arch/arm/cpu/armv7/sunxi/dram_sun8i.c | 340 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 4 + arch/arm/include/asm/arch-sunxi/dram.h | 2 + arch/arm/include/asm/arch-sunxi/dram_sun8i.h | 266 ++++++++++++++++++++ board/sunxi/Kconfig | 3 +- configs/Ippo_q8h_v5_defconfig | 17 +- include/configs/sun8i.h | 2 + 9 files changed, 631 insertions(+), 7 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun8i.c create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun8i.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 3e8975a..1e89937 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_MACH_SUN4I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN5I) += dram_sun4i.o obj-$(CONFIG_MACH_SUN6I) += dram_sun6i.o obj-$(CONFIG_MACH_SUN7I) += dram_sun4i.o +obj-$(CONFIG_MACH_SUN8I) += dram_sun8i.o ifdef CONFIG_SPL_FEL obj-y += start.o endif diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 9b3e80c..bc98c56 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -114,7 +114,8 @@ void reset_cpu(ulong addr) /* do some early init */ void s_init(void) { -#if defined CONFIG_SPL_BUILD && defined CONFIG_MACH_SUN6I +#if defined CONFIG_SPL_BUILD && \
/* Magic (undocmented) value taken from boot0, without this DRAM(defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I)
setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800);
- access gets messed up (seems cache related) */
diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun8i.c b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c new file mode 100644 index 0000000..3736fd1 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c @@ -0,0 +1,340 @@ +/*
- Sun8i platform dram controller init.
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
Is Allwinner copyright really not necessary here?
- SPDX-License-Identifier: GPL-2.0+
- */
+/*
- Note this code uses a lot of magic hex values, that is because this code
- simply replays the init sequence as done by the Allwinner boot0 code, so
- we do not know what these values mean. There are no symbolic constants for
- these magic values, since we do not know how to name them and making up
- names for them is not useful.
- */
You are well aware of this documentation since a long time ago, right? http://www.ti.com/lit/ug/spruhn7a/spruhn7a.pdf
I'm aware you've used various sources to figure out more about the A10 dram controller, since this DRAM controller seems significantly different I assumed your sources would not apply, it is good to hear that they do.
I'll add a comment to the next version of this patch referencing that document.
Please open it and find "Table4-2 DDR3 PHY Registers". Then have a look at the "sunxi_mctl_phy_reg" struct from your patch. Compare the register names and their offsets. Looks like we are reasonably lucky again.
Just as an example, I have commented about a few hardware registers at different locations in the code.
+#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h>
+static const struct dram_para dram_para = {
- .clock = CONFIG_DRAM_CLK,
- .type = 3,
- .zq = CONFIG_DRAM_ZQ,
- .odt_en = 1,
- .para1 = 0, /* not used (only used when tpr13 bit 31 is set */
- .para2 = 0, /* not used (only used when tpr13 bit 31 is set */
- .mr0 = 6736,
- .mr1 = 4,
- .mr2 = 16,
- .mr3 = 0,
- /* tpr0 - 10 contain timing constants or-ed together in u32 vals */
- .tpr0 = 0x2ab83def,
- .tpr1 = 0x18082356,
- .tpr2 = 0x00034156,
- .tpr3 = 0x448c5533,
- .tpr4 = 0x08010d00,
- .tpr5 = 0x0340b20f,
- .tpr6 = 0x20d118cc,
- .tpr7 = 0x14062485,
- .tpr8 = 0x220d1d52,
- .tpr9 = 0x1e078c22,
- .tpr10 = 0x3c,
- .tpr11 = 0, /* not used */
- .tpr12 = 0, /* not used */
- .tpr13 = 0x30000,
+};
+static void mctl_sys_init(void) +{
- struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- /* enable pll5, note the divide by 2 is deliberate! */
- clock_set_pll5(dram_para.clock * 1000000 / 2, 1, 2,
dram_para.tpr13 & 0x40000);
Is it really necessary to have the PLL5 setup so complicated and full of magic?
- /* deassert ahb mctl reset */
- setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
- /* enable ahb mctl clock */
- setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+}
+static void mctl_apply_odt_correction(u32 *reg, int correction) +{
- int val;
- val = (readl(reg) >> 8) & 0xff;
- val += correction;
- /* clamp */
- if (val < 0)
val = 0;
- else if (val > 255)
val = 255;
- clrsetbits_le32(reg, 0xff00, val << 8);
+}
+static void mctl_init(u32 *bus_width) +{
- struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
- struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
- struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
- int correction;
- if (dram_para.tpr13 & 0x20)
writel(0x40b, &mctl_phy->dcr);
The bits 2-0 are DDRMR (DDR Mode), and the value 3 means DDR3. And 0x40b is actually the default reset value of this register according to the TI Keystone2 documentation.
- else
writel(0x1000040b, &mctl_phy->dcr);
The bit 28 is marked as reserved and "Reads return zeros" in the TI Keystone2 documentation. It's a nice chance to test whether this write has any effect and whether it does modify the register.
It modifies the register, so that means that at least that bit does not match the TI Keystone2 documentation.
- if (dram_para.clock >= 480)
writel(0x5c000, &mctl_phy->dllgcr);
0x5c000 = (1 << 18) | (0xE << 13)
- else
writel(0xdc000, &mctl_phy->dllgcr);
0xdc000 = (3 << 18) | (0xE << 13)
The 'dllgcr' has the same offset as "4.40 PLL Control Register (PLLCR)" in the TI Keystone2 documentation.
Bits 16-13 are CPPC (Charge Pump Proportional Current Control) with the default value 0xE, which matches what we see here.
Bits 19-18 are FRQSEL: 01 = PLL reference clock (ctl_clk/REF_CLK) ranges from 225MHz to 385MHz 11 = PLL reference clock (ctl_clk/REF_CLK) ranges from 166MHz to 275MHz
That seems to match nicely, as said pll5 is set to dram_clk / 2. which puts the cut over point at 240 MHz
- writel(0x0a003e3f, &mctl_phy->pgcr0);
- writel(0x03008421, &mctl_phy->pgcr1);
- writel(dram_para.mr0, &mctl_phy->mr0);
- writel(dram_para.mr1, &mctl_phy->mr1);
- writel(dram_para.mr2, &mctl_phy->mr2);
- writel(dram_para.mr3, &mctl_phy->mr3);
- if (!(dram_para.tpr13 & 0x10000)) {
clrsetbits_le32(&mctl_phy->dx0gcr, 0x3800, 0x2000);
clrsetbits_le32(&mctl_phy->dx1gcr, 0x3800, 0x2000);
- }
- /*
* All the masking and shifting below converts what I assume are DDR
* timing constants from Allwinner dram_para tpr format to the actual
* timing registers format.
*/
- writel((dram_para.tpr0 & 0x000fffff), &mctl_phy->ptr2);
- writel((dram_para.tpr1 & 0x1fffffff), &mctl_phy->ptr3);
- writel((dram_para.tpr0 & 0x3ff00000) >> 2 |
(dram_para.tpr2 & 0x0003ffff), &mctl_phy->ptr4);
- writel(dram_para.tpr3, &mctl_phy->dtpr0);
- writel(dram_para.tpr4, &mctl_phy->dtpr2);
- writel(0x01000081, &mctl_phy->dtcr);
- if (dram_para.clock <= 240 || !(dram_para.odt_en & 0x01)) {
clrbits_le32(&mctl_phy->dx0gcr, 0x600);
clrbits_le32(&mctl_phy->dx1gcr, 0x600);
- }
- if (dram_para.clock <= 240) {
writel(0, &mctl_phy->odtcr);
writel(0, &mctl_ctl->odtmap);
- }
- writel(((dram_para.tpr5 & 0x0f00) << 12) |
((dram_para.tpr5 & 0x00f8) << 9) |
((dram_para.tpr5 & 0x0007) << 8),
&mctl_ctl->rfshctl0);
- writel(((dram_para.tpr5 & 0x0003f000) << 12) |
((dram_para.tpr5 & 0x00fc0000) >> 2) |
((dram_para.tpr5 & 0x3f000000) >> 16) |
((dram_para.tpr6 & 0x0000003f) >> 0),
&mctl_ctl->dramtmg0);
- writel(((dram_para.tpr6 & 0x000007c0) << 10) |
((dram_para.tpr6 & 0x0000f800) >> 3) |
((dram_para.tpr6 & 0x003f0000) >> 16),
&mctl_ctl->dramtmg1);
- writel(((dram_para.tpr6 & 0x0fc00000) << 2) |
((dram_para.tpr7 & 0x0000001f) << 16) |
((dram_para.tpr7 & 0x000003e0) << 3) |
((dram_para.tpr7 & 0x0000fc00) >> 10),
&mctl_ctl->dramtmg2);
- writel(((dram_para.tpr7 & 0x03ff0000) >> 16) |
((dram_para.tpr6 & 0xf0000000) >> 16),
&mctl_ctl->dramtmg3);
- writel(((dram_para.tpr7 & 0x3c000000) >> 2 ) |
((dram_para.tpr8 & 0x00000007) << 16) |
((dram_para.tpr8 & 0x00000038) << 5) |
((dram_para.tpr8 & 0x000003c0) >> 6),
&mctl_ctl->dramtmg4);
- writel(((dram_para.tpr8 & 0x00003c00) << 14) |
((dram_para.tpr8 & 0x0003c000) << 2) |
((dram_para.tpr8 & 0x00fc0000) >> 10) |
((dram_para.tpr8 & 0x0f000000) >> 24),
&mctl_ctl->dramtmg5);
- writel(0x00000008, &mctl_ctl->dramtmg8);
- writel(((dram_para.tpr8 & 0xf0000000) >> 4) |
((dram_para.tpr9 & 0x00007c00) << 6) |
((dram_para.tpr9 & 0x000003e0) << 3) |
((dram_para.tpr9 & 0x0000001f) >> 0),
&mctl_ctl->pitmg0);
Could there be any purpose for this other than obfuscation? Looks really strange.
I think the purpose is to keep the dram_para.tpr? values the same as with sun6i, so that they can be copy pasted between designs ...
- setbits_le32(&mctl_ctl->pitmg1, 0x80000);
- writel(((dram_para.tpr9 & 0x003f8000) << 9) | 0x2001,
&mctl_ctl->sched);
- writel((dram_para.mr0 << 16) | dram_para.mr1, &mctl_ctl->init3);
- writel((dram_para.mr2 << 16) | dram_para.mr3, &mctl_ctl->init4);
- writel(0x00000000, &mctl_ctl->pimisc);
- writel(0x80000000, &mctl_ctl->upd0);
- writel(((dram_para.tpr9 & 0xffc00000) >> 22) |
((dram_para.tpr10 & 0x00000fff) << 16),
&mctl_ctl->rfshtmg);
- if (dram_para.tpr13 & 0x20)
writel(0x01040001, &mctl_ctl->mstr);
- else
writel(0x01040401, &mctl_ctl->mstr);
- if (!(dram_para.tpr13 & 0x20000)) {
writel(0x00000002, &mctl_ctl->pwrctl);
writel(0x00008001, &mctl_ctl->pwrtmg);
- }
- writel(0x00000001, &mctl_ctl->rfshctl3);
- writel(0x00000001, &mctl_ctl->pimisc);
- /* deassert dram_clk_cfg reset */
- setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
- setbits_le32(&mctl_com->ccr, 0x80000);
- /* zq stuff */
- writel((dram_para.zq >> 8) & 0xff, &mctl_phy->zqcr1);
4.59 Impedance Control Register 1 (ZQnCR1)
Bits 7-0 - ZPROG (Impedance Divide Ratio), reset value 0x7B
- writel(0x00000003, &mctl_phy->pir);
4.34 PHY Initialization Register (PIR)
Bit 0 - INIT Bit 1 - ZCAL
- udelay(10);
- mctl_await_completion(&mctl_phy->pgsr0, 0x09, 0x09);
4.38 PHY General Status Register 0 (PGSR0)
Bit 0 - IDONE (Initialization Done) Bit 3 - ZCDONE (Impedance Calibration Done)
- writel(readl(&mctl_phy->zqsr0) | 0x10000000, &mctl_phy->zqcr2);
- writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
Overwriting ZPROG again? Is it doing something reasonable? Or might it be an attempt to hide data from the tools like a10-meminfo (which can dump the DRAM setting via /dev/mem)?
I've no clue.
Regards,
Hans
- /* A23-v1.0 SDK uses 0xfdf3, A23-v2.0 SDK uses 0x5f3 */
- writel(0x000005f3, &mctl_phy->pir);
- udelay(10);
- mctl_await_completion(&mctl_phy->pgsr0, 0x03, 0x03);
- if (readl(&mctl_phy->dx1gsr0) & 0x1000000) {
*bus_width = 8;
writel(0, &mctl_phy->dx1gcr);
writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
writel(0x5f3, &mctl_phy->pir);
udelay(10000);
setbits_le32(&mctl_ctl->mstr, 0x1000);
- } else
*bus_width = 16;
- correction = (dram_para.odt_en >> 8) & 0xff;
- if (correction) {
if (dram_para.odt_en & 0x80000000)
correction = -correction;
mctl_apply_odt_correction(&mctl_phy->dx0lcdlr1, correction);
mctl_apply_odt_correction(&mctl_phy->dx1lcdlr1, correction);
- }
- mctl_await_completion(&mctl_ctl->statr, 0x01, 0x01);
- writel(0x08003e3f, &mctl_phy->pgcr0);
- writel(0x00000000, &mctl_ctl->rfshctl3);
+}
+unsigned long sunxi_dram_init(void) +{
- struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
- const u32 columns = 13;
- u32 bus, bus_width, offset, page_size, rows;
- mctl_sys_init();
- mctl_init(&bus_width);
- if (bus_width == 16) {
page_size = 8;
bus = 1;
- } else {
page_size = 7;
bus = 0;
- }
- if (!(dram_para.tpr13 & 0x80000000)) {
/* Detect and set rows */
writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
mctl_mem_fill();
for (rows = 11; rows < 16; rows++) {
offset = 1 << (rows + columns + bus);
if (mctl_mem_matches(offset))
break;
}
clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK,
MCTL_CR_ROW(rows));
- } else {
rows = (dram_para.para1 >> 16) & 0xff;
writel(((dram_para.para2 & 0x000000f0) << 11) |
((rows - 1) << 4) |
((dram_para.para1 & 0x0f000000) >> 22) |
0x31000 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
- }
- /* Setup DRAM master priority? If this is left out things still work */
- writel(0x00000008, &mctl_com->mcr0_0);
- writel(0x0001000d, &mctl_com->mcr1_0);
- writel(0x00000004, &mctl_com->mcr0_1);
- writel(0x00000080, &mctl_com->mcr1_1);
- writel(0x00000004, &mctl_com->mcr0_2);
- writel(0x00000019, &mctl_com->mcr1_2);
- writel(0x00000004, &mctl_com->mcr0_3);
- writel(0x00000080, &mctl_com->mcr1_3);
- writel(0x00000004, &mctl_com->mcr0_4);
- writel(0x01010040, &mctl_com->mcr1_4);
- writel(0x00000004, &mctl_com->mcr0_5);
- writel(0x0001002f, &mctl_com->mcr1_5);
- writel(0x00000004, &mctl_com->mcr0_6);
- writel(0x00010020, &mctl_com->mcr1_6);
- writel(0x00000004, &mctl_com->mcr0_7);
- writel(0x00010020, &mctl_com->mcr1_7);
- writel(0x00000008, &mctl_com->mcr0_8);
- writel(0x00000001, &mctl_com->mcr1_8);
- writel(0x00000008, &mctl_com->mcr0_9);
- writel(0x00000005, &mctl_com->mcr1_9);
- writel(0x00000008, &mctl_com->mcr0_10);
- writel(0x00000003, &mctl_com->mcr1_10);
- writel(0x00000008, &mctl_com->mcr0_11);
- writel(0x00000005, &mctl_com->mcr1_11);
- writel(0x00000008, &mctl_com->mcr0_12);
- writel(0x00000003, &mctl_com->mcr1_12);
- writel(0x00000008, &mctl_com->mcr0_13);
- writel(0x00000004, &mctl_com->mcr1_13);
- writel(0x00000008, &mctl_com->mcr0_14);
- writel(0x00000002, &mctl_com->mcr1_14);
- writel(0x00000008, &mctl_com->mcr0_15);
- writel(0x00000003, &mctl_com->mcr1_15);
- writel(0x00010138, &mctl_com->bwcr);
- return 1 << (rows + columns + bus);
+} diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 7d61216..45a199c 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -273,7 +273,11 @@ struct sunxi_ccm_reg { #define CCM_HDMI_CTRL_DDC_GATE (0x1 << 30) #define CCM_HDMI_CTRL_GATE (0x1 << 31)
+#ifndef CONFIG_MACH_SUN8I #define MBUS_CLK_DEFAULT 0x81000001 /* PLL6 / 2 */ +#else +#define MBUS_CLK_DEFAULT 0x81000003 /* PLL6 / 4 */ +#endif
#define CCM_PLL5_PATTERN 0xd1303333
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index a8a37d5..8d78029 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -18,6 +18,8 @@ /* dram regs definition */ #if defined(CONFIG_MACH_SUN6I) #include <asm/arch/dram_sun6i.h> +#elif defined(CONFIG_MACH_SUN8I) +#include <asm/arch/dram_sun8i.h> #else #include <asm/arch/dram_sun4i.h> #endif diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun8i.h b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h new file mode 100644 index 0000000..425cf37 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun8i.h @@ -0,0 +1,266 @@ +/*
- Sun8i platform dram controller register and constant defines
- (C) Copyright 2007-2013
- Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- Jerry Wang wangflord@allwinnertech.com
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef _SUNXI_DRAM_SUN8I_H +#define _SUNXI_DRAM_SUN8I_H
+struct dram_para {
- u32 clock;
- u32 type;
- u32 zq;
- u32 odt_en;
- u32 para1;
- u32 para2;
- u32 mr0;
- u32 mr1;
- u32 mr2;
- u32 mr3;
- u32 tpr0;
- u32 tpr1;
- u32 tpr2;
- u32 tpr3;
- u32 tpr4;
- u32 tpr5;
- u32 tpr6;
- u32 tpr7;
- u32 tpr8;
- u32 tpr9;
- u32 tpr10;
- u32 tpr11;
- u32 tpr12;
- u32 tpr13;
+};
+struct sunxi_mctl_com_reg {
- u32 cr; /* 0x00 */
- u32 ccr; /* 0x04 controller configuration register */
- u32 dbgcr; /* 0x08 */
- u8 res0[0x4]; /* 0x0c */
- u32 mcr0_0; /* 0x10 */
- u32 mcr1_0; /* 0x14 */
- u32 mcr0_1; /* 0x18 */
- u32 mcr1_1; /* 0x1c */
- u32 mcr0_2; /* 0x20 */
- u32 mcr1_2; /* 0x24 */
- u32 mcr0_3; /* 0x28 */
- u32 mcr1_3; /* 0x2c */
- u32 mcr0_4; /* 0x30 */
- u32 mcr1_4; /* 0x34 */
- u32 mcr0_5; /* 0x38 */
- u32 mcr1_5; /* 0x3c */
- u32 mcr0_6; /* 0x40 */
- u32 mcr1_6; /* 0x44 */
- u32 mcr0_7; /* 0x48 */
- u32 mcr1_7; /* 0x4c */
- u32 mcr0_8; /* 0x50 */
- u32 mcr1_8; /* 0x54 */
- u32 mcr0_9; /* 0x58 */
- u32 mcr1_9; /* 0x5c */
- u32 mcr0_10; /* 0x60 */
- u32 mcr1_10; /* 0x64 */
- u32 mcr0_11; /* 0x68 */
- u32 mcr1_11; /* 0x6c */
- u32 mcr0_12; /* 0x70 */
- u32 mcr1_12; /* 0x74 */
- u32 mcr0_13; /* 0x78 */
- u32 mcr1_13; /* 0x7c */
- u32 mcr0_14; /* 0x80 */
- u32 mcr1_14; /* 0x84 */
- u32 mcr0_15; /* 0x88 */
- u32 mcr1_15; /* 0x8c */
- u32 bwcr; /* 0x90 */
- u32 maer; /* 0x94 */
- u8 res1[0x4]; /* 0x98 */
- u32 mcgcr; /* 0x9c */
- u32 bwctr; /* 0xa0 */
- u8 res2[0x4]; /* 0xa4 */
- u32 swonr; /* 0xa8 */
- u32 swoffr; /* 0xac */
+};
+struct sunxi_mctl_ctl_reg {
- u32 mstr; /* 0x00 */
- u32 statr; /* 0x04 */
- u8 res0[0x08]; /* 0x08 */
- u32 mrctrl0; /* 0x10 */
- u32 mrctrl1; /* 0x14 */
- u32 mrstatr; /* 0x18 */
- u8 res1[0x04]; /* 0x1c */
- u32 derateen; /* 0x20 */
- u32 deratenint; /* 0x24 */
- u8 res2[0x08]; /* 0x28 */
- u32 pwrctl; /* 0x30 */
- u32 pwrtmg; /* 0x34 */
- u8 res3[0x18]; /* 0x38 */
- u32 rfshctl0; /* 0x50 */
- u32 rfshctl1; /* 0x54 */
- u8 res4[0x8]; /* 0x58 */
- u32 rfshctl3; /* 0x60 */
- u32 rfshtmg; /* 0x64 */
- u8 res6[0x68]; /* 0x68 */
- u32 init0; /* 0xd0 */
- u32 init1; /* 0xd4 */
- u32 init2; /* 0xd8 */
- u32 init3; /* 0xdc */
- u32 init4; /* 0xe0 */
- u32 init5; /* 0xe4 */
- u8 res7[0x0c]; /* 0xe8 */
- u32 rankctl; /* 0xf4 */
- u8 res8[0x08]; /* 0xf8 */
- u32 dramtmg0; /* 0x100 */
- u32 dramtmg1; /* 0x104 */
- u32 dramtmg2; /* 0x108 */
- u32 dramtmg3; /* 0x10c */
- u32 dramtmg4; /* 0x110 */
- u32 dramtmg5; /* 0x114 */
- u32 dramtmg6; /* 0x118 */
- u32 dramtmg7; /* 0x11c */
- u32 dramtmg8; /* 0x120 */
- u8 res9[0x5c]; /* 0x124 */
- u32 zqctl0; /* 0x180 */
- u32 zqctl1; /* 0x184 */
- u32 zqctl2; /* 0x188 */
- u32 zqstat; /* 0x18c */
- u32 pitmg0; /* 0x190 */
- u32 pitmg1; /* 0x194 */
- u32 plpcfg0; /* 0x198 */
- u8 res10[0x04]; /* 0x19c */
- u32 upd0; /* 0x1a0 */
- u32 upd1; /* 0x1a4 */
- u32 upd2; /* 0x1a8 */
- u32 upd3; /* 0x1ac */
- u32 pimisc; /* 0x1b0 */
- u8 res11[0x1c]; /* 0x1b4 */
- u32 trainctl0; /* 0x1d0 */
- u32 trainctl1; /* 0x1d4 */
- u32 trainctl2; /* 0x1d8 */
- u32 trainstat; /* 0x1dc */
- u8 res12[0x60]; /* 0x1e0 */
- u32 odtcfg; /* 0x240 */
- u32 odtmap; /* 0x244 */
- u8 res13[0x08]; /* 0x248 */
- u32 sched; /* 0x250 */
- u8 res14[0x04]; /* 0x254 */
- u32 perfshpr0; /* 0x258 */
- u32 perfshpr1; /* 0x25c */
- u32 perflpr0; /* 0x260 */
- u32 perflpr1; /* 0x264 */
- u32 perfwr0; /* 0x268 */
- u32 perfwr1; /* 0x26c */
+};
+struct sunxi_mctl_phy_reg {
- u8 res0[0x04]; /* 0x00 */
- u32 pir; /* 0x04 */
- u32 pgcr0; /* 0x08 phy general configuration register */
- u32 pgcr1; /* 0x0c phy general configuration register */
- u32 pgsr0; /* 0x10 */
- u32 pgsr1; /* 0x14 */
- u32 dllgcr; /* 0x18 */
- u32 ptr0; /* 0x1c */
- u32 ptr1; /* 0x20 */
- u32 ptr2; /* 0x24 */
- u32 ptr3; /* 0x28 */
- u32 ptr4; /* 0x2c */
- u32 acmdlr; /* 0x30 */
- u32 acbdlr; /* 0x34 */
- u32 aciocr; /* 0x38 */
- u32 dxccr; /* 0x3c DATX8 common configuration register */
- u32 dsgcr; /* 0x40 dram system general config register */
- u32 dcr; /* 0x44 */
- u32 dtpr0; /* 0x48 dram timing parameters register 0 */
- u32 dtpr1; /* 0x4c dram timing parameters register 1 */
- u32 dtpr2; /* 0x50 dram timing parameters register 2 */
- u32 mr0; /* 0x54 mode register 0 */
- u32 mr1; /* 0x58 mode register 1 */
- u32 mr2; /* 0x5c mode register 2 */
- u32 mr3; /* 0x60 mode register 3 */
- u32 odtcr; /* 0x64 */
- u32 dtcr; /* 0x68 */
- u32 dtar0; /* 0x6c data training address register 0 */
- u32 dtar1; /* 0x70 data training address register 1 */
- u32 dtar2; /* 0x74 data training address register 2 */
- u32 dtar3; /* 0x78 data training address register 3 */
- u32 dtdr0; /* 0x7c */
- u32 dtdr1; /* 0x80 */
- u32 dtedr0; /* 0x84 */
- u32 dtedr1; /* 0x88 */
- u32 pgcr2; /* 0x8c */
- u8 res1[0x70]; /* 0x90 */
- u32 bistrr; /* 0x100 */
- u32 bistwcr; /* 0x104 */
- u32 bistmskr0; /* 0x108 */
- u32 bistmskr1; /* 0x10c */
- u32 bistmskr2; /* 0x110 */
- u32 bistlsr; /* 0x114 */
- u32 bistar0; /* 0x118 */
- u32 bistar1; /* 0x11c */
- u32 bistar2; /* 0x120 */
- u32 bistupdr; /* 0x124 */
- u32 bistgsr; /* 0x128 */
- u32 bistwer; /* 0x12c */
- u32 bistber0; /* 0x130 */
- u32 bistber1; /* 0x134 */
- u32 bistber2; /* 0x138 */
- u32 bistber3; /* 0x13c */
- u32 bistwcsr; /* 0x140 */
- u32 bistfwr0; /* 0x144 */
- u32 bistfwr1; /* 0x148 */
- u32 bistfwr2; /* 0x14c */
- u8 res2[0x30]; /* 0x150 */
- u32 zqcr0; /* 0x180 zq control register 0 */
- u32 zqcr1; /* 0x184 zq control register 1 */
- u32 zqsr0; /* 0x188 zq status register 0 */
- u32 zqsr1; /* 0x18c zq status register 1 */
- u32 zqcr2; /* 0x190 zq control register 2 */
- u8 res3[0x2c]; /* 0x194 */
- u32 dx0gcr; /* 0x1c0 */
- u32 dx0gsr0; /* 0x1c4 */
- u32 dx0gsr1; /* 0x1c8 */
- u32 dx0bdlr0; /* 0x1cc */
- u32 dx0bdlr1; /* 0x1d0 */
- u32 dx0bdlr2; /* 0x1d4 */
- u32 dx0bdlr3; /* 0x1d8 */
- u32 dx0bdlr4; /* 0x1dc */
- u32 dx0lcdlr0; /* 0x1e0 */
- u32 dx0lcdlr1; /* 0x1e4 */
- u32 dx0lcdlr2; /* 0x1e8 */
- u32 dx0mdlr; /* 0x1ec */
- u32 dx0gtr; /* 0x1f0 */
- u32 dx0gsr2; /* 0x1f4 */
- u8 res4[0x08]; /* 0x1f8 */
- u32 dx1gcr; /* 0x200 */
- u32 dx1gsr0; /* 0x204 */
- u32 dx1gsr1; /* 0x208 */
- u32 dx1bdlr0; /* 0x20c */
- u32 dx1bdlr1; /* 0x210 */
- u32 dx1bdlr2; /* 0x214 */
- u32 dx1bdlr3; /* 0x218 */
- u32 dx1bdlr4; /* 0x21c */
- u32 dx1lcdlr0; /* 0x220 */
- u32 dx1lcdlr1; /* 0x224 */
- u32 dx1lcdlr2; /* 0x228 */
- u32 dx1mdlr; /* 0x22c */
- u32 dx1gtr; /* 0x230 */
- u32 dx1gsr2; /* 0x234 */
+};
+/*
- DRAM common (sunxi_mctl_com_reg) register constants.
- */
+#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) ((x) << 8)
+#endif /* _SUNXI_DRAM_SUN8I_H */ diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 5bb2f58..5a88ba0 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -29,10 +29,11 @@ config MACH_SUN7I config MACH_SUN8I bool "sun8i (Allwinner A23)" select CPU_V7
select SUPPORT_SPL
endchoice
-if MACH_SUN6I +if MACH_SUN6I || MACH_SUN8I
config DRAM_CLK int "sun6i dram clock speed" diff --git a/configs/Ippo_q8h_v5_defconfig b/configs/Ippo_q8h_v5_defconfig index 50c2f93..37aa46d 100644 --- a/configs/Ippo_q8h_v5_defconfig +++ b/configs/Ippo_q8h_v5_defconfig @@ -1,8 +1,15 @@ +CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" -CONFIG_ARM=y -CONFIG_ARCH_SUNXI=y -CONFIG_MACH_SUN8I=y -CONFIG_TARGET_IPPO_Q8H_V5=y -CONFIG_DEFAULT_DEVICE_TREE="sun8i-a23-ippo-q8h-v5.dtb" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v5.dtb" CONFIG_VIDEO=n CONFIG_USB_KEYBOARD=n ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN8I=y ++S:CONFIG_DRAM_CLK=480 +# zq = 0xf777 ++S:CONFIG_DRAM_ZQ=63351 +# Wifi power ++S:CONFIG_AXP221_DLDO1_VOLT=3300 +# aldo1 is connected to VCC-IO, VCC-PD, VCC-USB and VCC-HP ++S:CONFIG_AXP221_ALDO1_VOLT=3000 diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 6f1fc48..792422d 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -12,6 +12,8 @@ /*
- A23 specific configuration
*/ +#define CONFIG_CLK_FULL_SPEED 1008000000
#define CONFIG_SYS_PROMPT "sun8i# "
/*

On Fri, 19 Dec 2014 18:05:49 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hi,
On 19-12-14 11:20, Siarhei Siamashka wrote:
On Tue, 16 Dec 2014 21:31:38 +0100 Hans de Goede hdegoede@redhat.com wrote:
Based on the register / dram_para headers from the Allwinner u-boot / linux sources + the init sequences from boot0.
Can the commit message have more detailed information about the precise location of the original Allwinner code, which was used as the reference?
No, it cannot because no code was referenced, I traced the boot0 binary to figure out what was needed to get dram going on the A23.
Thanks for explaining. And good job extracting all the information from there.
In this case, can we have a clear reference to this binary in the commit message? It might be useful to have a look at objdump logs when reviewing your patch.
[...]
+++ b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c @@ -0,0 +1,340 @@ +/*
- Sun8i platform dram controller init.
- (C) Copyright 2014 Hans de Goede hdegoede@redhat.com
Is Allwinner copyright really not necessary here?
- SPDX-License-Identifier: GPL-2.0+
- */
+/*
- Note this code uses a lot of magic hex values, that is because this code
- simply replays the init sequence as done by the Allwinner boot0 code, so
- we do not know what these values mean. There are no symbolic constants for
- these magic values, since we do not know how to name them and making up
- names for them is not useful.
- */
You are well aware of this documentation since a long time ago, right? http://www.ti.com/lit/ug/spruhn7a/spruhn7a.pdf
I'm aware you've used various sources to figure out more about the A10 dram controller, since this DRAM controller seems significantly different I assumed your sources would not apply, it is good to hear that they do.
I wonder how could you have ended up with this strange assumption? When I clearly told you that "It looks like the Allwinner A31 DRAM controller registers are very similar to what is used in RK3288" and provided the links to the relevant sources and documentation back in September: http://lists.denx.de/pipermail/u-boot/2014-September/189199.html
Then reminded yet again about this RK3288 similarity and used the mctl_phy->ptr0 register as an example: http://lists.denx.de/pipermail/u-boot/2014-December/198582.html
When we were discussing the A31 DRAM controller, of course you should have expected the information about A31.
Yes, A23 was not a part of the picture at that time. However comparing its registers with the information from spruhn7a.pdf and various Rockchip source code drops is a very natural thing to try.
So far the best matching pairs (almost 100% identical PHY) are: Allwinner A10 (sun4i) - RK29xx Allwinner A31 (sun6i) - RK3288 Allwinner A23 (sun8i) - TI Keystone2
It's all the same family of DRAM controllers, very likely just different revisions/generations from the same supplier. I don't expect A80 or other Allwinner chips to be any different.
[...]
- else
writel(0x1000040b, &mctl_phy->dcr);
The bit 28 is marked as reserved and "Reads return zeros" in the TI Keystone2 documentation. It's a nice chance to test whether this write has any effect and whether it does modify the register.
It modifies the register, so that means that at least that bit does not match the TI Keystone2 documentation.
OK.

We need separate defconfigs for the v5 and v1.2 versions of this board, as they use different DRAM parameters.
Note they also use different dtb files, as the wifi is different too.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- configs/Ippo_q8h_v1.2_defconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 configs/Ippo_q8h_v1.2_defconfig
diff --git a/configs/Ippo_q8h_v1.2_defconfig b/configs/Ippo_q8h_v1.2_defconfig new file mode 100644 index 0000000..fefed32 --- /dev/null +++ b/configs/Ippo_q8h_v1.2_defconfig @@ -0,0 +1,15 @@ +CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v1.2.dtb" +CONFIG_VIDEO=n +CONFIG_USB_KEYBOARD=n ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN8I=y ++S:CONFIG_DRAM_CLK=432 +# zq = 0xf74a ++S:CONFIG_DRAM_ZQ=63306 +# Wifi power ++S:CONFIG_AXP221_DLDO1_VOLT=3300 +# aldo1 is connected to VCC-IO, VCC-PD, VCC-USB and VCC-HP ++S:CONFIG_AXP221_ALDO1_VOLT=3000

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
We need separate defconfigs for the v5 and v1.2 versions of this board, as they use different DRAM parameters.
Note they also use different dtb files, as the wifi is different too.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
(I think this is the patch you mentioned just now WRT having added a MAINTAINERS entry, so I won't mention it again ;-))
diff --git a/configs/Ippo_q8h_v1.2_defconfig b/configs/Ippo_q8h_v1.2_defconfig new file mode 100644 index 0000000..fefed32 --- /dev/null +++ b/configs/Ippo_q8h_v1.2_defconfig @@ -0,0 +1,15 @@ +CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" +CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v1.2.dtb" +CONFIG_VIDEO=n +CONFIG_USB_KEYBOARD=n ++S:CONFIG_ARM=y ++S:CONFIG_ARCH_SUNXI=y ++S:CONFIG_MACH_SUN8I=y
No CONFIG_TARGET? Do you want to send a patch to nuke them all, better than having a mixture IMHO.
Ian.

On Tue, 2014-12-16 at 21:31 +0100, Hans de Goede wrote:
The p2wi interface is only available on sun6i, adjust the gpio pinmux defines for it to reflect this.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
participants (4)
-
Chen-Yu Tsai
-
Hans de Goede
-
Ian Campbell
-
Siarhei Siamashka