Skip to contentSkip to conversation

sun6i: dram-init & SPL support

PATCH Superseded Hans de Goede 4 participants 19 messages Nov 3, 2014 – Nov 5, 2014

Subsystems (3)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover sun6i: dram-init & SPL support

Hi All,

After spending a lot of time on this, I'm happy to present this patch series adding A31 dram init and SPL support, allowing one to boot Allwinner A31 boards using just u-boot for all stages of the boot :)

Please review, I will add these to u-boot-sunxi/next and send a pull-req as soon as these have gathered the necessary acks.

Regards,

Hans

#1 lore raw

PATCH 1/9 sun6i: Add new p2wi controller driver

From: Oliver Schinagl <[email protected]>

The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.

---
 arch/arm/cpu/armv7/sunxi/Makefile      |   1 +
 arch/arm/cpu/armv7/sunxi/p2wi.c        | 117 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/gpio.h |   3 +
 arch/arm/include/asm/arch-sunxi/p2wi.h | 140 +++++++++++++++++++++++++++++++++
 4 files changed, 261 insertions(+)
 create mode 100644 arch/arm/cpu/armv7/sunxi/p2wi.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/p2wi.h
arch/arm/cpu/armv7/sunxi/Makefile +1 -0
index 82dbf76..b3a3601 100644
@@ -13,6 +13,7 @@ obj-y	+= clock.o
 obj-y	+= pinmux.o
 obj-$(CONFIG_MACH_SUN6I)	+= prcm.o
 obj-$(CONFIG_MACH_SUN8I)	+= prcm.o
+obj-$(CONFIG_MACH_SUN6I)	+= p2wi.o
 obj-$(CONFIG_MACH_SUN4I)	+= clock_sun4i.o
 obj-$(CONFIG_MACH_SUN5I)	+= clock_sun4i.o
 obj-$(CONFIG_MACH_SUN6I)	+= clock_sun6i.o
arch/arm/cpu/armv7/sunxi/p2wi.c new file +117 -0

117 lines not on this page · raw message

arch/arm/include/asm/arch-sunxi/gpio.h +3 -0
index 437dd35..c734cf0 100644
@@ -172,6 +172,9 @@ enum sunxi_gpio_number {
 
 #define SUN4I_GPI4_SDC3		2
 
+#define SUNXI_GPL0_R_P2WI_SCK	3
+#define SUNXI_GPL1_R_P2WI_SDA	3
+
 #define SUN8I_GPL2_R_UART_TX	2
 #define SUN8I_GPL3_R_UART_RX	2
 
arch/arm/include/asm/arch-sunxi/p2wi.h new file +140 -0

140 lines not on this page · raw message

Signature · 1 line
2.1.0
#2 lore raw

PATCH 1/9 sun6i: Add new p2wi controller driver

Hi,

Quoted 70 lines · Hans de Goede

From: Oliver Schinagl <[email protected]>

The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.

---
 arch/arm/cpu/armv7/sunxi/Makefile      |   1 +
 arch/arm/cpu/armv7/sunxi/p2wi.c        | 117 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/gpio.h |   3 +
 arch/arm/include/asm/arch-sunxi/p2wi.h | 140 +++++++++++++++++++++++++++++++++
 4 files changed, 261 insertions(+)
 create mode 100644 arch/arm/cpu/armv7/sunxi/p2wi.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/p2wi.h
arch/arm/cpu/armv7/sunxi/Makefile +1 -0
index 82dbf76..b3a3601 100644
@@ -13,6 +13,7 @@ obj-y += clock.o
 obj-y  += pinmux.o
 obj-$(CONFIG_MACH_SUN6I)       += prcm.o
 obj-$(CONFIG_MACH_SUN8I)       += prcm.o
+obj-$(CONFIG_MACH_SUN6I)       += p2wi.o
 obj-$(CONFIG_MACH_SUN4I)       += clock_sun4i.o
 obj-$(CONFIG_MACH_SUN5I)       += clock_sun4i.o
 obj-$(CONFIG_MACH_SUN6I)       += clock_sun6i.o
arch/arm/cpu/armv7/sunxi/p2wi.c new file +34 -0
new file mode 100644
index 0000000..10df348
@@ -0,0 +1,117 @@
+/*
+ * Sunxi A31 Power Management Unit
+ *
+ * (C) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * Based on sun6i sources and earlier U-Boot Allwiner A10 SPL work
+ *
+ * (C) Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/p2wi.h>
+#include <asm/arch/prcm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+
+void p2wi_init(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+
+       /* Enable p2wi and PIO clk, and de-assert their resets */
+       prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
+
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUNXI_GPL0_R_P2WI_SCK);

arch/arm/cpu/armv7/sunxi/p2wi.c line 44

Quoted 10 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +10 -0
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUNXI_GPL1_R_P2WI_SDA);
+
+       /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */
+       writel(P2WI_CTRL_RESET, &p2wi->ctrl);
+       sdelay(0x100);
+       writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8),
+              &p2wi->cc);
+}
+
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data)

set_pmu_address is actually a misnomer. What it actually does is write <init_data> to <ctrl_reg> on device found at <slave_addr>, probably using I2C, with the sole purpose of putting the device into P2WI mode (or RSB mode for later AXPs).

According to the docs/code we have, it is always writing 0x3e to register 0x3e.

Quoted 216 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +73 -0
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       unsigned long tmo = timer_get_us() + 1000000;
+
+       writel(P2WI_PM_DEV_ADDR(slave_addr) |
+              P2WI_PM_CTRL_ADDR(ctrl_reg) |
+              P2WI_PM_INIT_DATA(init_data) |
+              P2WI_PM_INIT_SEND,
+              &p2wi->pm);
+
+       while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
+               if (timer_get_us() > tmo)
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int p2wi_await_trans(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       unsigned long tmo = timer_get_us() + 1000000;
+       int ret;
+       u8 reg;
+
+       while (1) {
+               reg = readl(&p2wi->status);
+               if (reg & P2WI_STAT_TRANS_ERR) {
+                       ret = -EIO;
+                       break;
+               }
+               if (reg & P2WI_STAT_TRANS_DONE) {
+                       ret = 0;
+                       break;
+               }
+               if (timer_get_us() > tmo) {
+                       ret = -ETIME;
+                       break;
+               }
+       }
+       writel(reg, &p2wi->status); /* Clear status bits */
+       return ret;
+}
+
+int p2wi_read(const u8 addr, u8 *data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       int ret;
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_NUM_BYTES(1) |
+              P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       ret = p2wi_await_trans();
+
+       *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK;
+       return ret;
+}
+
+int p2wi_write(const u8 addr, u8 data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_BYTE_1(data), &p2wi->data0);
+       writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       return p2wi_await_trans();
+}
arch/arm/include/asm/arch-sunxi/gpio.h +3 -0
index 437dd35..c734cf0 100644
@@ -172,6 +172,9 @@ enum sunxi_gpio_number {
 
 #define SUN4I_GPI4_SDC3                2
 
+#define SUNXI_GPL0_R_P2WI_SCK  3
+#define SUNXI_GPL1_R_P2WI_SDA  3
+
 #define SUN8I_GPL2_R_UART_TX   2
 #define SUN8I_GPL3_R_UART_RX   2
 
arch/arm/include/asm/arch-sunxi/p2wi.h new file +123 -0
new file mode 100644
index 0000000..1788fa4
@@ -0,0 +1,140 @@
+/*
+ * Sunxi platform Push-Push i2c register definition.
+ *
+ * (c) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * (c)Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _SUNXI_P2WI_H
+#define _SUNXI_P2WI_H
+
+#include <linux/types.h>
+
+#define P2WI_CTRL_RESET (0x1 << 0)
+#define P2WI_CTRL_IRQ_EN (0x1 << 1)
+#define P2WI_CTRL_TRANS_ABORT (0x1 << 6)
+#define P2WI_CTRL_TRANS_START (0x1 << 7)
+
+#define __P2WI_CC_CLK(n) (((n) & 0xff) << 0)
+#define P2WI_CC_CLK_MASK __P2WI_CC_CLK_DIV(0xff)
+#define __P2WI_CC_CLK_DIV(n) (((n) >> 1) - 1)
+#define P2WI_CC_CLK_DIV(n) \
+       __P2WI_CC_CLK(__P2WI_CC_CLK_DIV(n))
+#define P2WI_CC_SDA_OUT_DELAY(n) (((n) & 0x7) << 8)
+#define P2WI_CC_SDA_OUT_DELAY_MASK P2WI_CC_SDA_OUT_DELAY(0x7)
+
+#define P2WI_IRQ_TRANS_DONE (0x1 << 0)
+#define P2WI_IRQ_TRANS_ERR (0x1 << 1)
+#define P2WI_IRQ_LOAD_BUSY (0x1 << 2)
+
+#define P2WI_STAT_TRANS_DONE (0x1 << 0)
+#define P2WI_STAT_TRANS_ERR (0x1 << 1)
+#define P2WI_STAT_LOAD_BUSY (0x1 << 2)
+#define __P2WI_STAT_TRANS_ERR(n) (((n) & 0xff) << 8)
+#define P2WI_STAT_TRANS_ERR_MASK __P2WI_STAT_TRANS_ERR_ID(0xff)
+#define __P2WI_STAT_TRANS_ERR_BYTE_1 0x01
+#define __P2WI_STAT_TRANS_ERR_BYTE_2 0x02
+#define __P2WI_STAT_TRANS_ERR_BYTE_3 0x04
+#define __P2WI_STAT_TRANS_ERR_BYTE_4 0x08
+#define __P2WI_STAT_TRANS_ERR_BYTE_5 0x10
+#define __P2WI_STAT_TRANS_ERR_BYTE_6 0x20
+#define __P2WI_STAT_TRANS_ERR_BYTE_7 0x40
+#define __P2WI_STAT_TRANS_ERR_BYTE_8 0x80
+#define P2WI_STAT_TRANS_ERR_BYTE_1 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_1)
+#define P2WI_STAT_TRANS_ERR_BYTE_2 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_2)
+#define P2WI_STAT_TRANS_ERR_BYTE_3 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_3)
+#define P2WI_STAT_TRANS_ERR_BYTE_4 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_4)
+#define P2WI_STAT_TRANS_ERR_BYTE_5 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_5)
+#define P2WI_STAT_TRANS_ERR_BYTE_6 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_6)
+#define P2WI_STAT_TRANS_ERR_BYTE_7 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_7)
+#define P2WI_STAT_TRANS_ERR_BYTE_8 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_8)
+
+#define P2WI_DATADDR_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_1_MASK P2WI_DATADDR_BYTE_1(0xff)
+#define P2WI_DATADDR_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_2_MASK P2WI_DATADDR_BYTE_2(0xff)
+#define P2WI_DATADDR_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_3_MASK P2WI_DATADDR_BYTE_3(0xff)
+#define P2WI_DATADDR_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_4_MASK P2WI_DATADDR_BYTE_4(0xff)
+#define P2WI_DATADDR_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_5_MASK P2WI_DATADDR_BYTE_5(0xff)
+#define P2WI_DATADDR_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_6_MASK P2WI_DATADDR_BYTE_6(0xff)
+#define P2WI_DATADDR_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_7_MASK P2WI_DATADDR_BYTE_7(0xff)
+#define P2WI_DATADDR_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_8_MASK P2WI_DATADDR_BYTE_8(0xff)
+
+#define __P2WI_DATA_NUM_BYTES(n) (((n) & 0x7) << 0)
+#define P2WI_DATA_NUM_BYTES_MASK __P2WI_DATA_NUM_BYTES(0x7)
+#define P2WI_DATA_NUM_BYTES(n) __P2WI_DATA_NUM_BYTES((n) - 1)
+#define P2WI_DATA_NUM_BYTES_READ (0x1 << 4)
+
+#define P2WI_DATA_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_1_MASK P2WI_DATA_BYTE_1(0xff)
+#define P2WI_DATA_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_2_MASK P2WI_DATA_BYTE_2(0xff)
+#define P2WI_DATA_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_3_MASK P2WI_DATA_BYTE_3(0xff)
+#define P2WI_DATA_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_4_MASK P2WI_DATA_BYTE_4(0xff)
+#define P2WI_DATA_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_5_MASK P2WI_DATA_BYTE_5(0xff)
+#define P2WI_DATA_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_6_MASK P2WI_DATA_BYTE_6(0xff)
+#define P2WI_DATA_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_7_MASK P2WI_DATA_BYTE_7(0xff)
+#define P2WI_DATA_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_8_MASK P2WI_DATA_BYTE_8(0xff)
+
+#define P2WI_LINECTRL_SDA_CTRL_EN (0x1 << 0)
+#define P2WI_LINECTRL_SDA_OUT_HIGH (0x1 << 1)
+#define P2WI_LINECTRL_SCL_CTRL_EN (0x1 << 2)
+#define P2WI_LINECTRL_SCL_OUT_HIGH (0x1 << 3)
+#define P2WI_LINECTRL_SDA_STATE_HIGH (0x1 << 4)
+#define P2WI_LINECTRL_SCL_STATE_HIGH (0x1 << 5)
+
+#define P2WI_PM_DEV_ADDR(n) (((n) & 0xff) << 0)
+#define P2WI_PM_DEV_ADDR_MASK P2WI_PM_DEV_ADDR(0xff)
+#define P2WI_PM_CTRL_ADDR(n) (((n) & 0xff) << 8)
+#define P2WI_PM_CTRL_ADDR_MASK P2WI_PM_CTRL_ADDR(0xff)
+#define P2WI_PM_INIT_DATA(n) (((n) & 0xff) << 16)
+#define P2WI_PM_INIT_DATA_MASK P2WI_PM_INIT_DATA(0xff)
+#define P2WI_PM_INIT_SEND (0x1 << 31)
+
+struct sunxi_p2wi_reg {
+       u32 ctrl;       /* 0x00 control */
+       u32 cc;         /* 0x04 clock control */

arch/arm/include/asm/arch-sunxi/p2wi.h line 133

Quoted 10 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +10 -0
+       u32 irq;        /* 0x08 interrupt */
+       u32 status;     /* 0x0c status */
+       u32 dataddr0;   /* 0x10 data address 0 */
+       u32 dataddr1;   /* 0x14 data address 1 */
+       u32 numbytes;   /* 0x18 num bytes */
+       u32 data0;      /* 0x1c data buffer 0 */
+       u32 data1;      /* 0x20 data buffer 1 */
+       u32 linectrl;   /* 0x24 line control */
+       u32 pm;         /* 0x28 power management */
+};

