#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)
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 {
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);
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 =
(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(¶);