summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorJens Kuske <jenskuske@gmail.com>2017-01-02 11:48:40 +0000
committerJagan Teki <jagan@openedev.com>2017-01-04 16:37:42 +0100
commite013bead30e9c1fa26c765aa861875527531b83e (patch)
treeadfa18e4141d41c6168aa88968d680692146dd53 /arch
parent0eb6f9fd8109f54bfea5c209f58f3634ac4ef931 (diff)
sunxi: H3: add DRAM controller single bit delay support
So far the DRAM driver for the H3 SoC (and apparently boot0/libdram as well) only applied coarse delay line settings, with one delay value for all the data lines in each byte lane and one value for the control lines. Instead of setting the delays for whole bytes only allow setting it for each individual bit. Also add support for address/command lane delays. For the purpose of this patch the rules for the existing coarse settings were just applied to the new scheme, so the actual register writes don't change for the H3. Other SoCs will utilize this feature later properly. With a stock GCC 5.3.0 this increases the dram_sun8i_h3.o code size from 2296 to 2344 Bytes. [Andre: move delay parameters into macros to ease later sharing, use defines for numbers of delay registers, extend commit message] Signed-off-by: Jens Kuske <jenskuske@gmail.com> Signed-off-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Jagan Teki <jagan@openedev.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-sunxi/dram_sun8i_h3.c77
1 files changed, 50 insertions, 27 deletions
diff --git a/arch/arm/mach-sunxi/dram_sun8i_h3.c b/arch/arm/mach-sunxi/dram_sun8i_h3.c
index 539268f1fb..4396754766 100644
--- a/arch/arm/mach-sunxi/dram_sun8i_h3.c
+++ b/arch/arm/mach-sunxi/dram_sun8i_h3.c
@@ -15,13 +15,24 @@
#include <asm/arch/dram.h>
#include <linux/kconfig.h>
+/*
+ * The delay parameters below allow to allegedly specify delay times of some
+ * unknown unit for each individual bit trace in each of the four data bytes
+ * the 32-bit wide access consists of. Also three control signals can be
+ * adjusted individually.
+ */
+#define BITS_PER_BYTE 8
+#define NR_OF_BYTE_LANES (32 / BITS_PER_BYTE)
+/* The eight data lines (DQn) plus DM, DQS and DQSN */
+#define LINES_PER_BYTE_LANE (BITS_PER_BYTE + 3)
struct dram_para {
- u32 read_delays;
- u32 write_delays;
u16 page_size;
u8 bus_width;
u8 dual_rank;
u8 row_bits;
+ const u8 dx_read_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
+ const u8 dx_write_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
+ const u8 ac_delays[31];
};
static inline int ns_to_t(int nanoseconds)
@@ -64,34 +75,25 @@ static void mctl_phy_init(u32 val)
mctl_await_completion(&mctl_ctl->pgsr[0], PGSR_INIT_DONE, 0x1);
}
-static void mctl_dq_delay(u32 read, u32 write)
+static void mctl_set_bit_delays(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
int i, j;
- u32 val;
-
- for (i = 0; i < 4; i++) {
- val = DXBDLR_WRITE_DELAY((write >> (i * 4)) & 0xf) |
- DXBDLR_READ_DELAY(((read >> (i * 4)) & 0xf) * 2);
-
- for (j = DXBDLR_DQ(0); j <= DXBDLR_DM; j++)
- writel(val, &mctl_ctl->dx[i].bdlr[j]);
- }
clrbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
- for (i = 0; i < 4; i++) {
- val = DXBDLR_WRITE_DELAY((write >> (16 + i * 4)) & 0xf) |
- DXBDLR_READ_DELAY((read >> (16 + i * 4)) & 0xf);
+ for (i = 0; i < NR_OF_BYTE_LANES; i++)
+ for (j = 0; j < LINES_PER_BYTE_LANE; j++)
+ writel(DXBDLR_WRITE_DELAY(para->dx_write_delays[i][j]) |
+ DXBDLR_READ_DELAY(para->dx_read_delays[i][j]),
+ &mctl_ctl->dx[i].bdlr[j]);
- writel(val, &mctl_ctl->dx[i].bdlr[DXBDLR_DQS]);
- writel(val, &mctl_ctl->dx[i].bdlr[DXBDLR_DQSN]);
- }
+ for (i = 0; i < 31; i++)
+ writel(ACBDLR_WRITE_DELAY(para->ac_delays[i]),
+ &mctl_ctl->acbdlr[i]);
setbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
-
- udelay(1);
}
enum {
@@ -412,11 +414,8 @@ static int mctl_channel_init(struct dram_para *para)
clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24,
(para->dual_rank ? 0x3 : 0x1) << 24);
-
- if (para->read_delays || para->write_delays) {
- mctl_dq_delay(para->read_delays, para->write_delays);
- udelay(50);
- }
+ mctl_set_bit_delays(para);
+ udelay(50);
mctl_zq_calibration(para);
@@ -490,6 +489,29 @@ static void mctl_auto_detect_dram_size(struct dram_para *para)
break;
}
+/*
+ * The actual values used here are taken from Allwinner provided boot0
+ * binaries, though they are probably board specific, so would likely benefit
+ * from invidual tuning for each board. Apparently a lot of boards copy from
+ * some Allwinner reference design, so we go with those generic values for now
+ * in the hope that they are reasonable for most (all?) boards.
+ */
+#define SUN8I_H3_DX_READ_DELAYS \
+ {{ 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0 }, \
+ { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \
+ { 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0 }, \
+ { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }}
+#define SUN8I_H3_DX_WRITE_DELAYS \
+ {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6 }}
+#define SUN8I_H3_AC_DELAYS \
+ { 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0 }
+
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
@@ -498,12 +520,13 @@ unsigned long sunxi_dram_init(void)
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct dram_para para = {
- .read_delays = 0x00007979, /* dram_tpr12 */
- .write_delays = 0x6aaa0000, /* dram_tpr11 */
.dual_rank = 0,
.bus_width = 32,
.row_bits = 15,
.page_size = 4096,
+ .dx_read_delays = SUN8I_H3_DX_READ_DELAYS,
+ .dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS,
+ .ac_delays = SUN8I_H3_AC_DELAYS,
};
mctl_sys_init(&para);