Is there any reason not to put all of the above in the driver (.c) itself?

Quoted 9 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +7 -1
+
+void p2wi_init(void);
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data);
+int p2wi_read(const u8 addr, u8 *data);
+int p2wi_write(const u8 addr, u8 data);
+
+#endif /* _SUNXI_P2WI_H */
--

2.1.0

Thanks for working on this! I haven't had time to mainline the p2wi driver.

ChenYu

#3 lore raw

PATCH 1/9 sun6i: Add new p2wi controller driver

Hy Wens,

This is some really old work, and I'm assuming hans picked it up, as I never tested the code (didn't have hardware at the time)

I'll comment as I remember it and hope that helps.

On a side note, do we know what p2wi stands for? I'm guessing 2wi is allwinners try at combining the names twi and i2c, as i2c is some philips trademark and maybe they think twi is an avr amongst others? so using 2wi is save? And the P, 'program twi? Powermanagment twi?

If that is whats happening here, I'd be inclined to rename it to to just regular twi, maybe axp_twi if we must?

Olliver

Quoted 88 lines · Chen-Yu Tsai

Hi,

Quoted 70 lines · Hans de Goede

From: Oliver Schinagl <[email protected]>

The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.

---
  arch/arm/cpu/armv7/sunxi/Makefile      |   1 +
  arch/arm/cpu/armv7/sunxi/p2wi.c        | 117 +++++++++++++++++++++++++++
  arch/arm/include/asm/arch-sunxi/gpio.h |   3 +
  arch/arm/include/asm/arch-sunxi/p2wi.h | 140 +++++++++++++++++++++++++++++++++
  4 files changed, 261 insertions(+)

create mode 100644 arch/arm/cpu/armv7/sunxi/p2wi.c create mode 100644 arch/arm/include/asm/arch-sunxi/p2wi.h

arch/arm/cpu/armv7/sunxi/Makefile +1 -0
index 82dbf76..b3a3601 100644
@@ -13,6 +13,7 @@ obj-y += clock.o
  obj-y  += pinmux.o
  obj-$(CONFIG_MACH_SUN6I)       += prcm.o
  obj-$(CONFIG_MACH_SUN8I)       += prcm.o
+obj-$(CONFIG_MACH_SUN6I)       += p2wi.o
  obj-$(CONFIG_MACH_SUN4I)       += clock_sun4i.o
  obj-$(CONFIG_MACH_SUN5I)       += clock_sun4i.o
  obj-$(CONFIG_MACH_SUN6I)       += clock_sun6i.o
arch/arm/cpu/armv7/sunxi/p2wi.c new file +34 -0
new file mode 100644
index 0000000..10df348
@@ -0,0 +1,117 @@
+/*
+ * Sunxi A31 Power Management Unit
+ *
+ * (C) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * Based on sun6i sources and earlier U-Boot Allwiner A10 SPL work
+ *
+ * (C) Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/p2wi.h>
+#include <asm/arch/prcm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+
+void p2wi_init(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+
+       /* Enable p2wi and PIO clk, and de-assert their resets */
+       prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
+
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUNXI_GPL0_R_P2WI_SCK);

arch/arm/cpu/armv7/sunxi/p2wi.c line 44

Quoted 10 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +10 -0
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUNXI_GPL1_R_P2WI_SDA);
+
+       /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */
+       writel(P2WI_CTRL_RESET, &p2wi->ctrl);
+       sdelay(0x100);
+       writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8),
+              &p2wi->cc);
+}
+
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data)

set_pmu_address is actually a misnomer. What it actually does is write <init_data> to <ctrl_reg> on device found at <slave_addr>, probably using I2C, with the sole purpose of putting the device into P2WI mode (or RSB mode for later AXPs).

Quoted 2 lines

According to the docs/code we have, it is always writing 0x3e to register 0x3e.

Back then, we didn't have no official docs, nor official code ;)

I think this is what boot0 for a31 called it, or something close to it. Do you know what other AXP's take for this <ctrl_reg>?

What are the chances, that 0x3e (in this chip) does what you say, enable p2wi or RSB mode? If that would be the case, a more generic function would be nice indeed, axp_init_mode(AXP_ADDR, AXP_MODE) or something comes to mind?

Quoted 220 lines
Quoted 220 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +73 -0
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       unsigned long tmo = timer_get_us() + 1000000;
+
+       writel(P2WI_PM_DEV_ADDR(slave_addr) |
+              P2WI_PM_CTRL_ADDR(ctrl_reg) |
+              P2WI_PM_INIT_DATA(init_data) |
+              P2WI_PM_INIT_SEND,
+              &p2wi->pm);
+
+       while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
+               if (timer_get_us() > tmo)
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int p2wi_await_trans(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       unsigned long tmo = timer_get_us() + 1000000;
+       int ret;
+       u8 reg;
+
+       while (1) {
+               reg = readl(&p2wi->status);
+               if (reg & P2WI_STAT_TRANS_ERR) {
+                       ret = -EIO;
+                       break;
+               }
+               if (reg & P2WI_STAT_TRANS_DONE) {
+                       ret = 0;
+                       break;
+               }
+               if (timer_get_us() > tmo) {
+                       ret = -ETIME;
+                       break;
+               }
+       }
+       writel(reg, &p2wi->status); /* Clear status bits */
+       return ret;
+}
+
+int p2wi_read(const u8 addr, u8 *data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       int ret;
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_NUM_BYTES(1) |
+              P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       ret = p2wi_await_trans();
+
+       *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK;
+       return ret;
+}
+
+int p2wi_write(const u8 addr, u8 data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_BYTE_1(data), &p2wi->data0);
+       writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       return p2wi_await_trans();
+}
arch/arm/include/asm/arch-sunxi/gpio.h +3 -0
index 437dd35..c734cf0 100644
@@ -172,6 +172,9 @@ enum sunxi_gpio_number {
 
  #define SUN4I_GPI4_SDC3                2
 
+#define SUNXI_GPL0_R_P2WI_SCK  3
+#define SUNXI_GPL1_R_P2WI_SDA  3
+
  #define SUN8I_GPL2_R_UART_TX   2
  #define SUN8I_GPL3_R_UART_RX   2
 
arch/arm/include/asm/arch-sunxi/p2wi.h new file +126 -0
new file mode 100644
index 0000000..1788fa4
@@ -0,0 +1,140 @@
+/*
+ * Sunxi platform Push-Push i2c register definition.
+ *
+ * (c) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * (c)Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _SUNXI_P2WI_H
+#define _SUNXI_P2WI_H
+
+#include <linux/types.h>
+
+#define P2WI_CTRL_RESET (0x1 << 0)
+#define P2WI_CTRL_IRQ_EN (0x1 << 1)
+#define P2WI_CTRL_TRANS_ABORT (0x1 << 6)
+#define P2WI_CTRL_TRANS_START (0x1 << 7)
+
+#define __P2WI_CC_CLK(n) (((n) & 0xff) << 0)
+#define P2WI_CC_CLK_MASK __P2WI_CC_CLK_DIV(0xff)
+#define __P2WI_CC_CLK_DIV(n) (((n) >> 1) - 1)
+#define P2WI_CC_CLK_DIV(n) \
+       __P2WI_CC_CLK(__P2WI_CC_CLK_DIV(n))
+#define P2WI_CC_SDA_OUT_DELAY(n) (((n) & 0x7) << 8)
+#define P2WI_CC_SDA_OUT_DELAY_MASK P2WI_CC_SDA_OUT_DELAY(0x7)
+
+#define P2WI_IRQ_TRANS_DONE (0x1 << 0)
+#define P2WI_IRQ_TRANS_ERR (0x1 << 1)
+#define P2WI_IRQ_LOAD_BUSY (0x1 << 2)
+
+#define P2WI_STAT_TRANS_DONE (0x1 << 0)
+#define P2WI_STAT_TRANS_ERR (0x1 << 1)
+#define P2WI_STAT_LOAD_BUSY (0x1 << 2)
+#define __P2WI_STAT_TRANS_ERR(n) (((n) & 0xff) << 8)
+#define P2WI_STAT_TRANS_ERR_MASK __P2WI_STAT_TRANS_ERR_ID(0xff)
+#define __P2WI_STAT_TRANS_ERR_BYTE_1 0x01
+#define __P2WI_STAT_TRANS_ERR_BYTE_2 0x02
+#define __P2WI_STAT_TRANS_ERR_BYTE_3 0x04
+#define __P2WI_STAT_TRANS_ERR_BYTE_4 0x08
+#define __P2WI_STAT_TRANS_ERR_BYTE_5 0x10
+#define __P2WI_STAT_TRANS_ERR_BYTE_6 0x20
+#define __P2WI_STAT_TRANS_ERR_BYTE_7 0x40
+#define __P2WI_STAT_TRANS_ERR_BYTE_8 0x80
+#define P2WI_STAT_TRANS_ERR_BYTE_1 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_1)
+#define P2WI_STAT_TRANS_ERR_BYTE_2 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_2)
+#define P2WI_STAT_TRANS_ERR_BYTE_3 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_3)
+#define P2WI_STAT_TRANS_ERR_BYTE_4 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_4)
+#define P2WI_STAT_TRANS_ERR_BYTE_5 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_5)
+#define P2WI_STAT_TRANS_ERR_BYTE_6 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_6)
+#define P2WI_STAT_TRANS_ERR_BYTE_7 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_7)
+#define P2WI_STAT_TRANS_ERR_BYTE_8 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_8)
+
+#define P2WI_DATADDR_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_1_MASK P2WI_DATADDR_BYTE_1(0xff)
+#define P2WI_DATADDR_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_2_MASK P2WI_DATADDR_BYTE_2(0xff)
+#define P2WI_DATADDR_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_3_MASK P2WI_DATADDR_BYTE_3(0xff)
+#define P2WI_DATADDR_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_4_MASK P2WI_DATADDR_BYTE_4(0xff)
+#define P2WI_DATADDR_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_5_MASK P2WI_DATADDR_BYTE_5(0xff)
+#define P2WI_DATADDR_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_6_MASK P2WI_DATADDR_BYTE_6(0xff)
+#define P2WI_DATADDR_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_7_MASK P2WI_DATADDR_BYTE_7(0xff)
+#define P2WI_DATADDR_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_8_MASK P2WI_DATADDR_BYTE_8(0xff)
+
+#define __P2WI_DATA_NUM_BYTES(n) (((n) & 0x7) << 0)
+#define P2WI_DATA_NUM_BYTES_MASK __P2WI_DATA_NUM_BYTES(0x7)
+#define P2WI_DATA_NUM_BYTES(n) __P2WI_DATA_NUM_BYTES((n) - 1)
+#define P2WI_DATA_NUM_BYTES_READ (0x1 << 4)
+
+#define P2WI_DATA_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_1_MASK P2WI_DATA_BYTE_1(0xff)
+#define P2WI_DATA_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_2_MASK P2WI_DATA_BYTE_2(0xff)
+#define P2WI_DATA_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_3_MASK P2WI_DATA_BYTE_3(0xff)
+#define P2WI_DATA_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_4_MASK P2WI_DATA_BYTE_4(0xff)
+#define P2WI_DATA_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_5_MASK P2WI_DATA_BYTE_5(0xff)
+#define P2WI_DATA_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_6_MASK P2WI_DATA_BYTE_6(0xff)
+#define P2WI_DATA_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_7_MASK P2WI_DATA_BYTE_7(0xff)
+#define P2WI_DATA_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_8_MASK P2WI_DATA_BYTE_8(0xff)
+
+#define P2WI_LINECTRL_SDA_CTRL_EN (0x1 << 0)
+#define P2WI_LINECTRL_SDA_OUT_HIGH (0x1 << 1)
+#define P2WI_LINECTRL_SCL_CTRL_EN (0x1 << 2)
+#define P2WI_LINECTRL_SCL_OUT_HIGH (0x1 << 3)
+#define P2WI_LINECTRL_SDA_STATE_HIGH (0x1 << 4)
+#define P2WI_LINECTRL_SCL_STATE_HIGH (0x1 << 5)
+
+#define P2WI_PM_DEV_ADDR(n) (((n) & 0xff) << 0)
+#define P2WI_PM_DEV_ADDR_MASK P2WI_PM_DEV_ADDR(0xff)
+#define P2WI_PM_CTRL_ADDR(n) (((n) & 0xff) << 8)
+#define P2WI_PM_CTRL_ADDR_MASK P2WI_PM_CTRL_ADDR(0xff)
+#define P2WI_PM_INIT_DATA(n) (((n) & 0xff) << 16)
+#define P2WI_PM_INIT_DATA_MASK P2WI_PM_INIT_DATA(0xff)
+#define P2WI_PM_INIT_SEND (0x1 << 31)
+
+struct sunxi_p2wi_reg {
+       u32 ctrl;       /* 0x00 control */
+       u32 cc;         /* 0x04 clock control */
+       u32 irq;        /* 0x08 interrupt */
+       u32 status;     /* 0x0c status */
+       u32 dataddr0;   /* 0x10 data address 0 */
Quoted 8 lines

arch/arm/include/asm/arch-sunxi/p2wi.h line 133

Quoted 7 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +7 -0
+       u32 dataddr1;   /* 0x14 data address 1 */
+       u32 numbytes;   /* 0x18 num bytes */
+       u32 data0;      /* 0x1c data buffer 0 */
+       u32 data1;      /* 0x20 data buffer 1 */
+       u32 linectrl;   /* 0x24 line control */
+       u32 pm;         /* 0x28 power management */
+};

Is there any reason not to put all of the above in the driver (.c) itself?

no reason, I just followed what was common about a year + ago :) I'm still not sure what the general consensus is about structs for register data to begin with :)

Quoted 11 lines
Quoted 10 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +7 -1
+
+void p2wi_init(void);
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data);
+int p2wi_read(const u8 addr, u8 *data);
+int p2wi_write(const u8 addr, u8 data);
+
+#endif /* _SUNXI_P2WI_H */
--

2.1.0

Thanks for working on this! I haven't had time to mainline the p2wi driver.

Yes! Thanks for picking this up Hans, it feels good to see this being used :D

Quoted 2 lines

ChenYu

#4 lore raw

PATCH 1/9 sun6i: Add new p2wi controller driver

Hi,

Quoted 4 lines · Olliver Schinagl

Hy Wens,

This is some really old work, and I'm assuming hans picked it up, as I never tested the code (didn't have hardware at the time)

I believe Hans did pick it up for u-boot-sunxi, and now we're just mainlining it.

Quoted 6 lines

I'll comment as I remember it and hope that helps.

On a side note, do we know what p2wi stands for? I'm guessing 2wi is allwinners try at combining the names twi and i2c, as i2c is some philips trademark and maybe they think twi is an avr amongst others? so using 2wi is save? And the P, 'program twi? Powermanagment twi?

It stands for Push-pull 2 wire interface.

Quoted 3 lines

If that is whats happening here, I'd be inclined to rename it to to just regular twi, maybe axp_twi if we must?

It's not the same. Normal TWI uses an open-drain setup. Other specifics may be different as well, like request framing. Also P2WI does not have the concept of device addresses.

Quoted 102 lines

Olliver

Quoted 98 lines · Chen-Yu Tsai

Hi,

Quoted 80 lines · Hans de Goede

From: Oliver Schinagl <[email protected]>

The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.

---
  arch/arm/cpu/armv7/sunxi/Makefile      |   1 +
  arch/arm/cpu/armv7/sunxi/p2wi.c        | 117
+++++++++++++++++++++++++++
  arch/arm/include/asm/arch-sunxi/gpio.h |   3 +
  arch/arm/include/asm/arch-sunxi/p2wi.h | 140
+++++++++++++++++++++++++++++++++
  4 files changed, 261 insertions(+)

create mode 100644 arch/arm/cpu/armv7/sunxi/p2wi.c create mode 100644 arch/arm/include/asm/arch-sunxi/p2wi.h

arch/arm/cpu/armv7/sunxi/Makefile +1 -0
index 82dbf76..b3a3601 100644
@@ -13,6 +13,7 @@ obj-y += clock.o
  obj-y  += pinmux.o
  obj-$(CONFIG_MACH_SUN6I)       += prcm.o
  obj-$(CONFIG_MACH_SUN8I)       += prcm.o
+obj-$(CONFIG_MACH_SUN6I)       += p2wi.o
  obj-$(CONFIG_MACH_SUN4I)       += clock_sun4i.o
  obj-$(CONFIG_MACH_SUN5I)       += clock_sun4i.o
  obj-$(CONFIG_MACH_SUN6I)       += clock_sun6i.o
arch/arm/cpu/armv7/sunxi/p2wi.c new file +29 -0
new file mode 100644
index 0000000..10df348
@@ -0,0 +1,117 @@
+/*
+ * Sunxi A31 Power Management Unit
+ *
+ * (C) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * Based on sun6i sources and earlier U-Boot Allwiner A10 SPL work
+ *
+ * (C) Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/p2wi.h>
+#include <asm/arch/prcm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+
+void p2wi_init(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg

*)SUNXI_P2WI_BASE;

arch/arm/cpu/armv7/sunxi/p2wi.c new file +8 -0
+
+       /* Enable p2wi and PIO clk, and de-assert their resets */
+       prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
+
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUNXI_GPL0_R_P2WI_SCK);
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUNXI_GPL1_R_P2WI_SDA);
+
+       /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz

*/

arch/arm/cpu/armv7/sunxi/p2wi.c line 44

Quoted 7 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +7 -0
+       writel(P2WI_CTRL_RESET, &p2wi->ctrl);
+       sdelay(0x100);
+       writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8),
+              &p2wi->cc);
+}
+
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data)

set_pmu_address is actually a misnomer. What it actually does is write <init_data> to <ctrl_reg> on device found at <slave_addr>, probably using I2C, with the sole purpose of putting the device into P2WI mode (or RSB mode for later AXPs).

Quoted 7 lines
Quoted 2 lines

According to the docs/code we have, it is always writing 0x3e to register 0x3e.

Back then, we didn't have no official docs, nor official code ;)

I think this is what boot0 for a31 called it, or something close to it. Do you know what other AXP's take for this <ctrl_reg>?

The AXP806 datasheet says 0x7c, which is 0x3e left shifted by 1 bit. And the RSB driver for U-boot from Allwinner does use 0x7c.

So my guess is 0x3e for P2WI and 0x7c for RSB.

Also note that with RSB, it is still written to 0x3e on the slaves, but to all slaves, using the slave address 0x00.

Quoted 4 lines

What are the chances, that 0x3e (in this chip) does what you say, enable p2wi or RSB mode? If that would be the case, a more generic function would be nice indeed, axp_init_mode(AXP_ADDR, AXP_MODE) or something comes to mind?

Probably better than "set_pmu_address", which it does not do. And for RSB, there's yet another function to set the runtime address.

RSB driver uses "rsb_send_initseq".

Quoted 266 lines
Quoted 226 lines
Quoted 226 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +2 -0
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg

*)SUNXI_P2WI_BASE;

arch/arm/cpu/armv7/sunxi/p2wi.c new file +19 -0
+       unsigned long tmo = timer_get_us() + 1000000;
+
+       writel(P2WI_PM_DEV_ADDR(slave_addr) |
+              P2WI_PM_CTRL_ADDR(ctrl_reg) |
+              P2WI_PM_INIT_DATA(init_data) |
+              P2WI_PM_INIT_SEND,
+              &p2wi->pm);
+
+       while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
+               if (timer_get_us() > tmo)
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int p2wi_await_trans(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg

*)SUNXI_P2WI_BASE;

arch/arm/cpu/armv7/sunxi/p2wi.c new file +26 -0
+       unsigned long tmo = timer_get_us() + 1000000;
+       int ret;
+       u8 reg;
+
+       while (1) {
+               reg = readl(&p2wi->status);
+               if (reg & P2WI_STAT_TRANS_ERR) {
+                       ret = -EIO;
+                       break;
+               }
+               if (reg & P2WI_STAT_TRANS_DONE) {
+                       ret = 0;
+                       break;
+               }
+               if (timer_get_us() > tmo) {
+                       ret = -ETIME;
+                       break;
+               }
+       }
+       writel(reg, &p2wi->status); /* Clear status bits */
+       return ret;
+}
+
+int p2wi_read(const u8 addr, u8 *data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg

*)SUNXI_P2WI_BASE;

arch/arm/cpu/armv7/sunxi/p2wi.c new file +17 -0
+       int ret;
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_NUM_BYTES(1) |
+              P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       ret = p2wi_await_trans();
+
+       *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK;
+       return ret;
+}
+
+int p2wi_write(const u8 addr, u8 data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg

*)SUNXI_P2WI_BASE;

arch/arm/cpu/armv7/sunxi/p2wi.c new file +9 -0
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_BYTE_1(data), &p2wi->data0);
+       writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       return p2wi_await_trans();
+}
arch/arm/include/asm/arch-sunxi/gpio.h +3 -0
index 437dd35..c734cf0 100644
@@ -172,6 +172,9 @@ enum sunxi_gpio_number {
 
  #define SUN4I_GPI4_SDC3                2
 
+#define SUNXI_GPL0_R_P2WI_SCK  3
+#define SUNXI_GPL1_R_P2WI_SDA  3
+
  #define SUN8I_GPL2_R_UART_TX   2
  #define SUN8I_GPL3_R_UART_RX   2
 
arch/arm/include/asm/arch-sunxi/p2wi.h new file +127 -0
new file mode 100644
index 0000000..1788fa4
@@ -0,0 +1,140 @@
+/*
+ * Sunxi platform Push-Push i2c register definition.
+ *
+ * (c) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * (c)Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _SUNXI_P2WI_H
+#define _SUNXI_P2WI_H
+
+#include <linux/types.h>
+
+#define P2WI_CTRL_RESET (0x1 << 0)
+#define P2WI_CTRL_IRQ_EN (0x1 << 1)
+#define P2WI_CTRL_TRANS_ABORT (0x1 << 6)
+#define P2WI_CTRL_TRANS_START (0x1 << 7)
+
+#define __P2WI_CC_CLK(n) (((n) & 0xff) << 0)
+#define P2WI_CC_CLK_MASK __P2WI_CC_CLK_DIV(0xff)
+#define __P2WI_CC_CLK_DIV(n) (((n) >> 1) - 1)
+#define P2WI_CC_CLK_DIV(n) \
+       __P2WI_CC_CLK(__P2WI_CC_CLK_DIV(n))
+#define P2WI_CC_SDA_OUT_DELAY(n) (((n) & 0x7) << 8)
+#define P2WI_CC_SDA_OUT_DELAY_MASK P2WI_CC_SDA_OUT_DELAY(0x7)
+
+#define P2WI_IRQ_TRANS_DONE (0x1 << 0)
+#define P2WI_IRQ_TRANS_ERR (0x1 << 1)
+#define P2WI_IRQ_LOAD_BUSY (0x1 << 2)
+
+#define P2WI_STAT_TRANS_DONE (0x1 << 0)
+#define P2WI_STAT_TRANS_ERR (0x1 << 1)
+#define P2WI_STAT_LOAD_BUSY (0x1 << 2)
+#define __P2WI_STAT_TRANS_ERR(n) (((n) & 0xff) << 8)
+#define P2WI_STAT_TRANS_ERR_MASK __P2WI_STAT_TRANS_ERR_ID(0xff)
+#define __P2WI_STAT_TRANS_ERR_BYTE_1 0x01
+#define __P2WI_STAT_TRANS_ERR_BYTE_2 0x02
+#define __P2WI_STAT_TRANS_ERR_BYTE_3 0x04
+#define __P2WI_STAT_TRANS_ERR_BYTE_4 0x08
+#define __P2WI_STAT_TRANS_ERR_BYTE_5 0x10
+#define __P2WI_STAT_TRANS_ERR_BYTE_6 0x20
+#define __P2WI_STAT_TRANS_ERR_BYTE_7 0x40
+#define __P2WI_STAT_TRANS_ERR_BYTE_8 0x80
+#define P2WI_STAT_TRANS_ERR_BYTE_1 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_1)
+#define P2WI_STAT_TRANS_ERR_BYTE_2 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_2)
+#define P2WI_STAT_TRANS_ERR_BYTE_3 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_3)
+#define P2WI_STAT_TRANS_ERR_BYTE_4 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_4)
+#define P2WI_STAT_TRANS_ERR_BYTE_5 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_5)
+#define P2WI_STAT_TRANS_ERR_BYTE_6 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_6)
+#define P2WI_STAT_TRANS_ERR_BYTE_7 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_7)
+#define P2WI_STAT_TRANS_ERR_BYTE_8 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_8)
+
+#define P2WI_DATADDR_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_1_MASK P2WI_DATADDR_BYTE_1(0xff)
+#define P2WI_DATADDR_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_2_MASK P2WI_DATADDR_BYTE_2(0xff)
+#define P2WI_DATADDR_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_3_MASK P2WI_DATADDR_BYTE_3(0xff)
+#define P2WI_DATADDR_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_4_MASK P2WI_DATADDR_BYTE_4(0xff)
+#define P2WI_DATADDR_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_5_MASK P2WI_DATADDR_BYTE_5(0xff)
+#define P2WI_DATADDR_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_6_MASK P2WI_DATADDR_BYTE_6(0xff)
+#define P2WI_DATADDR_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_7_MASK P2WI_DATADDR_BYTE_7(0xff)
+#define P2WI_DATADDR_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_8_MASK P2WI_DATADDR_BYTE_8(0xff)
+
+#define __P2WI_DATA_NUM_BYTES(n) (((n) & 0x7) << 0)
+#define P2WI_DATA_NUM_BYTES_MASK __P2WI_DATA_NUM_BYTES(0x7)
+#define P2WI_DATA_NUM_BYTES(n) __P2WI_DATA_NUM_BYTES((n) - 1)
+#define P2WI_DATA_NUM_BYTES_READ (0x1 << 4)
+
+#define P2WI_DATA_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_1_MASK P2WI_DATA_BYTE_1(0xff)
+#define P2WI_DATA_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_2_MASK P2WI_DATA_BYTE_2(0xff)
+#define P2WI_DATA_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_3_MASK P2WI_DATA_BYTE_3(0xff)
+#define P2WI_DATA_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_4_MASK P2WI_DATA_BYTE_4(0xff)
+#define P2WI_DATA_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_5_MASK P2WI_DATA_BYTE_5(0xff)
+#define P2WI_DATA_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_6_MASK P2WI_DATA_BYTE_6(0xff)
+#define P2WI_DATA_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_7_MASK P2WI_DATA_BYTE_7(0xff)
+#define P2WI_DATA_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_8_MASK P2WI_DATA_BYTE_8(0xff)
+
+#define P2WI_LINECTRL_SDA_CTRL_EN (0x1 << 0)
+#define P2WI_LINECTRL_SDA_OUT_HIGH (0x1 << 1)
+#define P2WI_LINECTRL_SCL_CTRL_EN (0x1 << 2)
+#define P2WI_LINECTRL_SCL_OUT_HIGH (0x1 << 3)
+#define P2WI_LINECTRL_SDA_STATE_HIGH (0x1 << 4)
+#define P2WI_LINECTRL_SCL_STATE_HIGH (0x1 << 5)
+
+#define P2WI_PM_DEV_ADDR(n) (((n) & 0xff) << 0)
+#define P2WI_PM_DEV_ADDR_MASK P2WI_PM_DEV_ADDR(0xff)
+#define P2WI_PM_CTRL_ADDR(n) (((n) & 0xff) << 8)
+#define P2WI_PM_CTRL_ADDR_MASK P2WI_PM_CTRL_ADDR(0xff)
+#define P2WI_PM_INIT_DATA(n) (((n) & 0xff) << 16)
+#define P2WI_PM_INIT_DATA_MASK P2WI_PM_INIT_DATA(0xff)
+#define P2WI_PM_INIT_SEND (0x1 << 31)
+
+struct sunxi_p2wi_reg {
+       u32 ctrl;       /* 0x00 control */
+       u32 cc;         /* 0x04 clock control */
+       u32 irq;        /* 0x08 interrupt */
+       u32 status;     /* 0x0c status */
+       u32 dataddr0;   /* 0x10 data address 0 */
+       u32 dataddr1;   /* 0x14 data address 1 */
Quoted 8 lines

arch/arm/include/asm/arch-sunxi/p2wi.h line 133

Quoted 6 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +6 -0
+       u32 numbytes;   /* 0x18 num bytes */
+       u32 data0;      /* 0x1c data buffer 0 */
+       u32 data1;      /* 0x20 data buffer 1 */
+       u32 linectrl;   /* 0x24 line control */
+       u32 pm;         /* 0x28 power management */
+};

Is there any reason not to put all of the above in the driver (.c) itself?

no reason, I just followed what was common about a year + ago :) I'm still not sure what the general consensus is about structs for register data to begin with :)

Quoted 14 lines
Quoted 11 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +7 -1
+
+void p2wi_init(void);
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data);
+int p2wi_read(const u8 addr, u8 *data);
+int p2wi_write(const u8 addr, u8 data);
+
+#endif /* _SUNXI_P2WI_H */
--

2.1.0

Thanks for working on this! I haven't had time to mainline the p2wi driver.

Yes! Thanks for picking this up Hans, it feels good to see this being used :D

Quoted 3 lines

ChenYu

--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

#5 lore raw

PATCH 1/9 sun6i: Add new p2wi controller driver

In reply to Chen-Yu Tsai

Hi,

Quoted 89 lines · Chen-Yu Tsai

Hi,

Quoted 70 lines · Hans de Goede

From: Oliver Schinagl <[email protected]>

The A31 uses a new push-pull two wire interface, which features higher transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8 bytes each time, this driver will only see very little use and thus is limited to single byte transmission only.

---
 arch/arm/cpu/armv7/sunxi/Makefile      |   1 +
 arch/arm/cpu/armv7/sunxi/p2wi.c        | 117 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/gpio.h |   3 +
 arch/arm/include/asm/arch-sunxi/p2wi.h | 140 +++++++++++++++++++++++++++++++++
 4 files changed, 261 insertions(+)
 create mode 100644 arch/arm/cpu/armv7/sunxi/p2wi.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/p2wi.h
arch/arm/cpu/armv7/sunxi/Makefile +1 -0
index 82dbf76..b3a3601 100644
@@ -13,6 +13,7 @@ obj-y += clock.o
 obj-y  += pinmux.o
 obj-$(CONFIG_MACH_SUN6I)       += prcm.o
 obj-$(CONFIG_MACH_SUN8I)       += prcm.o
+obj-$(CONFIG_MACH_SUN6I)       += p2wi.o
 obj-$(CONFIG_MACH_SUN4I)       += clock_sun4i.o
 obj-$(CONFIG_MACH_SUN5I)       += clock_sun4i.o
 obj-$(CONFIG_MACH_SUN6I)       += clock_sun6i.o
arch/arm/cpu/armv7/sunxi/p2wi.c new file +34 -0
new file mode 100644
index 0000000..10df348
@@ -0,0 +1,117 @@
+/*
+ * Sunxi A31 Power Management Unit
+ *
+ * (C) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * Based on sun6i sources and earlier U-Boot Allwiner A10 SPL work
+ *
+ * (C) Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/p2wi.h>
+#include <asm/arch/prcm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+
+void p2wi_init(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+
+       /* Enable p2wi and PIO clk, and de-assert their resets */
+       prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
+
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUNXI_GPL0_R_P2WI_SCK);

arch/arm/cpu/armv7/sunxi/p2wi.c line 44

Quoted 10 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +10 -0
+       sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUNXI_GPL1_R_P2WI_SDA);
+
+       /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */
+       writel(P2WI_CTRL_RESET, &p2wi->ctrl);
+       sdelay(0x100);
+       writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8),
+              &p2wi->cc);
+}
+
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data)

set_pmu_address is actually a misnomer. What it actually does is write <init_data> to <ctrl_reg> on device found at <slave_addr>, probably using I2C, with the sole purpose of putting the device into P2WI mode (or RSB mode for later AXPs).

Quoted 2 lines

According to the docs/code we have, it is always writing 0x3e to register 0x3e.

I've renamed this to p2wi_change_to_p2wi_mode for the next version of this patch-set.

Quoted 220 lines
Quoted 220 lines
arch/arm/cpu/armv7/sunxi/p2wi.c new file +73 -0
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       unsigned long tmo = timer_get_us() + 1000000;
+
+       writel(P2WI_PM_DEV_ADDR(slave_addr) |
+              P2WI_PM_CTRL_ADDR(ctrl_reg) |
+              P2WI_PM_INIT_DATA(init_data) |
+              P2WI_PM_INIT_SEND,
+              &p2wi->pm);
+
+       while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
+               if (timer_get_us() > tmo)
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int p2wi_await_trans(void)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       unsigned long tmo = timer_get_us() + 1000000;
+       int ret;
+       u8 reg;
+
+       while (1) {
+               reg = readl(&p2wi->status);
+               if (reg & P2WI_STAT_TRANS_ERR) {
+                       ret = -EIO;
+                       break;
+               }
+               if (reg & P2WI_STAT_TRANS_DONE) {
+                       ret = 0;
+                       break;
+               }
+               if (timer_get_us() > tmo) {
+                       ret = -ETIME;
+                       break;
+               }
+       }
+       writel(reg, &p2wi->status); /* Clear status bits */
+       return ret;
+}
+
+int p2wi_read(const u8 addr, u8 *data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+       int ret;
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_NUM_BYTES(1) |
+              P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       ret = p2wi_await_trans();
+
+       *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK;
+       return ret;
+}
+
+int p2wi_write(const u8 addr, u8 data)
+{
+       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUNXI_P2WI_BASE;
+
+       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
+       writel(P2WI_DATA_BYTE_1(data), &p2wi->data0);
+       writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes);
+       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
+       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
+
+       return p2wi_await_trans();
+}
arch/arm/include/asm/arch-sunxi/gpio.h +3 -0
index 437dd35..c734cf0 100644
@@ -172,6 +172,9 @@ enum sunxi_gpio_number {
 
 #define SUN4I_GPI4_SDC3                2
 
+#define SUNXI_GPL0_R_P2WI_SCK  3
+#define SUNXI_GPL1_R_P2WI_SDA  3
+
 #define SUN8I_GPL2_R_UART_TX   2
 #define SUN8I_GPL3_R_UART_RX   2
 
arch/arm/include/asm/arch-sunxi/p2wi.h new file +127 -0
new file mode 100644
index 0000000..1788fa4
@@ -0,0 +1,140 @@
+/*
+ * Sunxi platform Push-Push i2c register definition.
+ *
+ * (c) Copyright 2013 Oliver Schinagl <[email protected]>
+ * http://linux-sunxi.org
+ *
+ * (c)Copyright 2006-2013
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Berg Xing <[email protected]>
+ * Tom Cubie <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _SUNXI_P2WI_H
+#define _SUNXI_P2WI_H
+
+#include <linux/types.h>
+
+#define P2WI_CTRL_RESET (0x1 << 0)
+#define P2WI_CTRL_IRQ_EN (0x1 << 1)
+#define P2WI_CTRL_TRANS_ABORT (0x1 << 6)
+#define P2WI_CTRL_TRANS_START (0x1 << 7)
+
+#define __P2WI_CC_CLK(n) (((n) & 0xff) << 0)
+#define P2WI_CC_CLK_MASK __P2WI_CC_CLK_DIV(0xff)
+#define __P2WI_CC_CLK_DIV(n) (((n) >> 1) - 1)
+#define P2WI_CC_CLK_DIV(n) \
+       __P2WI_CC_CLK(__P2WI_CC_CLK_DIV(n))
+#define P2WI_CC_SDA_OUT_DELAY(n) (((n) & 0x7) << 8)
+#define P2WI_CC_SDA_OUT_DELAY_MASK P2WI_CC_SDA_OUT_DELAY(0x7)
+
+#define P2WI_IRQ_TRANS_DONE (0x1 << 0)
+#define P2WI_IRQ_TRANS_ERR (0x1 << 1)
+#define P2WI_IRQ_LOAD_BUSY (0x1 << 2)
+
+#define P2WI_STAT_TRANS_DONE (0x1 << 0)
+#define P2WI_STAT_TRANS_ERR (0x1 << 1)
+#define P2WI_STAT_LOAD_BUSY (0x1 << 2)
+#define __P2WI_STAT_TRANS_ERR(n) (((n) & 0xff) << 8)
+#define P2WI_STAT_TRANS_ERR_MASK __P2WI_STAT_TRANS_ERR_ID(0xff)
+#define __P2WI_STAT_TRANS_ERR_BYTE_1 0x01
+#define __P2WI_STAT_TRANS_ERR_BYTE_2 0x02
+#define __P2WI_STAT_TRANS_ERR_BYTE_3 0x04
+#define __P2WI_STAT_TRANS_ERR_BYTE_4 0x08
+#define __P2WI_STAT_TRANS_ERR_BYTE_5 0x10
+#define __P2WI_STAT_TRANS_ERR_BYTE_6 0x20
+#define __P2WI_STAT_TRANS_ERR_BYTE_7 0x40
+#define __P2WI_STAT_TRANS_ERR_BYTE_8 0x80
+#define P2WI_STAT_TRANS_ERR_BYTE_1 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_1)
+#define P2WI_STAT_TRANS_ERR_BYTE_2 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_2)
+#define P2WI_STAT_TRANS_ERR_BYTE_3 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_3)
+#define P2WI_STAT_TRANS_ERR_BYTE_4 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_4)
+#define P2WI_STAT_TRANS_ERR_BYTE_5 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_5)
+#define P2WI_STAT_TRANS_ERR_BYTE_6 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_6)
+#define P2WI_STAT_TRANS_ERR_BYTE_7 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_7)
+#define P2WI_STAT_TRANS_ERR_BYTE_8 \
+       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_8)
+
+#define P2WI_DATADDR_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_1_MASK P2WI_DATADDR_BYTE_1(0xff)
+#define P2WI_DATADDR_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_2_MASK P2WI_DATADDR_BYTE_2(0xff)
+#define P2WI_DATADDR_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_3_MASK P2WI_DATADDR_BYTE_3(0xff)
+#define P2WI_DATADDR_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_4_MASK P2WI_DATADDR_BYTE_4(0xff)
+#define P2WI_DATADDR_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATADDR_BYTE_5_MASK P2WI_DATADDR_BYTE_5(0xff)
+#define P2WI_DATADDR_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATADDR_BYTE_6_MASK P2WI_DATADDR_BYTE_6(0xff)
+#define P2WI_DATADDR_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATADDR_BYTE_7_MASK P2WI_DATADDR_BYTE_7(0xff)
+#define P2WI_DATADDR_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATADDR_BYTE_8_MASK P2WI_DATADDR_BYTE_8(0xff)
+
+#define __P2WI_DATA_NUM_BYTES(n) (((n) & 0x7) << 0)
+#define P2WI_DATA_NUM_BYTES_MASK __P2WI_DATA_NUM_BYTES(0x7)
+#define P2WI_DATA_NUM_BYTES(n) __P2WI_DATA_NUM_BYTES((n) - 1)
+#define P2WI_DATA_NUM_BYTES_READ (0x1 << 4)
+
+#define P2WI_DATA_BYTE_1(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_1_MASK P2WI_DATA_BYTE_1(0xff)
+#define P2WI_DATA_BYTE_2(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_2_MASK P2WI_DATA_BYTE_2(0xff)
+#define P2WI_DATA_BYTE_3(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_3_MASK P2WI_DATA_BYTE_3(0xff)
+#define P2WI_DATA_BYTE_4(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_4_MASK P2WI_DATA_BYTE_4(0xff)
+#define P2WI_DATA_BYTE_5(n) (((n) & 0xff) << 0)
+#define P2WI_DATA_BYTE_5_MASK P2WI_DATA_BYTE_5(0xff)
+#define P2WI_DATA_BYTE_6(n) (((n) & 0xff) << 8)
+#define P2WI_DATA_BYTE_6_MASK P2WI_DATA_BYTE_6(0xff)
+#define P2WI_DATA_BYTE_7(n) (((n) & 0xff) << 16)
+#define P2WI_DATA_BYTE_7_MASK P2WI_DATA_BYTE_7(0xff)
+#define P2WI_DATA_BYTE_8(n) (((n) & 0xff) << 24)
+#define P2WI_DATA_BYTE_8_MASK P2WI_DATA_BYTE_8(0xff)
+
+#define P2WI_LINECTRL_SDA_CTRL_EN (0x1 << 0)
+#define P2WI_LINECTRL_SDA_OUT_HIGH (0x1 << 1)
+#define P2WI_LINECTRL_SCL_CTRL_EN (0x1 << 2)
+#define P2WI_LINECTRL_SCL_OUT_HIGH (0x1 << 3)
+#define P2WI_LINECTRL_SDA_STATE_HIGH (0x1 << 4)
+#define P2WI_LINECTRL_SCL_STATE_HIGH (0x1 << 5)
+
+#define P2WI_PM_DEV_ADDR(n) (((n) & 0xff) << 0)
+#define P2WI_PM_DEV_ADDR_MASK P2WI_PM_DEV_ADDR(0xff)
+#define P2WI_PM_CTRL_ADDR(n) (((n) & 0xff) << 8)
+#define P2WI_PM_CTRL_ADDR_MASK P2WI_PM_CTRL_ADDR(0xff)
+#define P2WI_PM_INIT_DATA(n) (((n) & 0xff) << 16)
+#define P2WI_PM_INIT_DATA_MASK P2WI_PM_INIT_DATA(0xff)
+#define P2WI_PM_INIT_SEND (0x1 << 31)
+
+struct sunxi_p2wi_reg {
+       u32 ctrl;       /* 0x00 control */
+       u32 cc;         /* 0x04 clock control */
+       u32 irq;        /* 0x08 interrupt */
+       u32 status;     /* 0x0c status */
+       u32 dataddr0;   /* 0x10 data address 0 */
+       u32 dataddr1;   /* 0x14 data address 1 */
Quoted 8 lines

arch/arm/include/asm/arch-sunxi/p2wi.h line 133

Quoted 6 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +6 -0
+       u32 numbytes;   /* 0x18 num bytes */
+       u32 data0;      /* 0x1c data buffer 0 */
+       u32 data1;      /* 0x20 data buffer 1 */
+       u32 linectrl;   /* 0x24 line control */
+       u32 pm;         /* 0x28 power management */
+};

Is there any reason not to put all of the above in the driver (.c) itself?

Not really, this is what all the axpXXX drivers also do, and I prefer it this way as it keeps the .c file a lot more readable.

Quoted 11 lines
Quoted 9 lines
arch/arm/include/asm/arch-sunxi/p2wi.h new file +7 -1
+
+void p2wi_init(void);
+int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data);
+int p2wi_read(const u8 addr, u8 *data);
+int p2wi_write(const u8 addr, u8 data);
+
+#endif /* _SUNXI_P2WI_H */
--

2.1.0

Thanks for working on this! I haven't had time to mainline the p2wi driver.

You're welcome.

Regards,

Hans

#6 lore raw

PATCH 2/9 sun6i: Add basic axp221 driver

In reply to Hans de Goede

From: Oliver Schinagl <[email protected]>

The A31 uses the AXP221 pmic for various voltages.

---
 board/sunxi/board.c        |  14 ++++++
 configs/Colombus_defconfig |   1 +
 drivers/power/Makefile     |   1 +
 drivers/power/axp221.c     | 103 +++++++++++++++++++++++++++++++++++++++++++++
 include/axp221.h           |  30 +++++++++++++
 5 files changed, 149 insertions(+)
 create mode 100644 drivers/power/axp221.c
 create mode 100644 include/axp221.h
board/sunxi/board.c +14 -0
index 8d5d03e..75b29b0 100644
@@ -19,6 +19,9 @@
 #ifdef CONFIG_AXP209_POWER
 #include <axp209.h>
 #endif
+#ifdef CONFIG_AXP221_POWER
+#include <axp221.h>
+#endif
 #include <asm/arch/clock.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/dram.h>
@@ -173,6 +176,17 @@ void sunxi_board_init(void)
 	power_failed |= axp209_set_ldo3(2800);
 	power_failed |= axp209_set_ldo4(2800);
 #endif
+#ifdef CONFIG_AXP221_POWER
+	power_failed = axp221_init();
+	power_failed |= axp221_set_dcdc1(3000);
+	power_failed |= axp221_set_dcdc2(1200);
+	power_failed |= axp221_set_dcdc3(1200);
+	power_failed |= axp221_set_dcdc4(1200);
+	power_failed |= axp221_set_dcdc5(1500);
+#ifdef CONFIG_ENABLE_DLDO1_POWER
+	power_failed |= axp221_set_dldo1(3300);
+#endif
+#endif
 
 	printf("DRAM:");
 	ramsize = sunxi_dram_init();
configs/Colombus_defconfig +1 -0
index 8ba0029..06fb642 100644
@@ -1,3 +1,4 @@
+CONFIG_SYS_EXTRA_OPTIONS="AXP221_POWER"
 CONFIG_ARM=y
 CONFIG_ARCH_SUNXI=y
 CONFIG_MACH_SUN6I=y
drivers/power/Makefile +1 -0
index dc64e4d..04bd996 100644
@@ -7,6 +7,7 @@
 
 obj-$(CONFIG_AXP152_POWER)	+= axp152.o
 obj-$(CONFIG_AXP209_POWER)	+= axp209.o
+obj-$(CONFIG_AXP221_POWER)	+= axp221.o
 obj-$(CONFIG_EXYNOS_TMU)	+= exynos-tmu.o
 obj-$(CONFIG_FTPMU010_POWER)	+= ftpmu010.o
 obj-$(CONFIG_TPS6586X_POWER)	+= tps6586x.o
drivers/power/axp221.c new file +103 -0

103 lines not on this page · raw message

include/axp221.h new file +30 -0
new file mode 100644
index 0000000..c3a6cf4
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 2013 Oliver Schinagl <[email protected]>
+ *
+ * X-Powers AXP221 Power Management IC driver
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#define AXP221_CHIP_ADDR 0x68
+#define AXP221_CTRL_ADDR 0x3e
+#define AXP221_INIT_DATA 0x3e
+
+#define AXP221_CHIP_ID		0x03
+#define AXP221_OUTPUT_CTRL1	0x10
+#define AXP221_OUTPUT_CTRL2	0x12
+#define AXP221_OUTPUT_CTRL3	0x13
+#define AXP221_DLDO1_CTRL	0x15
+#define AXP221_DCDC1_CTRL	0x21
+#define AXP221_DCDC2_CTRL	0x22
+#define AXP221_DCDC3_CTRL	0x23
+#define AXP221_DCDC4_CTRL	0x24
+#define AXP221_DCDC5_CTRL	0x25
+
+int axp221_set_dcdc1(unsigned int mvolt);
+int axp221_set_dcdc2(unsigned int mvolt);
+int axp221_set_dcdc3(unsigned int mvolt);
+int axp221_set_dcdc4(unsigned int mvolt);
+int axp221_set_dcdc5(unsigned int mvolt);
+int axp221_set_dldo1(unsigned int mvolt);
+int axp221_init(void);
Signature · 1 line
2.1.0
#7 lore raw

PATCH 3/9 sun6i: Add clock functions needed for SPL / DRAM init

In reply to Hans de Goede

Add clock_init_safe and clockset_pll5 functions, as these are needed for SPL support resp. DRAM init (which is needed for SPL too).

Also add some extra clock register constant defines.

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

PATCH 4/9 sun6i: Add cpucfg register definitions

In reply to Hans de Goede

Not used atm, for future use (e.g. PSCI).

---
 arch/arm/include/asm/arch-sunxi/cpu.h    |  5 +++
 arch/arm/include/asm/arch-sunxi/cpucfg.h | 67 ++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-sunxi/cpucfg.h
arch/arm/include/asm/arch-sunxi/cpu.h +5 -0
index 0de79a0..6258cce 100644
@@ -70,7 +70,9 @@
 
 #define SUNXI_TP_BASE			0x01c25000
 #define SUNXI_PMU_BASE			0x01c25400
+#ifndef CONFIG_MACH_SUN6I
 #define SUNXI_CPUCFG_BASE              0x01c25c00
+#endif
 
 #define SUNXI_UART0_BASE		0x01c28000
 #define SUNXI_UART1_BASE		0x01c28400
@@ -111,6 +113,9 @@
 #define SUNXI_AVG_BASE			0x01ea0000
 
 #define SUNXI_PRCM_BASE			0x01f01400
+#ifdef CONFIG_MACH_SUN6I
+#define SUNXI_CPUCFG_BASE		0x01f01c00
+#endif
 #define SUNXI_R_UART_BASE		0x01f02800
 #define SUNXI_R_PIO_BASE		0x01f02c00
 #define SUNXI_P2WI_BASE			0x01f03400
arch/arm/include/asm/arch-sunxi/cpucfg.h new file +67 -0

67 lines not on this page · raw message

Signature · 1 line
2.1.0
#9 lore raw

PATCH 4/9 sun6i: Add cpucfg register definitions

Hi,

Quoted 14 lines · Hans de Goede

Not used atm, for future use (e.g. PSCI).

---
 arch/arm/include/asm/arch-sunxi/cpu.h    |  5 +++
 arch/arm/include/asm/arch-sunxi/cpucfg.h | 67 ++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-sunxi/cpucfg.h
arch/arm/include/asm/arch-sunxi/cpu.h +0 -0
index 0de79a0..6258cce 100644

arch/arm/include/asm/arch-sunxi/cpu.h line 75

Quoted 6 lines
arch/arm/include/asm/arch-sunxi/cpu.h +2 -0
@@ -70,7 +70,9 @@
 
 #define SUNXI_TP_BASE                  0x01c25000
 #define SUNXI_PMU_BASE                 0x01c25400
+#ifndef CONFIG_MACH_SUN6I
 #define SUNXI_CPUCFG_BASE              0x01c25c00
+#endif

Could we rename it SUN7I_CPUCFG_BASE? Since it is only used on sun7i.

arch/arm/include/asm/arch-sunxi/cpu.h line 118

Quoted 10 lines
arch/arm/include/asm/arch-sunxi/cpu.h +3 -0
 #define SUNXI_UART0_BASE               0x01c28000
 #define SUNXI_UART1_BASE               0x01c28400
@@ -111,6 +113,9 @@
 #define SUNXI_AVG_BASE                 0x01ea0000
 
 #define SUNXI_PRCM_BASE                        0x01f01400
+#ifdef CONFIG_MACH_SUN6I
+#define SUNXI_CPUCFG_BASE              0x01f01c00
+#endif

And this would be SUN6I_CPUCFG_BASE.

arch/arm/include/asm/arch-sunxi/cpucfg.h

Quoted 8 lines
arch/arm/include/asm/arch-sunxi/cpu.h +0 -0
 #define SUNXI_R_UART_BASE              0x01f02800
 #define SUNXI_R_PIO_BASE               0x01f02c00
 #define SUNXI_P2WI_BASE                        0x01f03400
arch/arm/include/asm/arch-sunxi/cpucfg.h new file +0 -0
new file mode 100644
index 0000000..e2a29cb

sun6i_cpucfg.h?

This might be better, considering the registers are different for each different SoC.

Cheers
ChenYu

Quoted 75 lines
arch/arm/include/asm/arch-sunxi/cpucfg.h new file +67 -1
@@ -0,0 +1,67 @@
+/*
+ * Sunxi A31 CPUCFG register definition.
+ *
+ * (C) Copyright 2014 Hans de Goede <[email protected]
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _SUNXI_CPUCFG_H
+#define _SUNXI_CPUCFG_H
+
+#ifndef __ASSEMBLY__
+
+struct sunxi_cpucfg_reg {
+       u8 res0[0x40];          /* 0x000 */
+       u32 cpu0_rst;           /* 0x040 */
+       u32 cpu0_ctrl;          /* 0x044 */
+       u32 cpu0_status;        /* 0x048 */
+       u8 res1[0x34];          /* 0x04c */
+       u32 cpu1_rst;           /* 0x080 */
+       u32 cpu1_ctrl;          /* 0x084 */
+       u32 cpu1_status;        /* 0x088 */
+       u8 res2[0x34];          /* 0x08c */
+       u32 cpu2_rst;           /* 0x0c0 */
+       u32 cpu2_ctrl;          /* 0x0c4 */
+       u32 cpu2_status;        /* 0x0c8 */
+       u8 res3[0x34];          /* 0x0cc */
+       u32 cpu3_rst;           /* 0x100 */
+       u32 cpu3_ctrl;          /* 0x104 */
+       u32 cpu3_status;        /* 0x108 */
+       u8 res4[0x78];          /* 0x10c */
+       u32 gen_ctrl;           /* 0x184 */
+       u32 l2_status;          /* 0x188 */
+       u8 res5[0x4];           /* 0x18c */
+       u32 event_in;           /* 0x190 */
+       u8 res6[0xc];           /* 0x194 */
+       u32 super_standy_flag;  /* 0x1a0 */
+       u32 priv0;              /* 0x1a4 */
+       u32 priv1;              /* 0x1a8 */
+       u8 res7[0x54];          /* 0x1ac */
+       u32 idle_cnt0_low;      /* 0x200 */
+       u32 idle_cnt0_high;     /* 0x204 */
+       u32 idle_cnt0_ctrl;     /* 0x208 */
+       u8 res8[0x4];           /* 0x20c */
+       u32 idle_cnt1_low;      /* 0x210 */
+       u32 idle_cnt1_high;     /* 0x214 */
+       u32 idle_cnt1_ctrl;     /* 0x218 */
+       u8 res9[0x4];           /* 0x21c */
+       u32 idle_cnt2_low;      /* 0x220 */
+       u32 idle_cnt2_high;     /* 0x224 */
+       u32 idle_cnt2_ctrl;     /* 0x228 */
+       u8 res10[0x4];          /* 0x22c */
+       u32 idle_cnt3_low;      /* 0x230 */
+       u32 idle_cnt3_high;     /* 0x234 */
+       u32 idle_cnt3_ctrl;     /* 0x238 */
+       u8 res11[0x4];          /* 0x23c */
+       u32 idle_cnt4_low;      /* 0x240 */
+       u32 idle_cnt4_high;     /* 0x244 */
+       u32 idle_cnt4_ctrl;     /* 0x248 */
+       u8 res12[0x34];         /* 0x24c */
+       u32 cnt64_ctrl;         /* 0x280 */
+       u32 cnt64_low;          /* 0x284 */
+       u32 cnt64_high;         /* 0x288 */
+};
+
+#endif /* __ASSEMBLY__ */
+#endif /* _SUNXI_CPUCFG_H */
--

2.1.0

--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com. For more options, visit https://groups.google.com/d/optout.

#10 lore raw

PATCH 4/9 sun6i: Add cpucfg register definitions

Hi,

Quoted 32 lines · Chen-Yu Tsai

Hi,

Quoted 14 lines · Hans de Goede

Not used atm, for future use (e.g. PSCI).

---
 arch/arm/include/asm/arch-sunxi/cpu.h    |  5 +++
 arch/arm/include/asm/arch-sunxi/cpucfg.h | 67 ++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-sunxi/cpucfg.h
arch/arm/include/asm/arch-sunxi/cpu.h +0 -0
index 0de79a0..6258cce 100644

arch/arm/include/asm/arch-sunxi/cpu.h line 75

Quoted 6 lines
arch/arm/include/asm/arch-sunxi/cpu.h +2 -0
@@ -70,7 +70,9 @@
 
 #define SUNXI_TP_BASE                  0x01c25000
 #define SUNXI_PMU_BASE                 0x01c25400
+#ifndef CONFIG_MACH_SUN6I
 #define SUNXI_CPUCFG_BASE              0x01c25c00
+#endif

Could we rename it SUN7I_CPUCFG_BASE? Since it is only used on sun7i.

Quoted 6 lines
arch/arm/include/asm/arch-sunxi/cpu.h +0 -0
 #define SUNXI_UART0_BASE               0x01c28000
 #define SUNXI_UART1_BASE               0x01c28400
@@ -111,6 +113,9 @@
 #define SUNXI_AVG_BASE                 0x01ea0000
 
Quoted 6 lines

arch/arm/include/asm/arch-sunxi/cpu.h line 118

Quoted 4 lines
arch/arm/include/asm/arch-sunxi/cpu.h +3 -0
 #define SUNXI_PRCM_BASE                        0x01f01400
+#ifdef CONFIG_MACH_SUN6I
+#define SUNXI_CPUCFG_BASE              0x01f01c00
+#endif

And this would be SUN6I_CPUCFG_BASE.

Yes, good idea, done.

Quoted 11 lines

arch/arm/include/asm/arch-sunxi/cpucfg.h

Quoted 9 lines
arch/arm/include/asm/arch-sunxi/cpu.h +0 -0
 #define SUNXI_R_UART_BASE              0x01f02800
 #define SUNXI_R_PIO_BASE               0x01f02c00
 #define SUNXI_P2WI_BASE                        0x01f03400
arch/arm/include/asm/arch-sunxi/cpucfg.h new file +0 -0
new file mode 100644
index 0000000..e2a29cb

sun6i_cpucfg.h?

cpucfg_sun6i.h to be consistent with dram_sun#i.h and clock_sun#i.h, otherwise a good idea, done.

Quoted 6 lines

This might be better, considering the registers are different for each different SoC.

Cheers
ChenYu

Regards,

Hans

Quoted 76 lines
Quoted 76 lines
arch/arm/include/asm/arch-sunxi/cpucfg.h new file +67 -1
@@ -0,0 +1,67 @@
+/*
+ * Sunxi A31 CPUCFG register definition.
+ *
+ * (C) Copyright 2014 Hans de Goede <[email protected]
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _SUNXI_CPUCFG_H
+#define _SUNXI_CPUCFG_H
+
+#ifndef __ASSEMBLY__
+
+struct sunxi_cpucfg_reg {
+       u8 res0[0x40];          /* 0x000 */
+       u32 cpu0_rst;           /* 0x040 */
+       u32 cpu0_ctrl;          /* 0x044 */
+       u32 cpu0_status;        /* 0x048 */
+       u8 res1[0x34];          /* 0x04c */
+       u32 cpu1_rst;           /* 0x080 */
+       u32 cpu1_ctrl;          /* 0x084 */
+       u32 cpu1_status;        /* 0x088 */
+       u8 res2[0x34];          /* 0x08c */
+       u32 cpu2_rst;           /* 0x0c0 */
+       u32 cpu2_ctrl;          /* 0x0c4 */
+       u32 cpu2_status;        /* 0x0c8 */
+       u8 res3[0x34];          /* 0x0cc */
+       u32 cpu3_rst;           /* 0x100 */
+       u32 cpu3_ctrl;          /* 0x104 */
+       u32 cpu3_status;        /* 0x108 */
+       u8 res4[0x78];          /* 0x10c */
+       u32 gen_ctrl;           /* 0x184 */
+       u32 l2_status;          /* 0x188 */
+       u8 res5[0x4];           /* 0x18c */
+       u32 event_in;           /* 0x190 */
+       u8 res6[0xc];           /* 0x194 */
+       u32 super_standy_flag;  /* 0x1a0 */
+       u32 priv0;              /* 0x1a4 */
+       u32 priv1;              /* 0x1a8 */
+       u8 res7[0x54];          /* 0x1ac */
+       u32 idle_cnt0_low;      /* 0x200 */
+       u32 idle_cnt0_high;     /* 0x204 */
+       u32 idle_cnt0_ctrl;     /* 0x208 */
+       u8 res8[0x4];           /* 0x20c */
+       u32 idle_cnt1_low;      /* 0x210 */
+       u32 idle_cnt1_high;     /* 0x214 */
+       u32 idle_cnt1_ctrl;     /* 0x218 */
+       u8 res9[0x4];           /* 0x21c */
+       u32 idle_cnt2_low;      /* 0x220 */
+       u32 idle_cnt2_high;     /* 0x224 */
+       u32 idle_cnt2_ctrl;     /* 0x228 */
+       u8 res10[0x4];          /* 0x22c */
+       u32 idle_cnt3_low;      /* 0x230 */
+       u32 idle_cnt3_high;     /* 0x234 */
+       u32 idle_cnt3_ctrl;     /* 0x238 */
+       u8 res11[0x4];          /* 0x23c */
+       u32 idle_cnt4_low;      /* 0x240 */
+       u32 idle_cnt4_high;     /* 0x244 */
+       u32 idle_cnt4_ctrl;     /* 0x248 */
+       u8 res12[0x34];         /* 0x24c */
+       u32 cnt64_ctrl;         /* 0x280 */
+       u32 cnt64_low;          /* 0x284 */
+       u32 cnt64_high;         /* 0x288 */
+};
+
+#endif /* __ASSEMBLY__ */
+#endif /* _SUNXI_CPUCFG_H */
--

2.1.0

--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com. For more options, visit https://groups.google.com/d/optout.

#11 lore raw

PATCH 5/9 sun4i: Rename dram files to dram_sun4i.x

In reply to Hans de Goede

In preparation for adding sun6i dram support.

---
 arch/arm/cpu/armv7/sunxi/Makefile            |   6 +-
 arch/arm/cpu/armv7/sunxi/dram.c              | 750 ---------------------------
 arch/arm/cpu/armv7/sunxi/dram_sun4i.c        | 750 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/dram.h       | 171 +-----
 arch/arm/include/asm/arch-sunxi/dram_sun4i.h | 182 +++++++
 5 files changed, 941 insertions(+), 918 deletions(-)
 delete mode 100644 arch/arm/cpu/armv7/sunxi/dram.c
 create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun4i.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun4i.h
arch/arm/cpu/armv7/sunxi/Makefile +3 -3
index b3a3601..48cca0b 100644
@@ -28,9 +28,9 @@ endif
 endif
 
 ifdef CONFIG_SPL_BUILD
-obj-$(CONFIG_MACH_SUN4I)	+= dram.o
-obj-$(CONFIG_MACH_SUN5I)	+= dram.o
-obj-$(CONFIG_MACH_SUN7I)	+= dram.o
+obj-$(CONFIG_MACH_SUN4I)	+= dram_sun4i.o
+obj-$(CONFIG_MACH_SUN5I)	+= dram_sun4i.o
+obj-$(CONFIG_MACH_SUN7I)	+= dram_sun4i.o
 ifdef CONFIG_SPL_FEL
 obj-y	+= start.o
 endif
arch/arm/cpu/armv7/sunxi/dram.c deleted +0 -750

750 lines not on this page · raw message

arch/arm/cpu/armv7/sunxi/dram_sun4i.c new file +750 -0

750 lines not on this page · raw message

arch/arm/include/asm/arch-sunxi/dram.h +6 -165

178 lines not on this page · raw message

arch/arm/include/asm/arch-sunxi/dram_sun4i.h new file +182 -0

182 lines not on this page · raw message

Signature · 1 line
2.1.0
#12 lore raw

PATCH 5/9 sun4i: Rename dram files to dram_sun4i.x

Quoted 13 lines · Hans de Goede

In preparation for adding sun6i dram support.

---
 arch/arm/cpu/armv7/sunxi/Makefile            |   6 +-
 arch/arm/cpu/armv7/sunxi/dram.c              | 750 ---------------------------
 arch/arm/cpu/armv7/sunxi/dram_sun4i.c        | 750 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/dram.h       | 171 +-----
 arch/arm/include/asm/arch-sunxi/dram_sun4i.h | 182 +++++++
 5 files changed, 941 insertions(+), 918 deletions(-)
 delete mode 100644 arch/arm/cpu/armv7/sunxi/dram.c
 create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun4i.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun4i.h

Probably be better to run "git format-patch" with "-M" so it doesn't generate a whole diff. Though it doesn't make much of a difference since you'll be the one committing and pushing, it does make the mail shorter and easier to read. :)

ChenYu

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

2.1.0

--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com. For more options, visit https://groups.google.com/d/optout.

#13 lore raw

PATCH 5/9 sun4i: Rename dram files to dram_sun4i.x

In reply to Hans de Goede

Hi Hans,

Quoted 179 lines · Hans de Goede

In preparation for adding sun6i dram support.

---
 arch/arm/cpu/armv7/sunxi/Makefile            |   6 +-
 arch/arm/cpu/armv7/sunxi/dram.c              | 750 ---------------------------
 arch/arm/cpu/armv7/sunxi/dram_sun4i.c        | 750 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/dram.h       | 171 +-----
 arch/arm/include/asm/arch-sunxi/dram_sun4i.h | 182 +++++++
 5 files changed, 941 insertions(+), 918 deletions(-)
 delete mode 100644 arch/arm/cpu/armv7/sunxi/dram.c
 create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun4i.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun4i.h
arch/arm/include/asm/arch-sunxi/dram.h +0 -157
index 1945f75..9072e68 100644
@@ -14,172 +14,13 @@
 
 #include <linux/types.h>
 
-struct sunxi_dram_reg {
-       u32 ccr;                /* 0x00 controller configuration register */
-       u32 dcr;                /* 0x04 dram configuration register */
-       u32 iocr;               /* 0x08 i/o configuration register */
-       u32 csr;                /* 0x0c controller status register */
-       u32 drr;                /* 0x10 dram refresh register */
-       u32 tpr0;               /* 0x14 dram timing parameters register 0 */
-       u32 tpr1;               /* 0x18 dram timing parameters register 1 */
-       u32 tpr2;               /* 0x1c dram timing parameters register 2 */
-       u32 gdllcr;             /* 0x20 global dll control register */
-       u8 res0[0x28];
-       u32 rslr0;              /* 0x4c rank system latency register */
-       u32 rslr1;              /* 0x50 rank system latency register */
-       u8 res1[0x8];
-       u32 rdgr0;              /* 0x5c rank dqs gating register */
-       u32 rdgr1;              /* 0x60 rank dqs gating register */
-       u8 res2[0x34];
-       u32 odtcr;              /* 0x98 odt configuration register */
-       u32 dtr0;               /* 0x9c data training register 0 */
-       u32 dtr1;               /* 0xa0 data training register 1 */
-       u32 dtar;               /* 0xa4 data training address register */
-       u32 zqcr0;              /* 0xa8 zq control register 0 */
-       u32 zqcr1;              /* 0xac zq control register 1 */
-       u32 zqsr;               /* 0xb0 zq status register */
-       u32 idcr;               /* 0xb4 initializaton delay configure reg */
-       u8 res3[0x138];
-       u32 mr;                 /* 0x1f0 mode register */
-       u32 emr;                /* 0x1f4 extended mode register */
-       u32 emr2;               /* 0x1f8 extended mode register */
-       u32 emr3;               /* 0x1fc extended mode register */
-       u32 dllctr;             /* 0x200 dll control register */
-       u32 dllcr[5];           /* 0x204 dll control register 0(byte 0) */
-       /* 0x208 dll control register 1(byte 1) */
-       /* 0x20c dll control register 2(byte 2) */
-       /* 0x210 dll control register 3(byte 3) */
-       /* 0x214 dll control register 4(byte 4) */
-       u32 dqtr0;              /* 0x218 dq timing register */
-       u32 dqtr1;              /* 0x21c dq timing register */
-       u32 dqtr2;              /* 0x220 dq timing register */
-       u32 dqtr3;              /* 0x224 dq timing register */
-       u32 dqstr;              /* 0x228 dqs timing register */
-       u32 dqsbtr;             /* 0x22c dqsb timing register */
-       u32 mcr;                /* 0x230 mode configure register */
-       u8 res[0x8];
-       u32 ppwrsctl;           /* 0x23c pad power save control */
-       u32 apr;                /* 0x240 arbiter period register */
-       u32 pldtr;              /* 0x244 priority level data threshold reg */
-       u8 res5[0x8];
-       u32 hpcr[32];           /* 0x250 host port configure register */
-       u8 res6[0x10];
-       u32 csel;               /* 0x2e0 controller select register */
-};
-
-struct dram_para {
-       u32 clock;
-       u32 mbus_clock;
-       u32 type;
-       u32 rank_num;
-       u32 density;
-       u32 io_width;
-       u32 bus_width;
-       u32 cas;
-       u32 zq;
-       u32 odt_en;
-       u32 size;
-       u32 tpr0;
-       u32 tpr1;
-       u32 tpr2;
-       u32 tpr3;
-       u32 tpr4;
-       u32 tpr5;
-       u32 emr1;
-       u32 emr2;
-       u32 emr3;
-       u32 dqs_gating_delay;
-       u32 active_windowing;
-};
-
-#define DRAM_CCR_COMMAND_RATE_1T (0x1 << 5)
-#define DRAM_CCR_DQS_GATE (0x1 << 14)
-#define DRAM_CCR_DQS_DRIFT_COMP (0x1 << 17)
-#define DRAM_CCR_ITM_OFF (0x1 << 28)
-#define DRAM_CCR_DATA_TRAINING (0x1 << 30)
-#define DRAM_CCR_INIT (0x1 << 31)
-
-#define DRAM_MEMORY_TYPE_DDR1 1
-#define DRAM_MEMORY_TYPE_DDR2 2
-#define DRAM_MEMORY_TYPE_DDR3 3
-#define DRAM_MEMORY_TYPE_LPDDR2 4
-#define DRAM_MEMORY_TYPE_LPDDR 5
-#define DRAM_DCR_TYPE (0x1 << 0)
-#define DRAM_DCR_TYPE_DDR2 0x0
-#define DRAM_DCR_TYPE_DDR3 0x1
-#define DRAM_DCR_IO_WIDTH(n) (((n) & 0x3) << 1)
-#define DRAM_DCR_IO_WIDTH_MASK DRAM_DCR_IO_WIDTH(0x3)
-#define DRAM_DCR_IO_WIDTH_8BIT 0x0
-#define DRAM_DCR_IO_WIDTH_16BIT 0x1
-#define DRAM_DCR_CHIP_DENSITY(n) (((n) & 0x7) << 3)
-#define DRAM_DCR_CHIP_DENSITY_MASK DRAM_DCR_CHIP_DENSITY(0x7)
-#define DRAM_DCR_CHIP_DENSITY_256M 0x0
-#define DRAM_DCR_CHIP_DENSITY_512M 0x1
-#define DRAM_DCR_CHIP_DENSITY_1024M 0x2
-#define DRAM_DCR_CHIP_DENSITY_2048M 0x3
-#define DRAM_DCR_CHIP_DENSITY_4096M 0x4
-#define DRAM_DCR_CHIP_DENSITY_8192M 0x5
-#define DRAM_DCR_BUS_WIDTH(n) (((n) & 0x7) << 6)
-#define DRAM_DCR_BUS_WIDTH_MASK DRAM_DCR_BUS_WIDTH(0x7)
-#define DRAM_DCR_BUS_WIDTH_32BIT 0x3
-#define DRAM_DCR_BUS_WIDTH_16BIT 0x1
-#define DRAM_DCR_BUS_WIDTH_8BIT 0x0
-#define DRAM_DCR_RANK_SEL(n) (((n) & 0x3) << 10)
-#define DRAM_DCR_RANK_SEL_MASK DRAM_DCR_CMD_RANK(0x3)
-#define DRAM_DCR_CMD_RANK_ALL (0x1 << 12)
-#define DRAM_DCR_MODE(n) (((n) & 0x3) << 13)
-#define DRAM_DCR_MODE_MASK DRAM_DCR_MODE(0x3)
-#define DRAM_DCR_MODE_SEQ 0x0
-#define DRAM_DCR_MODE_INTERLEAVE 0x1
-
-#define DRAM_CSR_DTERR  (0x1 << 20)
-#define DRAM_CSR_DTIERR (0x1 << 21)
-#define DRAM_CSR_FAILED (DRAM_CSR_DTERR | DRAM_CSR_DTIERR)
-
-#define DRAM_DRR_TRFC(n) ((n) & 0xff)
-#define DRAM_DRR_TREFI(n) (((n) & 0xffff) << 8)
-#define DRAM_DRR_BURST(n) ((((n) - 1) & 0xf) << 24)
-
-#define DRAM_MCR_MODE_NORM(n) (((n) & 0x3) << 0)
-#define DRAM_MCR_MODE_NORM_MASK DRAM_MCR_MOD_NORM(0x3)
-#define DRAM_MCR_MODE_DQ_OUT(n) (((n) & 0x3) << 2)
-#define DRAM_MCR_MODE_DQ_OUT_MASK DRAM_MCR_MODE_DQ_OUT(0x3)
-#define DRAM_MCR_MODE_ADDR_OUT(n) (((n) & 0x3) << 4)
-#define DRAM_MCR_MODE_ADDR_OUT_MASK DRAM_MCR_MODE_ADDR_OUT(0x3)
-#define DRAM_MCR_MODE_DQ_IN_OUT(n) (((n) & 0x3) << 6)
-#define DRAM_MCR_MODE_DQ_IN_OUT_MASK DRAM_MCR_MODE_DQ_IN_OUT(0x3)
-#define DRAM_MCR_MODE_DQ_TURNON_DELAY(n) (((n) & 0x7) << 8)
-#define DRAM_MCR_MODE_DQ_TURNON_DELAY_MASK DRAM_MCR_MODE_DQ_TURNON_DELAY(0x7)
-#define DRAM_MCR_MODE_ADDR_IN (0x1 << 11)
-#define DRAM_MCR_RESET (0x1 << 12)
-#define DRAM_MCR_MODE_EN(n) (((n) & 0x3) << 13)
-#define DRAM_MCR_MODE_EN_MASK DRAM_MCR_MOD_EN(0x3)
-#define DRAM_MCR_DCLK_OUT (0x1 << 16)
-
-#define DRAM_DLLCR_NRESET (0x1 << 30)
-#define DRAM_DLLCR_DISABLE (0x1 << 31)
-
-#define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20)
-#define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff)
-#define DRAM_ZQCR0_ZCAL (1 << 31) /* Starts ZQ calibration when set to 1 */
-#define DRAM_ZQCR0_ZDEN (1 << 28) /* Uses ZDATA instead of doing calibration */
-
-#define DRAM_ZQSR_ZDONE (1 << 31) /* ZQ calibration completion flag */
-
-#define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0)
-#define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3)
-
-#define DRAM_MR_BURST_LENGTH(n) (((n) & 0x7) << 0)
-#define DRAM_MR_BURST_LENGTH_MASK DRAM_MR_BURST_LENGTH(0x7)

arch/arm/include/asm/arch-sunxi/dram.h line 19

Quoted 10 lines
arch/arm/include/asm/arch-sunxi/dram.h +3 -7
-#define DRAM_MR_CAS_LAT(n) (((n) & 0x7) << 4)
-#define DRAM_MR_CAS_LAT_MASK DRAM_MR_CAS_LAT(0x7)
-#define DRAM_MR_WRITE_RECOVERY(n) (((n) & 0x7) << 9)
-#define DRAM_MR_WRITE_RECOVERY_MASK DRAM_MR_WRITE_RECOVERY(0x7)
-#define DRAM_MR_POWER_DOWN (0x1 << 12)
-
-#define DRAM_CSEL_MAGIC 0x16237495
+/* dram regs definition */
+#if defined(CONFIG_MACH_SUN6I)
+#include <asm/arch/dram_sun6i.h>

I'm sure CONFIG_MACH_SUN6I isn't set yet, but doesn't this file not exist?

Quoted 8 lines
arch/arm/include/asm/arch-sunxi/dram.h +3 -1
+#else
+#include <asm/arch/dram_sun4i.h>
+#endif
 
 unsigned long sunxi_dram_init(void);
-unsigned long dramc_init(struct dram_para *para);
 
 #endif /* _SUNXI_DRAM_H */
Signature · 4 lines
Julian Calaby

Email: julian.calaby at gmail.com
Profile: http://www.google.com/profiles/julian.calaby/
#14 lore raw

PATCH 5/9 sun4i: Rename dram files to dram_sun4i.x

Hi,

Quoted 186 lines · Julian Calaby

Hi Hans,

Quoted 183 lines · Hans de Goede

In preparation for adding sun6i dram support.

---
 arch/arm/cpu/armv7/sunxi/Makefile            |   6 +-
 arch/arm/cpu/armv7/sunxi/dram.c              | 750 ---------------------------
 arch/arm/cpu/armv7/sunxi/dram_sun4i.c        | 750 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/dram.h       | 171 +-----
 arch/arm/include/asm/arch-sunxi/dram_sun4i.h | 182 +++++++
 5 files changed, 941 insertions(+), 918 deletions(-)
 delete mode 100644 arch/arm/cpu/armv7/sunxi/dram.c
 create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun4i.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun4i.h
arch/arm/include/asm/arch-sunxi/dram.h +0 -161
index 1945f75..9072e68 100644
@@ -14,172 +14,13 @@
 
 #include <linux/types.h>
 
-struct sunxi_dram_reg {
-       u32 ccr;                /* 0x00 controller configuration register */
-       u32 dcr;                /* 0x04 dram configuration register */
-       u32 iocr;               /* 0x08 i/o configuration register */
-       u32 csr;                /* 0x0c controller status register */
-       u32 drr;                /* 0x10 dram refresh register */
-       u32 tpr0;               /* 0x14 dram timing parameters register 0 */
-       u32 tpr1;               /* 0x18 dram timing parameters register 1 */
-       u32 tpr2;               /* 0x1c dram timing parameters register 2 */
-       u32 gdllcr;             /* 0x20 global dll control register */
-       u8 res0[0x28];
-       u32 rslr0;              /* 0x4c rank system latency register */
-       u32 rslr1;              /* 0x50 rank system latency register */
-       u8 res1[0x8];
-       u32 rdgr0;              /* 0x5c rank dqs gating register */
-       u32 rdgr1;              /* 0x60 rank dqs gating register */
-       u8 res2[0x34];
-       u32 odtcr;              /* 0x98 odt configuration register */
-       u32 dtr0;               /* 0x9c data training register 0 */
-       u32 dtr1;               /* 0xa0 data training register 1 */
-       u32 dtar;               /* 0xa4 data training address register */
-       u32 zqcr0;              /* 0xa8 zq control register 0 */
-       u32 zqcr1;              /* 0xac zq control register 1 */
-       u32 zqsr;               /* 0xb0 zq status register */
-       u32 idcr;               /* 0xb4 initializaton delay configure reg */
-       u8 res3[0x138];
-       u32 mr;                 /* 0x1f0 mode register */
-       u32 emr;                /* 0x1f4 extended mode register */
-       u32 emr2;               /* 0x1f8 extended mode register */
-       u32 emr3;               /* 0x1fc extended mode register */
-       u32 dllctr;             /* 0x200 dll control register */
-       u32 dllcr[5];           /* 0x204 dll control register 0(byte 0) */
-       /* 0x208 dll control register 1(byte 1) */
-       /* 0x20c dll control register 2(byte 2) */
-       /* 0x210 dll control register 3(byte 3) */
-       /* 0x214 dll control register 4(byte 4) */
-       u32 dqtr0;              /* 0x218 dq timing register */
-       u32 dqtr1;              /* 0x21c dq timing register */
-       u32 dqtr2;              /* 0x220 dq timing register */
-       u32 dqtr3;              /* 0x224 dq timing register */
-       u32 dqstr;              /* 0x228 dqs timing register */
-       u32 dqsbtr;             /* 0x22c dqsb timing register */
-       u32 mcr;                /* 0x230 mode configure register */
-       u8 res[0x8];
-       u32 ppwrsctl;           /* 0x23c pad power save control */
-       u32 apr;                /* 0x240 arbiter period register */
-       u32 pldtr;              /* 0x244 priority level data threshold reg */
-       u8 res5[0x8];
-       u32 hpcr[32];           /* 0x250 host port configure register */
-       u8 res6[0x10];
-       u32 csel;               /* 0x2e0 controller select register */
-};
-
-struct dram_para {
-       u32 clock;
-       u32 mbus_clock;
-       u32 type;
-       u32 rank_num;
-       u32 density;
-       u32 io_width;
-       u32 bus_width;
-       u32 cas;
-       u32 zq;
-       u32 odt_en;
-       u32 size;
-       u32 tpr0;
-       u32 tpr1;
-       u32 tpr2;
-       u32 tpr3;
-       u32 tpr4;
-       u32 tpr5;
-       u32 emr1;
-       u32 emr2;
-       u32 emr3;
-       u32 dqs_gating_delay;
-       u32 active_windowing;
-};
-
-#define DRAM_CCR_COMMAND_RATE_1T (0x1 << 5)
-#define DRAM_CCR_DQS_GATE (0x1 << 14)
-#define DRAM_CCR_DQS_DRIFT_COMP (0x1 << 17)
-#define DRAM_CCR_ITM_OFF (0x1 << 28)
-#define DRAM_CCR_DATA_TRAINING (0x1 << 30)
-#define DRAM_CCR_INIT (0x1 << 31)
-
-#define DRAM_MEMORY_TYPE_DDR1 1
-#define DRAM_MEMORY_TYPE_DDR2 2
-#define DRAM_MEMORY_TYPE_DDR3 3
-#define DRAM_MEMORY_TYPE_LPDDR2 4
-#define DRAM_MEMORY_TYPE_LPDDR 5
-#define DRAM_DCR_TYPE (0x1 << 0)
-#define DRAM_DCR_TYPE_DDR2 0x0
-#define DRAM_DCR_TYPE_DDR3 0x1
-#define DRAM_DCR_IO_WIDTH(n) (((n) & 0x3) << 1)
-#define DRAM_DCR_IO_WIDTH_MASK DRAM_DCR_IO_WIDTH(0x3)
-#define DRAM_DCR_IO_WIDTH_8BIT 0x0
-#define DRAM_DCR_IO_WIDTH_16BIT 0x1
-#define DRAM_DCR_CHIP_DENSITY(n) (((n) & 0x7) << 3)
-#define DRAM_DCR_CHIP_DENSITY_MASK DRAM_DCR_CHIP_DENSITY(0x7)
-#define DRAM_DCR_CHIP_DENSITY_256M 0x0
-#define DRAM_DCR_CHIP_DENSITY_512M 0x1
-#define DRAM_DCR_CHIP_DENSITY_1024M 0x2
-#define DRAM_DCR_CHIP_DENSITY_2048M 0x3
-#define DRAM_DCR_CHIP_DENSITY_4096M 0x4
-#define DRAM_DCR_CHIP_DENSITY_8192M 0x5
-#define DRAM_DCR_BUS_WIDTH(n) (((n) & 0x7) << 6)
-#define DRAM_DCR_BUS_WIDTH_MASK DRAM_DCR_BUS_WIDTH(0x7)
-#define DRAM_DCR_BUS_WIDTH_32BIT 0x3
-#define DRAM_DCR_BUS_WIDTH_16BIT 0x1
-#define DRAM_DCR_BUS_WIDTH_8BIT 0x0
-#define DRAM_DCR_RANK_SEL(n) (((n) & 0x3) << 10)
-#define DRAM_DCR_RANK_SEL_MASK DRAM_DCR_CMD_RANK(0x3)
-#define DRAM_DCR_CMD_RANK_ALL (0x1 << 12)
-#define DRAM_DCR_MODE(n) (((n) & 0x3) << 13)
-#define DRAM_DCR_MODE_MASK DRAM_DCR_MODE(0x3)
-#define DRAM_DCR_MODE_SEQ 0x0
-#define DRAM_DCR_MODE_INTERLEAVE 0x1
-
-#define DRAM_CSR_DTERR  (0x1 << 20)
-#define DRAM_CSR_DTIERR (0x1 << 21)
-#define DRAM_CSR_FAILED (DRAM_CSR_DTERR | DRAM_CSR_DTIERR)
-
-#define DRAM_DRR_TRFC(n) ((n) & 0xff)
-#define DRAM_DRR_TREFI(n) (((n) & 0xffff) << 8)
-#define DRAM_DRR_BURST(n) ((((n) - 1) & 0xf) << 24)
-
-#define DRAM_MCR_MODE_NORM(n) (((n) & 0x3) << 0)
-#define DRAM_MCR_MODE_NORM_MASK DRAM_MCR_MOD_NORM(0x3)
-#define DRAM_MCR_MODE_DQ_OUT(n) (((n) & 0x3) << 2)
-#define DRAM_MCR_MODE_DQ_OUT_MASK DRAM_MCR_MODE_DQ_OUT(0x3)
-#define DRAM_MCR_MODE_ADDR_OUT(n) (((n) & 0x3) << 4)
-#define DRAM_MCR_MODE_ADDR_OUT_MASK DRAM_MCR_MODE_ADDR_OUT(0x3)
-#define DRAM_MCR_MODE_DQ_IN_OUT(n) (((n) & 0x3) << 6)
-#define DRAM_MCR_MODE_DQ_IN_OUT_MASK DRAM_MCR_MODE_DQ_IN_OUT(0x3)
-#define DRAM_MCR_MODE_DQ_TURNON_DELAY(n) (((n) & 0x7) << 8)
-#define DRAM_MCR_MODE_DQ_TURNON_DELAY_MASK DRAM_MCR_MODE_DQ_TURNON_DELAY(0x7)
-#define DRAM_MCR_MODE_ADDR_IN (0x1 << 11)
-#define DRAM_MCR_RESET (0x1 << 12)
-#define DRAM_MCR_MODE_EN(n) (((n) & 0x3) << 13)
-#define DRAM_MCR_MODE_EN_MASK DRAM_MCR_MOD_EN(0x3)
-#define DRAM_MCR_DCLK_OUT (0x1 << 16)
-
-#define DRAM_DLLCR_NRESET (0x1 << 30)
-#define DRAM_DLLCR_DISABLE (0x1 << 31)
-
-#define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20)
-#define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff)
-#define DRAM_ZQCR0_ZCAL (1 << 31) /* Starts ZQ calibration when set to 1 */
-#define DRAM_ZQCR0_ZDEN (1 << 28) /* Uses ZDATA instead of doing calibration */
-
-#define DRAM_ZQSR_ZDONE (1 << 31) /* ZQ calibration completion flag */
-
-#define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0)
-#define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3)
-
-#define DRAM_MR_BURST_LENGTH(n) (((n) & 0x7) << 0)
-#define DRAM_MR_BURST_LENGTH_MASK DRAM_MR_BURST_LENGTH(0x7)
-#define DRAM_MR_CAS_LAT(n) (((n) & 0x7) << 4)
-#define DRAM_MR_CAS_LAT_MASK DRAM_MR_CAS_LAT(0x7)
-#define DRAM_MR_WRITE_RECOVERY(n) (((n) & 0x7) << 9)
-#define DRAM_MR_WRITE_RECOVERY_MASK DRAM_MR_WRITE_RECOVERY(0x7)
Quoted 8 lines

arch/arm/include/asm/arch-sunxi/dram.h line 19

Quoted 6 lines
arch/arm/include/asm/arch-sunxi/dram.h +3 -3
-#define DRAM_MR_POWER_DOWN (0x1 << 12)
-
-#define DRAM_CSEL_MAGIC 0x16237495
+/* dram regs definition */
+#if defined(CONFIG_MACH_SUN6I)
+#include <asm/arch/dram_sun6i.h>

I'm sure CONFIG_MACH_SUN6I isn't set yet, but doesn't this file not exist?

Actually it can be set already, good catch, will fix for v2.

Regards,

Hans

#15 lore raw

PATCH 6/9 sun6i: Add dram initialization code

In reply to Hans de Goede

Add full support for dram initialization, using a fixed clock and autodetection of the memory organization (numbers of channels, bus-width, etc.).

This is based on dram_sun6i.c and dram.h from u-boot in the Allwinner A31 SDK, extended with extra initialization sequences and the autodetect algorithm from boot0.

---
 arch/arm/cpu/armv7/sunxi/Makefile            |   1 +
 arch/arm/cpu/armv7/sunxi/dram_sun6i.c        | 436 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/cpu.h        |   8 +-
 arch/arm/include/asm/arch-sunxi/dram_sun6i.h | 359 ++++++++++++++++++++++
 4 files changed, 801 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/cpu/armv7/sunxi/dram_sun6i.c
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun6i.h
arch/arm/cpu/armv7/sunxi/Makefile +1 -0
index 48cca0b..3b6ae47 100644
@@ -30,6 +30,7 @@ endif
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_MACH_SUN4I)	+= dram_sun4i.o
 obj-$(CONFIG_MACH_SUN5I)	+= dram_sun4i.o
+obj-$(CONFIG_MACH_SUN6I)	+= dram_sun6i.o
 obj-$(CONFIG_MACH_SUN7I)	+= dram_sun4i.o
 ifdef CONFIG_SPL_FEL
 obj-y	+= start.o
arch/arm/cpu/armv7/sunxi/dram_sun6i.c new file +436 -0

436 lines not on this page · raw message

arch/arm/include/asm/arch-sunxi/cpu.h +5 -3
index 6258cce..2d06376 100644
@@ -98,9 +98,10 @@
 #define SUNXI_GMAC_BASE			0x01c50000
 
 #define SUNXI_DRAM_COM_BASE		0x01c62000
-#define SUNXI_DRAM_CTL_BASE		0x01c63000
-#define SUNXI_DRAM_PHY_CH1_BASE		0x01c65000
-#define SUNXI_DRAM_PHY_CH2_BASE		0x01c66000
+#define SUNXI_DRAM_CTL0_BASE		0x01c63000
+#define SUNXI_DRAM_CTL1_BASE		0x01c64000
+#define SUNXI_DRAM_PHY0_BASE		0x01c65000
+#define SUNXI_DRAM_PHY1_BASE		0x01c66000
 
 /* module sram */
 #define SUNXI_SRAM_C_BASE		0x01d00000
@@ -112,6 +113,7 @@
 #define SUNXI_MP_BASE			0x01e80000
 #define SUNXI_AVG_BASE			0x01ea0000
 
+#define SUNXI_RTC_BASE			0x01f00000
 #define SUNXI_PRCM_BASE			0x01f01400
 #ifdef CONFIG_MACH_SUN6I
 #define SUNXI_CPUCFG_BASE		0x01f01c00
arch/arm/include/asm/arch-sunxi/dram_sun6i.h new file +359 -0

359 lines not on this page · raw message

Signature · 1 line
2.1.0
#16 lore raw

PATCH 7/9 sun6i: Poke magic sram controller register to avoid cache issues

In reply to Hans de Goede

Without this the cache will only work in write-through mode, and as soon as it is put in write-back mode things break.

---
 arch/arm/cpu/armv7/sunxi/board.c | 5 +++++
 1 file changed, 5 insertions(+)
arch/arm/cpu/armv7/sunxi/board.c +5 -0
index 6c812fc..9b3e80c 100644
@@ -114,6 +114,11 @@ void reset_cpu(ulong addr)
 /* do some early init */
 void s_init(void)
 {
+#if defined CONFIG_SPL_BUILD && defined CONFIG_MACH_SUN6I
+	/* Magic (undocmented) value taken from boot0, without this DRAM
+	 * access gets messed up (seems cache related) */
+	setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800);
+#endif
 #if !defined CONFIG_SPL_BUILD && (defined CONFIG_MACH_SUN7I || \
 		defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I)
 	/* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */
Signature · 1 line
2.1.0
#17 lore raw

PATCH 8/9 sun6i: Enable SPL

In reply to Hans de Goede

Enable the SPL now that we've all the necessary bits in place.

---
 board/sunxi/Kconfig        | 1 +
 configs/Colombus_defconfig | 9 +++++----
 2 files changed, 6 insertions(+), 4 deletions(-)
board/sunxi/Kconfig +1 -0
index 905e62d..df47863 100644
@@ -16,6 +16,7 @@ config MACH_SUN5I
 config MACH_SUN6I
 	bool "sun6i (Allwinner A31)"
 	select CPU_V7
+	select SUPPORT_SPL
 
 config MACH_SUN7I
 	bool "sun7i (Allwinner A20)"
configs/Colombus_defconfig +5 -4
index 06fb642..67ae391 100644
@@ -1,8 +1,9 @@
+CONFIG_SPL=y
 CONFIG_SYS_EXTRA_OPTIONS="AXP221_POWER"
-CONFIG_ARM=y
-CONFIG_ARCH_SUNXI=y
-CONFIG_MACH_SUN6I=y
-CONFIG_TARGET_COLOMBUS=y
 CONFIG_FDTFILE="sun6i-a31-colombus.dtb"
 CONFIG_VIDEO=n
 CONFIG_USB_KEYBOARD=n
++S:CONFIG_ARM=y
++S:CONFIG_ARCH_SUNXI=y
++S:CONFIG_MACH_SUN6I=y
++S:CONFIG_TARGET_COLOMBUS=y
Signature · 1 line
2.1.0
#18 lore raw

PATCH 9/9 sun6i: Add Mele M9 board

In reply to Hans de Goede
---
 board/sunxi/Kconfig       | 4 ++++
 board/sunxi/MAINTAINERS   | 1 +
 configs/Mele_M9_defconfig | 9 +++++++++
 3 files changed, 14 insertions(+)
 create mode 100644 configs/Mele_M9_defconfig
board/sunxi/Kconfig +4 -0
index df47863..f60da9d 100644
@@ -113,6 +113,10 @@ config TARGET_MELE_M3
 	bool "MELE_M3"
 	depends on MACH_SUN7I
 
+config TARGET_MELE_M9
+	bool "MELE_M9"
+	depends on MACH_SUN6I
+
 config TARGET_MINI_X_1GB
 	bool "MINI_X_1GB"
 	depends on MACH_SUN4I
board/sunxi/MAINTAINERS +1 -0
index b3c77a8..ca03002 100644
@@ -9,6 +9,7 @@ F:	configs/Cubieboard_defconfig
 F:	configs/Mele_A1000_defconfig
 F:	configs/Mele_A1000G_defconfig
 F:	configs/Mele_M3_defconfig
+F:	configs/Mele_M9_defconfig
 F:	configs/Mini-X_defconfig
 F:	configs/Mini-X-1Gb_defconfig
 F:	include/configs/sun5i.h
configs/Mele_M9_defconfig new file +9 -0
new file mode 100644
index 0000000..8cad212
@@ -0,0 +1,9 @@
+CONFIG_SPL=y
+CONFIG_SYS_EXTRA_OPTIONS="AXP221_POWER"
+CONFIG_FDTFILE="sun6i-a31-m9.dtb"
+CONFIG_VIDEO=n
+CONFIG_USB_KEYBOARD=n
++S:CONFIG_ARM=y
++S:CONFIG_ARCH_SUNXI=y
++S:CONFIG_MACH_SUN6I=y
++S:CONFIG_TARGET_MELE_M9=y
Signature · 1 line
2.1.0