To quote Andre:

The first few patches are some easy refactorings and fixes, most of them
actually don't change the generated binaries at all. Then there is a
defconfig for a new board, for which we just gained the .dts file from
the last kernel DT sync.
On top there is support for a new PMIC (AXP313), and LPDDR4 support for
the Allwinner H616 SoC, both of which are needed to support new devices
that appeared lately, especially cheap TV boxes.

While those are technically new features, they don't affect existing
boards, for instance the LPDDR4 support code is guarded by a new DRAM
type Kconfig variable. So the risk for regressions is very slim.

Gitlab CI passed, and I booted that briefly on some boards, including an
H616 and an H618 one (with LPDDR4).
This commit is contained in:
Tom Rini 2023-11-12 16:36:22 -05:00
commit 92b27528d7
22 changed files with 686 additions and 257 deletions

View file

@ -721,6 +721,7 @@ dtb-$(CONFIG_MACH_SUN7I) += \
sun7i-a20-haoyu-marsboard.dtb \
sun7i-a20-hummingbird.dtb \
sun7i-a20-i12-tvbox.dtb \
sun7i-a20-icnova-a20-adb4006.dtb \
sun7i-a20-icnova-swac.dtb \
sun7i-a20-itead-ibox.dtb \
sun7i-a20-lamobo-r1.dtb \

View file

@ -130,6 +130,7 @@ check_member(sunxi_mctl_ctl_reg, unk_0x4240, 0x4240);
#define MSTR_DEVICETYPE_LPDDR2 BIT(2)
#define MSTR_DEVICETYPE_LPDDR3 BIT(3)
#define MSTR_DEVICETYPE_DDR4 BIT(4)
#define MSTR_DEVICETYPE_LPDDR4 BIT(5)
#define MSTR_DEVICETYPE_MASK GENMASK(5, 0)
#define MSTR_2TMODE BIT(10)
#define MSTR_BUSWIDTH_FULL (0 << 12)
@ -154,6 +155,7 @@ struct dram_para {
u32 odt_en;
u32 tpr0;
u32 tpr2;
u32 tpr6;
u32 tpr10;
u32 tpr11;
u32 tpr12;

View file

@ -1,139 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Aaron <leafy.myeh@allwinnertech.com>
*
* MMC register definition for allwinner sunxi platform.
*/
#ifndef _SUNXI_MMC_H
#define _SUNXI_MMC_H
#include <linux/types.h>
struct sunxi_mmc {
u32 gctrl; /* 0x00 global control */
u32 clkcr; /* 0x04 clock control */
u32 timeout; /* 0x08 time out */
u32 width; /* 0x0c bus width */
u32 blksz; /* 0x10 block size */
u32 bytecnt; /* 0x14 byte count */
u32 cmd; /* 0x18 command */
u32 arg; /* 0x1c argument */
u32 resp0; /* 0x20 response 0 */
u32 resp1; /* 0x24 response 1 */
u32 resp2; /* 0x28 response 2 */
u32 resp3; /* 0x2c response 3 */
u32 imask; /* 0x30 interrupt mask */
u32 mint; /* 0x34 masked interrupt status */
u32 rint; /* 0x38 raw interrupt status */
u32 status; /* 0x3c status */
u32 ftrglevel; /* 0x40 FIFO threshold watermark*/
u32 funcsel; /* 0x44 function select */
u32 cbcr; /* 0x48 CIU byte count */
u32 bbcr; /* 0x4c BIU byte count */
u32 dbgc; /* 0x50 debug enable */
u32 res0; /* 0x54 reserved */
u32 a12a; /* 0x58 Auto command 12 argument */
u32 ntsr; /* 0x5c New timing set register */
u32 res1[8];
u32 dmac; /* 0x80 internal DMA control */
u32 dlba; /* 0x84 internal DMA descr list base address */
u32 idst; /* 0x88 internal DMA status */
u32 idie; /* 0x8c internal DMA interrupt enable */
u32 chda; /* 0x90 */
u32 cbda; /* 0x94 */
u32 res2[26];
#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
u32 res3[17];
u32 samp_dl;
u32 res4[46];
#endif
u32 fifo; /* 0x100 / 0x200 FIFO access address */
};
#define SUNXI_MMC_CLK_POWERSAVE (0x1 << 17)
#define SUNXI_MMC_CLK_ENABLE (0x1 << 16)
#define SUNXI_MMC_CLK_DIVIDER_MASK (0xff)
#define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0)
#define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1)
#define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2)
#define SUNXI_MMC_GCTRL_RESET (SUNXI_MMC_GCTRL_SOFT_RESET|\
SUNXI_MMC_GCTRL_FIFO_RESET|\
SUNXI_MMC_GCTRL_DMA_RESET)
#define SUNXI_MMC_GCTRL_DMA_ENABLE (0x1 << 5)
#define SUNXI_MMC_GCTRL_ACCESS_BY_AHB (0x1 << 31)
#define SUNXI_MMC_CMD_RESP_EXPIRE (0x1 << 6)
#define SUNXI_MMC_CMD_LONG_RESPONSE (0x1 << 7)
#define SUNXI_MMC_CMD_CHK_RESPONSE_CRC (0x1 << 8)
#define SUNXI_MMC_CMD_DATA_EXPIRE (0x1 << 9)
#define SUNXI_MMC_CMD_WRITE (0x1 << 10)
#define SUNXI_MMC_CMD_AUTO_STOP (0x1 << 12)
#define SUNXI_MMC_CMD_WAIT_PRE_OVER (0x1 << 13)
#define SUNXI_MMC_CMD_SEND_INIT_SEQ (0x1 << 15)
#define SUNXI_MMC_CMD_UPCLK_ONLY (0x1 << 21)
#define SUNXI_MMC_CMD_START (0x1 << 31)
#define SUNXI_MMC_RINT_RESP_ERROR (0x1 << 1)
#define SUNXI_MMC_RINT_COMMAND_DONE (0x1 << 2)
#define SUNXI_MMC_RINT_DATA_OVER (0x1 << 3)
#define SUNXI_MMC_RINT_TX_DATA_REQUEST (0x1 << 4)
#define SUNXI_MMC_RINT_RX_DATA_REQUEST (0x1 << 5)
#define SUNXI_MMC_RINT_RESP_CRC_ERROR (0x1 << 6)
#define SUNXI_MMC_RINT_DATA_CRC_ERROR (0x1 << 7)
#define SUNXI_MMC_RINT_RESP_TIMEOUT (0x1 << 8)
#define SUNXI_MMC_RINT_DATA_TIMEOUT (0x1 << 9)
#define SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE (0x1 << 10)
#define SUNXI_MMC_RINT_FIFO_RUN_ERROR (0x1 << 11)
#define SUNXI_MMC_RINT_HARD_WARE_LOCKED (0x1 << 12)
#define SUNXI_MMC_RINT_START_BIT_ERROR (0x1 << 13)
#define SUNXI_MMC_RINT_AUTO_COMMAND_DONE (0x1 << 14)
#define SUNXI_MMC_RINT_END_BIT_ERROR (0x1 << 15)
#define SUNXI_MMC_RINT_SDIO_INTERRUPT (0x1 << 16)
#define SUNXI_MMC_RINT_CARD_INSERT (0x1 << 30)
#define SUNXI_MMC_RINT_CARD_REMOVE (0x1 << 31)
#define SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT \
(SUNXI_MMC_RINT_RESP_ERROR | \
SUNXI_MMC_RINT_RESP_CRC_ERROR | \
SUNXI_MMC_RINT_DATA_CRC_ERROR | \
SUNXI_MMC_RINT_RESP_TIMEOUT | \
SUNXI_MMC_RINT_DATA_TIMEOUT | \
SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE | \
SUNXI_MMC_RINT_FIFO_RUN_ERROR | \
SUNXI_MMC_RINT_HARD_WARE_LOCKED | \
SUNXI_MMC_RINT_START_BIT_ERROR | \
SUNXI_MMC_RINT_END_BIT_ERROR) /* 0xbfc2 */
#define SUNXI_MMC_RINT_INTERRUPT_DONE_BIT \
(SUNXI_MMC_RINT_AUTO_COMMAND_DONE | \
SUNXI_MMC_RINT_DATA_OVER | \
SUNXI_MMC_RINT_COMMAND_DONE | \
SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE)
#define SUNXI_MMC_STATUS_RXWL_FLAG (0x1 << 0)
#define SUNXI_MMC_STATUS_TXWL_FLAG (0x1 << 1)
#define SUNXI_MMC_STATUS_FIFO_EMPTY (0x1 << 2)
#define SUNXI_MMC_STATUS_FIFO_FULL (0x1 << 3)
#define SUNXI_MMC_STATUS_CARD_PRESENT (0x1 << 8)
#define SUNXI_MMC_STATUS_CARD_DATA_BUSY (0x1 << 9)
#define SUNXI_MMC_STATUS_DATA_FSM_BUSY (0x1 << 10)
#define SUNXI_MMC_STATUS_FIFO_LEVEL(reg) (((reg) >> 17) & 0x3fff)
#define SUNXI_MMC_NTSR_MODE_SEL_NEW (0x1 << 31)
#define SUNXI_MMC_IDMAC_RESET (0x1 << 0)
#define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1)
#define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7)
#define SUNXI_MMC_IDIE_TXIRQ (0x1 << 0)
#define SUNXI_MMC_IDIE_RXIRQ (0x1 << 1)
#define SUNXI_MMC_COMMON_CLK_GATE (1 << 16)
#define SUNXI_MMC_COMMON_RESET (1 << 18)
#define SUNXI_MMC_CAL_DL_SW_EN (0x1 << 7)
#ifndef _ASM_ARCH_MMC_H_
#define _ASM_ARCH_MMC_H_
struct mmc *sunxi_mmc_init(int sdc_no);
#endif /* _SUNXI_MMC_H */
#endif /* _ASM_ARCH_MMC_H_ */

View file

@ -85,6 +85,12 @@ config DRAM_SUN50I_H616_TPR2
help
TPR2 value from vendor DRAM settings.
config DRAM_SUN50I_H616_TPR6
hex "H616 DRAM TPR6 parameter"
default 0x3300c080
help
TPR6 value from vendor DRAM settings.
config DRAM_SUN50I_H616_TPR10
hex "H616 DRAM TPR10 parameter"
help
@ -462,6 +468,9 @@ config SUNXI_DRAM_DDR2
config SUNXI_DRAM_LPDDR3
bool
config SUNXI_DRAM_LPDDR4
bool
choice
prompt "DRAM Type and Timing"
default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S
@ -505,6 +514,14 @@ config SUNXI_DRAM_H616_LPDDR3
This option is the LPDDR3 timing used by the stock boot0 by
Allwinner.
config SUNXI_DRAM_H616_LPDDR4
bool "LPDDR4 DRAM chips on the H616 DRAM controller"
select SUNXI_DRAM_LPDDR4
depends on DRAM_SUN50I_H616
help
This option is the LPDDR4 timing used by the stock boot0 by
Allwinner.
config SUNXI_DRAM_H616_DDR3_1333
bool "DDR3-1333 boot0 timings on the H616 DRAM controller"
select SUNXI_DRAM_DDR3

View file

@ -6,8 +6,8 @@
* unknown. That's why this driver has plenty of magic numbers. Some
* meaning was nevertheless deduced from strings found in boot0 and
* known meaning of some dram parameters.
* This driver only supports DDR3 memory and omits logic for all
* other supported types supported by hardware.
* This driver supports DDR3, LPDDR3 and LPDDR4 memory. There is no
* DDR4 support yet.
*
* (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
*
@ -238,64 +238,59 @@ static const u8 phy_init[] = {
0x08, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07,
0x17, 0x19, 0x1a
#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR4)
0x02, 0x00, 0x17, 0x05, 0x04, 0x19, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x01,
0x18, 0x03, 0x1a
#endif
};
#define MASK_BYTE(reg, nr) (((reg) >> ((nr) * 8)) & 0x1f)
static void mctl_phy_configure_odt(const struct dram_para *para)
{
unsigned int val;
uint32_t val_lo, val_hi;
val = para->dx_dri & 0x1f;
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x388);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x38c);
/*
* This part should be applicable to all memory types, but is
* usually found in LPDDR4 bootloaders. Therefore, we will leave
* only for this type of memory.
*/
if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x390, BIT(5), BIT(4));
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3d0, BIT(5), BIT(4));
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x410, BIT(5), BIT(4));
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x450, BIT(5), BIT(4));
}
val = (para->dx_dri >> 8) & 0x1f;
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c8);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3cc);
val_lo = para->dx_dri;
val_hi = (para->type == SUNXI_DRAM_TYPE_LPDDR4) ? 0x04040404 : para->dx_dri;
writel_relaxed(MASK_BYTE(val_lo, 0), SUNXI_DRAM_PHY0_BASE + 0x388);
writel_relaxed(MASK_BYTE(val_hi, 0), SUNXI_DRAM_PHY0_BASE + 0x38c);
writel_relaxed(MASK_BYTE(val_lo, 1), SUNXI_DRAM_PHY0_BASE + 0x3c8);
writel_relaxed(MASK_BYTE(val_hi, 1), SUNXI_DRAM_PHY0_BASE + 0x3cc);
writel_relaxed(MASK_BYTE(val_lo, 2), SUNXI_DRAM_PHY0_BASE + 0x408);
writel_relaxed(MASK_BYTE(val_hi, 2), SUNXI_DRAM_PHY0_BASE + 0x40c);
writel_relaxed(MASK_BYTE(val_lo, 3), SUNXI_DRAM_PHY0_BASE + 0x448);
writel_relaxed(MASK_BYTE(val_hi, 3), SUNXI_DRAM_PHY0_BASE + 0x44c);
val = (para->dx_dri >> 16) & 0x1f;
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x408);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x40c);
val_lo = para->ca_dri;
val_hi = para->ca_dri;
writel_relaxed(MASK_BYTE(val_lo, 0), SUNXI_DRAM_PHY0_BASE + 0x340);
writel_relaxed(MASK_BYTE(val_hi, 0), SUNXI_DRAM_PHY0_BASE + 0x344);
writel_relaxed(MASK_BYTE(val_lo, 1), SUNXI_DRAM_PHY0_BASE + 0x348);
writel_relaxed(MASK_BYTE(val_hi, 1), SUNXI_DRAM_PHY0_BASE + 0x34c);
val = (para->dx_dri >> 24) & 0x1f;
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x448);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x44c);
val = para->ca_dri & 0x1f;
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x340);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x344);
val = (para->ca_dri >> 8) & 0x1f;
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x348);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x34c);
val = para->dx_odt & 0x1f;
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x380);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x380);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x384);
val = (para->dx_odt >> 8) & 0x1f;
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x3c0);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c0);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c4);
val = (para->dx_odt >> 16) & 0x1f;
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x400);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x400);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x404);
val = (para->dx_odt >> 24) & 0x1f;
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x440);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x440);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x444);
val_lo = (para->type == SUNXI_DRAM_TYPE_LPDDR3) ? 0 : para->dx_odt;
val_hi = (para->type == SUNXI_DRAM_TYPE_LPDDR4) ? 0 : para->dx_odt;
writel_relaxed(MASK_BYTE(val_lo, 0), SUNXI_DRAM_PHY0_BASE + 0x380);
writel_relaxed(MASK_BYTE(val_hi, 0), SUNXI_DRAM_PHY0_BASE + 0x384);
writel_relaxed(MASK_BYTE(val_lo, 1), SUNXI_DRAM_PHY0_BASE + 0x3c0);
writel_relaxed(MASK_BYTE(val_hi, 1), SUNXI_DRAM_PHY0_BASE + 0x3c4);
writel_relaxed(MASK_BYTE(val_lo, 2), SUNXI_DRAM_PHY0_BASE + 0x400);
writel_relaxed(MASK_BYTE(val_hi, 2), SUNXI_DRAM_PHY0_BASE + 0x404);
writel_relaxed(MASK_BYTE(val_lo, 3), SUNXI_DRAM_PHY0_BASE + 0x440);
writel_relaxed(MASK_BYTE(val_hi, 3), SUNXI_DRAM_PHY0_BASE + 0x444);
dmb();
}
@ -414,12 +409,18 @@ static bool mctl_phy_read_calibration(const struct dram_config *config)
return result;
}
static bool mctl_phy_read_training(const struct dram_config *config)
static bool mctl_phy_read_training(const struct dram_para *para,
const struct dram_config *config)
{
u32 val1, val2, *ptr1, *ptr2;
bool result = true;
int i;
if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
writel(0, SUNXI_DRAM_PHY0_BASE + 0x800);
writel(0, SUNXI_DRAM_PHY0_BASE + 0x81c);
}
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2);
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x804, 0x3f, 0xf);
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x808, 0x3f, 0xf);
@ -600,6 +601,8 @@ static void mctl_phy_bit_delay_compensation(const struct dram_para *para)
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1);
setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 8);
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10);
if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, 0x80);
if (para->tpr10 & BIT(30))
val = para->tpr11 & 0x3f;
@ -813,8 +816,9 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para,
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e0);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f4);
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
val = (para->tpr10 >> 7) & 0x1e;
val = (para->tpr10 >> 7) & 0x1e;
switch (para->type) {
case SUNXI_DRAM_TYPE_DDR3:
if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
if (config->ranks == 2) {
@ -840,8 +844,8 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para,
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
}
}
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
val = (para->tpr10 >> 7) & 0x1e;
break;
case SUNXI_DRAM_TYPE_LPDDR3:
if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a0);
if (config->ranks == 2) {
@ -855,7 +859,18 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para,
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f8);
}
}
}
break;
case SUNXI_DRAM_TYPE_LPDDR4:
if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x788);
} else {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
};
break;
case SUNXI_DRAM_TYPE_DDR4:
default:
panic("This DRAM setup is currently not supported.\n");
};
}
static bool mctl_phy_init(const struct dram_para *para,
@ -868,30 +883,42 @@ static bool mctl_phy_init(const struct dram_para *para,
u32 val, val2, *ptr, mr0, mr2;
int i;
if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4,0x80);
if (config->bus_full_width)
val = 0xf;
else
val = 3;
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val);
if (para->tpr2 & 0x100) {
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
switch (para->type) {
case SUNXI_DRAM_TYPE_DDR3:
if (para->tpr2 & 0x100) {
val = 9;
val2 = 7;
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
// untested setup: use some values for now
val = 14;
val2 = 8;
}
} else {
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
} else {
val = 13;
val2 = 9;
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
}
break;
case SUNXI_DRAM_TYPE_LPDDR3:
if (para->tpr2 & 0x100) {
val = 12;
val2 = 6;
} else {
val = 14;
val2 = 8;
}
}
break;
case SUNXI_DRAM_TYPE_LPDDR4:
val = 20;
val2 = 10;
break;
case SUNXI_DRAM_TYPE_DDR4:
default:
panic("This DRAM setup is currently not supported.\n");
};
writel(val, SUNXI_DRAM_PHY0_BASE + 0x14);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x35c);
@ -915,19 +942,40 @@ static bool mctl_phy_init(const struct dram_para *para,
if (para->tpr10 & TPR10_CA_BIT_DELAY)
mctl_phy_ca_bit_delay_compensation(para, config);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
val = 0x80;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
val = 0xc0;
switch (para->type) {
case SUNXI_DRAM_TYPE_DDR3:
val = para->tpr6 & 0xff;
break;
case SUNXI_DRAM_TYPE_LPDDR3:
val = para->tpr6 >> 8 & 0xff;
break;
case SUNXI_DRAM_TYPE_LPDDR4:
val = para->tpr6 >> 24 & 0xff;
break;
case SUNXI_DRAM_TYPE_DDR4:
default:
panic("This DRAM setup is currently not supported.\n");
};
writel(val, SUNXI_DRAM_PHY0_BASE + 0x3dc);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x45c);
mctl_phy_configure_odt(para);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
switch (para->type) {
case SUNXI_DRAM_TYPE_DDR3:
val = 0x0a;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
break;
case SUNXI_DRAM_TYPE_LPDDR3:
val = 0x0b;
break;
case SUNXI_DRAM_TYPE_LPDDR4:
val = 0x0d;
break;
case SUNXI_DRAM_TYPE_DDR4:
default:
panic("This DRAM setup is currently not supported.\n");
};
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x7, val);
if (para->clk <= 672)
@ -977,8 +1025,8 @@ static bool mctl_phy_init(const struct dram_para *para,
mr0 = 0x1f14;
mr2 = 0x20;
}
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
switch (para->type) {
case SUNXI_DRAM_TYPE_DDR3:
writel(mr0, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
@ -994,7 +1042,8 @@ static bool mctl_phy_init(const struct dram_para *para,
writel(0, &mctl_ctl->mrctrl1);
writel(0x80003030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
break;
case SUNXI_DRAM_TYPE_LPDDR3:
writel(mr0, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
@ -1010,7 +1059,48 @@ static bool mctl_phy_init(const struct dram_para *para,
writel(0x301, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
}
break;
case SUNXI_DRAM_TYPE_LPDDR4:
writel(0x0, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0x134, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0x21b, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0x333, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0x403, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0xb04, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0xc72, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0xe09, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0x1624, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
break;
case SUNXI_DRAM_TYPE_DDR4:
default:
panic("This DRAM setup is currently not supported.\n");
};
writel(0, SUNXI_DRAM_PHY0_BASE + 0x54);
@ -1040,7 +1130,7 @@ static bool mctl_phy_init(const struct dram_para *para,
if (para->tpr10 & TPR10_READ_TRAINING) {
for (i = 0; i < 5; i++)
if (mctl_phy_read_training(config))
if (mctl_phy_read_training(para, config))
break;
if (i == 5) {
debug("read training failed!\n");
@ -1079,17 +1169,29 @@ static bool mctl_ctrl_init(const struct dram_para *para,
setbits_le32(&mctl_com->unk_0x008, 0xff00);
if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
writel(1, SUNXI_DRAM_COM_BASE + 0x50);
clrsetbits_le32(&mctl_ctl->sched[0], 0xff00, 0x3000);
writel(0, &mctl_ctl->hwlpctl);
setbits_le32(&mctl_com->unk_0x008, 0xff00);
reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(config->ranks);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
reg_val |= MSTR_DEVICETYPE_LPDDR3;
reg_val = MSTR_ACTIVE_RANKS(config->ranks);
switch (para->type) {
case SUNXI_DRAM_TYPE_DDR3:
reg_val |= MSTR_BURST_LENGTH(8) | MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
break;
case SUNXI_DRAM_TYPE_LPDDR3:
reg_val |= MSTR_BURST_LENGTH(8) | MSTR_DEVICETYPE_LPDDR3;
break;
case SUNXI_DRAM_TYPE_LPDDR4:
reg_val |= MSTR_BURST_LENGTH(16) | MSTR_DEVICETYPE_LPDDR4;
break;
case SUNXI_DRAM_TYPE_DDR4:
default:
panic("This DRAM setup is currently not supported.\n");
};
if (config->bus_full_width)
reg_val |= MSTR_BUSWIDTH_FULL;
else
@ -1101,10 +1203,20 @@ static bool mctl_ctrl_init(const struct dram_para *para,
else
writel(0x0201, &mctl_ctl->odtmap);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
switch (para->type) {
case SUNXI_DRAM_TYPE_DDR3:
reg_val = 0x06000400;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
break;
case SUNXI_DRAM_TYPE_LPDDR3:
reg_val = 0x09020400;
break;
case SUNXI_DRAM_TYPE_LPDDR4:
reg_val = 0x04000400;
break;
case SUNXI_DRAM_TYPE_DDR4:
default:
panic("This DRAM setup is currently not supported.\n");
};
writel(reg_val, &mctl_ctl->odtcfg);
writel(reg_val, &mctl_ctl->unk_0x2240);
writel(reg_val, &mctl_ctl->unk_0x3240);
@ -1124,6 +1236,9 @@ static bool mctl_ctrl_init(const struct dram_para *para,
setbits_le32(&mctl_ctl->unk_0x3180, BIT(31) | BIT(30));
setbits_le32(&mctl_ctl->unk_0x4180, BIT(31) | BIT(30));
if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
setbits_le32(&mctl_ctl->dbictl, 0x1);
setbits_le32(&mctl_ctl->rfshctl3, BIT(0));
clrbits_le32(&mctl_ctl->dfimisc, BIT(0));
@ -1246,6 +1361,8 @@ static const struct dram_para para = {
.type = SUNXI_DRAM_TYPE_DDR3,
#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3)
.type = SUNXI_DRAM_TYPE_LPDDR3,
#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR4)
.type = SUNXI_DRAM_TYPE_LPDDR4,
#endif
.dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT,
.dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI,
@ -1253,6 +1370,7 @@ static const struct dram_para para = {
.odt_en = CONFIG_DRAM_SUN50I_H616_ODT_EN,
.tpr0 = CONFIG_DRAM_SUN50I_H616_TPR0,
.tpr2 = CONFIG_DRAM_SUN50I_H616_TPR2,
.tpr6 = CONFIG_DRAM_SUN50I_H616_TPR6,
.tpr10 = CONFIG_DRAM_SUN50I_H616_TPR10,
.tpr11 = CONFIG_DRAM_SUN50I_H616_TPR11,
.tpr12 = CONFIG_DRAM_SUN50I_H616_TPR12,

View file

@ -5,3 +5,4 @@ obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o
obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o
obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o
obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o
obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o

View file

@ -0,0 +1,95 @@
/*
* sun50i H616 LPDDR4-2133 timings, as programmed by Allwinner's boot0
* for orangepi zero3 with the H618 and LPDDR4 memory.
*
* (C) Copyright 2023 Mikhail Kalashnikov <iuncuim@gmail.com>
* Based on H6 DDR3 timings:
* (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
void mctl_set_timing_params(const struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
u8 tccd = 4;
u8 tfaw = ns_to_t(40);
u8 trrd = max(ns_to_t(10), 2);
u8 trcd = max(ns_to_t(18), 2);
u8 trc = ns_to_t(65);
u8 txp = max(ns_to_t(8), 2);
u8 trtp = max(ns_to_t(8), 4);
u8 trp = ns_to_t(21);
u8 tras = ns_to_t(42);
u16 trefi = ns_to_t(3904) / 32;
u16 trfc = ns_to_t(280);
u16 txsr = ns_to_t(190);
u8 tmrw = max(ns_to_t(14), 5);
u8 tmrd = tmrw;
u8 tmod = 12;
u8 tcke = max(ns_to_t(15), 2);
u8 tcksrx = max(ns_to_t(2), 2);
u8 tcksre = max(ns_to_t(5), 2);
u8 tckesr = tcke;
u8 trasmax = (trefi * 9) / 32;
u8 txs = 4;
u8 txsdll = 16;
u8 txsabort = 4;
u8 txsfast = 4;
u8 tcl = 10;
u8 tcwl = 5;
u8 t_rdata_en = 17;
u8 tphy_wrlat = 5;
u8 twtp = 24;
u8 twr2rd = max(trrd, (u8)4) + 14;
u8 trd2wr = (ns_to_t(4) + 17) - ns_to_t(1);
/* set DRAM timing */
writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
&mctl_ctl->dramtmg[0]);
writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
&mctl_ctl->dramtmg[2]);
writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
&mctl_ctl->dramtmg[4]);
writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
&mctl_ctl->dramtmg[5]);
/* Value suggested by ZynqMP manual and used by libdram */
writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);
writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,
&mctl_ctl->dramtmg[8]);
writel(0x00020208, &mctl_ctl->dramtmg[9]);
writel(0xE0C05, &mctl_ctl->dramtmg[10]);
writel(0x440C021C, &mctl_ctl->dramtmg[11]);
writel(8, &mctl_ctl->dramtmg[12]);
writel(0xA100002, &mctl_ctl->dramtmg[13]);
writel(txsr, &mctl_ctl->dramtmg[14]);
clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x3f0);
writel(0x01f20000, &mctl_ctl->init[1]);
writel(0x00000d05, &mctl_ctl->init[2]);
writel(0, &mctl_ctl->dfimisc);
writel(0x0034001b, &mctl_ctl->init[3]);
writel(0x00330000, &mctl_ctl->init[4]);
writel(0x00040072, &mctl_ctl->init[6]);
writel(0x00240009, &mctl_ctl->init[7]);
clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
/* Configure DFI timing */
writel(tphy_wrlat | 0x2000000 | (t_rdata_en << 16) | 0x808000,
&mctl_ctl->dfitmg0);
writel(0x100202, &mctl_ctl->dfitmg1);
/* set refresh timing */
writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
}

View file

@ -22,6 +22,7 @@
#define AXP209_I2C_ADDR 0x34
#define AXP305_I2C_ADDR 0x36
#define AXP313_I2C_ADDR 0x36
#define AXP221_CHIP_ADDR 0x68
@ -34,6 +35,8 @@ static int pmic_i2c_address(void)
return AXP152_I2C_ADDR;
if (IS_ENABLED(CONFIG_AXP305_POWER))
return AXP305_I2C_ADDR;
if (IS_ENABLED(CONFIG_AXP313_POWER))
return AXP313_I2C_ADDR;
/* Other AXP2xx and AXP8xx variants */
return AXP209_I2C_ADDR;

View file

@ -236,6 +236,11 @@ M: Stefan Roese <sr@denx.de>
S: Maintained
F: configs/icnova-a20-swac_defconfig
ICnova-A20-ADB4006 BOARD
M: Ludwig Kormann <ludwig.kormann@ict42.de>
S: Maintained
F: configs/icnova-a20-adb4006_defconfig
ITEAD IBOX BOARD
M: Marcus Cooper <codekipper@gmail.com>
S: Maintained

View file

@ -34,6 +34,7 @@
#include <asm/global_data.h>
#include <linux/delay.h>
#include <linux/printk.h>
#include <linux/types.h>
#ifndef CONFIG_ARM64
#include <asm/armv7.h>
#endif
@ -568,7 +569,8 @@ void sunxi_board_init(void)
#if defined CONFIG_AXP152_POWER || defined CONFIG_AXP209_POWER || \
defined CONFIG_AXP221_POWER || defined CONFIG_AXP305_POWER || \
defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER || \
defined CONFIG_AXP313_POWER
power_failed = axp_init();
if (IS_ENABLED(CONFIG_AXP_DISABLE_BOOT_ON_POWERON) && !power_failed) {
@ -581,50 +583,46 @@ void sunxi_board_init(void)
}
}
#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
defined CONFIG_AXP818_POWER
#ifdef CONFIG_AXP_DCDC1_VOLT
power_failed |= axp_set_dcdc1(CONFIG_AXP_DCDC1_VOLT);
power_failed |= axp_set_dcdc5(CONFIG_AXP_DCDC5_VOLT);
#endif
#if !defined(CONFIG_AXP305_POWER)
#ifdef CONFIG_AXP_DCDC2_VOLT
power_failed |= axp_set_dcdc2(CONFIG_AXP_DCDC2_VOLT);
power_failed |= axp_set_dcdc3(CONFIG_AXP_DCDC3_VOLT);
#endif
#if !defined(CONFIG_AXP209_POWER) && !defined(CONFIG_AXP818_POWER)
#ifdef CONFIG_AXP_DCDC4_VOLT
power_failed |= axp_set_dcdc4(CONFIG_AXP_DCDC4_VOLT);
#endif
#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
defined CONFIG_AXP818_POWER
power_failed |= axp_set_dcdc5(CONFIG_AXP_DCDC5_VOLT);
#endif
#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
defined CONFIG_AXP818_POWER
#ifdef CONFIG_AXP_ALDO1_VOLT
power_failed |= axp_set_aldo1(CONFIG_AXP_ALDO1_VOLT);
#endif
#if !defined(CONFIG_AXP305_POWER)
#ifdef CONFIG_AXP_ALDO2_VOLT
power_failed |= axp_set_aldo2(CONFIG_AXP_ALDO2_VOLT);
#endif
#if !defined(CONFIG_AXP152_POWER) && !defined(CONFIG_AXP305_POWER)
#ifdef CONFIG_AXP_ALDO3_VOLT
power_failed |= axp_set_aldo3(CONFIG_AXP_ALDO3_VOLT);
#endif
#ifdef CONFIG_AXP209_POWER
#ifdef CONFIG_AXP_ALDO4_VOLT
power_failed |= axp_set_aldo4(CONFIG_AXP_ALDO4_VOLT);
#endif
#if defined(CONFIG_AXP221_POWER) || defined(CONFIG_AXP809_POWER) || \
defined(CONFIG_AXP818_POWER)
#ifdef CONFIG_AXP_DLDO1_VOLT
power_failed |= axp_set_dldo(1, CONFIG_AXP_DLDO1_VOLT);
power_failed |= axp_set_dldo(2, CONFIG_AXP_DLDO2_VOLT);
#if !defined CONFIG_AXP809_POWER
#endif
#ifdef CONFIG_AXP_DLDO3_VOLT
power_failed |= axp_set_dldo(3, CONFIG_AXP_DLDO3_VOLT);
power_failed |= axp_set_dldo(4, CONFIG_AXP_DLDO4_VOLT);
#endif
#ifdef CONFIG_AXP_ELDO1_VOLT
power_failed |= axp_set_eldo(1, CONFIG_AXP_ELDO1_VOLT);
power_failed |= axp_set_eldo(2, CONFIG_AXP_ELDO2_VOLT);
power_failed |= axp_set_eldo(3, CONFIG_AXP_ELDO3_VOLT);
#endif
#ifdef CONFIG_AXP818_POWER
#ifdef CONFIG_AXP_FLDO1_VOLT
power_failed |= axp_set_fldo(1, CONFIG_AXP_FLDO1_VOLT);
power_failed |= axp_set_fldo(2, CONFIG_AXP_FLDO2_VOLT);
power_failed |= axp_set_fldo(3, CONFIG_AXP_FLDO3_VOLT);
@ -633,7 +631,7 @@ void sunxi_board_init(void)
#if defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
power_failed |= axp_set_sw(IS_ENABLED(CONFIG_AXP_SW_ON));
#endif
#endif
#endif /* CONFIG_AXPxxx_POWER */
printf("DRAM:");
gd->ram_size = sunxi_dram_init();
printf(" %d MiB\n", (int)(gd->ram_size >> 20));
@ -941,7 +939,7 @@ int board_fit_config_name_match(const char *name)
#ifdef CONFIG_PINE64_DT_SELECTION
if (strstr(best_dt_name, "-pine64-plus")) {
/* Differentiate the Pine A64 boards by their DRAM size. */
if ((gd->ram_size == 512 * 1024 * 1024))
if (gd->ram_size == SZ_512M)
best_dt_name = "sun50i-a64-pine64";
}
#endif

View file

@ -0,0 +1,21 @@
CONFIG_ARM=y
CONFIG_ARCH_SUNXI=y
CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-icnova-a20-adb4006"
CONFIG_SPL=y
CONFIG_MACH_SUN7I=y
CONFIG_DRAM_CLK=384
CONFIG_AHCI=y
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
CONFIG_SPL_I2C=y
CONFIG_SCSI_AHCI=y
CONFIG_SYS_64BIT_LBA=y
CONFIG_SYS_I2C_MVTWSI=y
CONFIG_SYS_I2C_SLAVE=0x7f
CONFIG_SYS_I2C_SPEED=400000
CONFIG_ETH_DESIGNWARE=y
CONFIG_MII=y
CONFIG_SUN7I_GMAC=y
CONFIG_AXP_ALDO4_VOLT=2800
CONFIG_SCSI=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_OHCI_HCD=y

View file

@ -27,7 +27,6 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_SOCFPGA) += altera/
obj-$(CONFIG_ARCH_STM32) += stm32/
obj-$(CONFIG_ARCH_STM32MP) += stm32/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_CLK_AT91) += at91/
obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
@ -43,6 +42,7 @@ obj-$(CONFIG_CLK_OWL) += owl/
obj-$(CONFIG_CLK_RENESAS) += renesas/
obj-$(CONFIG_$(SPL_TPL_)CLK_SCMI) += clk_scmi.o
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-$(CONFIG_CLK_SUNXI) += sunxi/
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_CLK_VERSACLOCK) += clk_versaclock.o
obj-$(CONFIG_CLK_VERSAL) += clk_versal.o

View file

@ -25,10 +25,14 @@
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/cpu.h>
#if !CONFIG_IS_ENABLED(DM_MMC)
#include <asm/arch/mmc.h>
#endif
#include <linux/delay.h>
#include <sunxi_gpio.h>
#include "sunxi_mmc.h"
#ifndef CCM_MMC_CTRL_MODE_SEL_NEW
#define CCM_MMC_CTRL_MODE_SEL_NEW 0
#endif
@ -701,13 +705,13 @@ static const struct udevice_id sunxi_mmc_ids[] = {
{ .compatible = "allwinner,sun7i-a20-mmc" },
{ .compatible = "allwinner,sun8i-a83t-emmc" },
{ .compatible = "allwinner,sun9i-a80-mmc" },
{ .compatible = "allwinner,sun20i-d1-mmc" },
{ .compatible = "allwinner,sun50i-a64-mmc" },
{ .compatible = "allwinner,sun50i-a64-emmc" },
{ .compatible = "allwinner,sun50i-h6-mmc" },
{ .compatible = "allwinner,sun50i-h6-emmc" },
{ .compatible = "allwinner,sun50i-a100-mmc" },
{ .compatible = "allwinner,sun50i-a100-emmc" },
{ .compatible = "allwinner,sun20i-d1-mmc" },
{ /* sentinel */ }
};

138
drivers/mmc/sunxi_mmc.h Normal file
View file

@ -0,0 +1,138 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Aaron <leafy.myeh@allwinnertech.com>
*
* MMC register definition for allwinner sunxi platform.
*/
#ifndef _SUNXI_MMC_H
#define _SUNXI_MMC_H
#include <linux/types.h>
struct sunxi_mmc {
u32 gctrl; /* 0x00 global control */
u32 clkcr; /* 0x04 clock control */
u32 timeout; /* 0x08 time out */
u32 width; /* 0x0c bus width */
u32 blksz; /* 0x10 block size */
u32 bytecnt; /* 0x14 byte count */
u32 cmd; /* 0x18 command */
u32 arg; /* 0x1c argument */
u32 resp0; /* 0x20 response 0 */
u32 resp1; /* 0x24 response 1 */
u32 resp2; /* 0x28 response 2 */
u32 resp3; /* 0x2c response 3 */
u32 imask; /* 0x30 interrupt mask */
u32 mint; /* 0x34 masked interrupt status */
u32 rint; /* 0x38 raw interrupt status */
u32 status; /* 0x3c status */
u32 ftrglevel; /* 0x40 FIFO threshold watermark*/
u32 funcsel; /* 0x44 function select */
u32 cbcr; /* 0x48 CIU byte count */
u32 bbcr; /* 0x4c BIU byte count */
u32 dbgc; /* 0x50 debug enable */
u32 res0; /* 0x54 reserved */
u32 a12a; /* 0x58 Auto command 12 argument */
u32 ntsr; /* 0x5c New timing set register */
u32 res1[8];
u32 dmac; /* 0x80 internal DMA control */
u32 dlba; /* 0x84 internal DMA descr list base address */
u32 idst; /* 0x88 internal DMA status */
u32 idie; /* 0x8c internal DMA interrupt enable */
u32 chda; /* 0x90 */
u32 cbda; /* 0x94 */
u32 res2[26];
#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
u32 res3[17];
u32 samp_dl;
u32 res4[46];
#endif
u32 fifo; /* 0x100 / 0x200 FIFO access address */
};
#define SUNXI_MMC_CLK_POWERSAVE (0x1 << 17)
#define SUNXI_MMC_CLK_ENABLE (0x1 << 16)
#define SUNXI_MMC_CLK_DIVIDER_MASK (0xff)
#define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0)
#define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1)
#define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2)
#define SUNXI_MMC_GCTRL_RESET (SUNXI_MMC_GCTRL_SOFT_RESET|\
SUNXI_MMC_GCTRL_FIFO_RESET|\
SUNXI_MMC_GCTRL_DMA_RESET)
#define SUNXI_MMC_GCTRL_DMA_ENABLE (0x1 << 5)
#define SUNXI_MMC_GCTRL_ACCESS_BY_AHB (0x1 << 31)
#define SUNXI_MMC_CMD_RESP_EXPIRE (0x1 << 6)
#define SUNXI_MMC_CMD_LONG_RESPONSE (0x1 << 7)
#define SUNXI_MMC_CMD_CHK_RESPONSE_CRC (0x1 << 8)
#define SUNXI_MMC_CMD_DATA_EXPIRE (0x1 << 9)
#define SUNXI_MMC_CMD_WRITE (0x1 << 10)
#define SUNXI_MMC_CMD_AUTO_STOP (0x1 << 12)
#define SUNXI_MMC_CMD_WAIT_PRE_OVER (0x1 << 13)
#define SUNXI_MMC_CMD_SEND_INIT_SEQ (0x1 << 15)
#define SUNXI_MMC_CMD_UPCLK_ONLY (0x1 << 21)
#define SUNXI_MMC_CMD_START (0x1 << 31)
#define SUNXI_MMC_RINT_RESP_ERROR (0x1 << 1)
#define SUNXI_MMC_RINT_COMMAND_DONE (0x1 << 2)
#define SUNXI_MMC_RINT_DATA_OVER (0x1 << 3)
#define SUNXI_MMC_RINT_TX_DATA_REQUEST (0x1 << 4)
#define SUNXI_MMC_RINT_RX_DATA_REQUEST (0x1 << 5)
#define SUNXI_MMC_RINT_RESP_CRC_ERROR (0x1 << 6)
#define SUNXI_MMC_RINT_DATA_CRC_ERROR (0x1 << 7)
#define SUNXI_MMC_RINT_RESP_TIMEOUT (0x1 << 8)
#define SUNXI_MMC_RINT_DATA_TIMEOUT (0x1 << 9)
#define SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE (0x1 << 10)
#define SUNXI_MMC_RINT_FIFO_RUN_ERROR (0x1 << 11)
#define SUNXI_MMC_RINT_HARD_WARE_LOCKED (0x1 << 12)
#define SUNXI_MMC_RINT_START_BIT_ERROR (0x1 << 13)
#define SUNXI_MMC_RINT_AUTO_COMMAND_DONE (0x1 << 14)
#define SUNXI_MMC_RINT_END_BIT_ERROR (0x1 << 15)
#define SUNXI_MMC_RINT_SDIO_INTERRUPT (0x1 << 16)
#define SUNXI_MMC_RINT_CARD_INSERT (0x1 << 30)
#define SUNXI_MMC_RINT_CARD_REMOVE (0x1 << 31)
#define SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT \
(SUNXI_MMC_RINT_RESP_ERROR | \
SUNXI_MMC_RINT_RESP_CRC_ERROR | \
SUNXI_MMC_RINT_DATA_CRC_ERROR | \
SUNXI_MMC_RINT_RESP_TIMEOUT | \
SUNXI_MMC_RINT_DATA_TIMEOUT | \
SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE | \
SUNXI_MMC_RINT_FIFO_RUN_ERROR | \
SUNXI_MMC_RINT_HARD_WARE_LOCKED | \
SUNXI_MMC_RINT_START_BIT_ERROR | \
SUNXI_MMC_RINT_END_BIT_ERROR) /* 0xbfc2 */
#define SUNXI_MMC_RINT_INTERRUPT_DONE_BIT \
(SUNXI_MMC_RINT_AUTO_COMMAND_DONE | \
SUNXI_MMC_RINT_DATA_OVER | \
SUNXI_MMC_RINT_COMMAND_DONE | \
SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE)
#define SUNXI_MMC_STATUS_RXWL_FLAG (0x1 << 0)
#define SUNXI_MMC_STATUS_TXWL_FLAG (0x1 << 1)
#define SUNXI_MMC_STATUS_FIFO_EMPTY (0x1 << 2)
#define SUNXI_MMC_STATUS_FIFO_FULL (0x1 << 3)
#define SUNXI_MMC_STATUS_CARD_PRESENT (0x1 << 8)
#define SUNXI_MMC_STATUS_CARD_DATA_BUSY (0x1 << 9)
#define SUNXI_MMC_STATUS_DATA_FSM_BUSY (0x1 << 10)
#define SUNXI_MMC_STATUS_FIFO_LEVEL(reg) (((reg) >> 17) & 0x3fff)
#define SUNXI_MMC_NTSR_MODE_SEL_NEW (0x1 << 31)
#define SUNXI_MMC_IDMAC_RESET (0x1 << 0)
#define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1)
#define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7)
#define SUNXI_MMC_IDIE_TXIRQ (0x1 << 0)
#define SUNXI_MMC_IDIE_RXIRQ (0x1 << 1)
#define SUNXI_MMC_COMMON_CLK_GATE (1 << 16)
#define SUNXI_MMC_COMMON_RESET (1 << 18)
#define SUNXI_MMC_CAL_DL_SW_EN (0x1 << 7)
#endif /* _SUNXI_MMC_H */

View file

@ -168,9 +168,7 @@ struct emac_eth_dev {
struct clk ephy_clk;
struct reset_ctl tx_rst;
struct reset_ctl ephy_rst;
#if CONFIG_IS_ENABLED(DM_GPIO)
struct gpio_desc reset_gpio;
#endif
struct udevice *phy_reg;
};
@ -617,7 +615,6 @@ err_tx_clk:
return ret;
}
#if CONFIG_IS_ENABLED(DM_GPIO)
static int sun8i_mdio_reset(struct mii_dev *bus)
{
struct udevice *dev = bus->priv;
@ -649,7 +646,6 @@ static int sun8i_mdio_reset(struct mii_dev *bus)
return 0;
}
#endif
static int sun8i_mdio_init(const char *name, struct udevice *priv)
{
@ -664,9 +660,7 @@ static int sun8i_mdio_init(const char *name, struct udevice *priv)
bus->write = sun8i_mdio_write;
snprintf(bus->name, sizeof(bus->name), name);
bus->priv = (void *)priv;
#if CONFIG_IS_ENABLED(DM_GPIO)
bus->reset = sun8i_mdio_reset;
#endif
return mdio_register(bus);
}
@ -783,9 +777,7 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
const fdt32_t *reg;
int node = dev_of_offset(dev);
int offset = 0;
#if CONFIG_IS_ENABLED(DM_GPIO)
int reset_flags = GPIOD_IS_OUT;
#endif
int ret;
pdata->iobase = dev_read_addr(dev);
@ -872,7 +864,6 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
printf("%s: Invalid RX delay value %d\n", __func__,
sun8i_pdata->rx_delay_ps);
#if CONFIG_IS_ENABLED(DM_GPIO)
if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
"snps,reset-active-low"))
reset_flags |= GPIOD_ACTIVE_LOW;
@ -887,7 +878,6 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
} else if (ret == -ENOENT) {
ret = 0;
}
#endif
return 0;
}

View file

@ -50,7 +50,7 @@ static const char *sunxi_pinctrl_get_pin_name(struct udevice *dev,
uint pin_selector)
{
const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
static char pin_name[sizeof("PN31")];
static char pin_name[sizeof("PN31")] __section(".data");
snprintf(pin_name, sizeof(pin_name), "P%c%d",
pin_selector / SUNXI_GPIOS_PER_BANK + desc->first_bank + 'A',

View file

@ -101,6 +101,15 @@ config AXP305_POWER
Select this to enable support for the axp305 pmic found on most
H616 boards.
config AXP313_POWER
bool "axp313 pmic support"
depends on MACH_SUN50I_H616
select AXP_PMIC_BUS
select CMD_POWEROFF
---help---
Select this to enable support for the AXP313 PMIC found on some
H616 boards.
config AXP809_POWER
bool "axp809 pmic support"
depends on MACH_SUN9I
@ -143,9 +152,10 @@ config AXP_DCDC1_VOLT
config AXP_DCDC2_VOLT
int "axp pmic dcdc2 voltage"
depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER
depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER
default 900 if AXP818_POWER
default 1400 if AXP152_POWER || AXP209_POWER
default 1000 if AXP313_POWER
default 1200 if MACH_SUN6I
default 1100 if MACH_SUN8I
default 0 if MACH_SUN9I
@ -158,13 +168,15 @@ config AXP_DCDC2_VOLT
On A80 boards dcdc2 powers the GPU and can be left off.
On A83T boards dcdc2 is used for VDD-CPUA(cluster 0) and should be 0.9V.
On R40 boards dcdc2 is VDD-CPU and should be 1.1V
On boards using the AXP313 it's often VDD-CPU.
config AXP_DCDC3_VOLT
int "axp pmic dcdc3 voltage"
depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER
depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER
default 900 if AXP809_POWER || AXP818_POWER
default 1500 if AXP152_POWER
default 1250 if AXP209_POWER
default 1100 if AXP313_POWER
default 1100 if MACH_SUN8I_R40
default 1200 if MACH_SUN6I || MACH_SUN8I
---help---
@ -177,10 +189,11 @@ config AXP_DCDC3_VOLT
On A80 boards dcdc3 is used for VDD-CPUA(cluster 0) and should be 0.9V.
On A83T boards dcdc3 is used for VDD-CPUB(cluster 1) and should be 0.9V.
On R40 boards dcdc3 is VDD-SYS and VDD-GPU and should be 1.1V.
On boards using the AXP313 it's often VDD-DRAM and should be 1.1V for LPDDR4.
config AXP_DCDC4_VOLT
int "axp pmic dcdc4 voltage"
depends on AXP152_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP305_POWER
depends on AXP152_POWER || AXP221_POWER || AXP809_POWER || AXP305_POWER
default 1250 if AXP152_POWER
default 1200 if MACH_SUN6I
default 0 if MACH_SUN8I

View file

@ -12,6 +12,7 @@ obj-$(CONFIG_AXP152_POWER) += axp152.o
obj-$(CONFIG_AXP209_POWER) += axp209.o
obj-$(CONFIG_AXP221_POWER) += axp221.o
obj-$(CONFIG_AXP305_POWER) += axp305.o
obj-$(CONFIG_AXP313_POWER) += axp313.o
obj-$(CONFIG_AXP809_POWER) += axp809.o
obj-$(CONFIG_AXP818_POWER) += axp818.o
obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o

134
drivers/power/axp313.c Normal file
View file

@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* AXP313(a) driver
*
* (C) Copyright 2023 Arm Ltd.
*
* Based on axp305.c
* (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
* (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
* (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl>
*/
#include <common.h>
#include <command.h>
#include <errno.h>
#include <asm/arch/pmic_bus.h>
#include <axp_pmic.h>
enum axp313_reg {
AXP313_CHIP_VERSION = 0x03,
AXP313_OUTPUT_CTRL = 0x10,
AXP313_DCDC1_CTRL = 0x13,
AXP313_SHUTDOWN = 0x1a,
};
#define AXP313_CHIP_VERSION_MASK 0xcf
#define AXP313_CHIP_VERSION_AXP1530 0x48
#define AXP313_CHIP_VERSION_AXP313A 0x4b
#define AXP313_CHIP_VERSION_AXP313B 0x4c
#define AXP313_DCDC_SPLIT_OFFSET 71
#define AXP313_DCDC_SPLIT_MVOLT 1200
#define AXP313_POWEROFF BIT(7)
static u8 mvolt_to_cfg(int mvolt, int min, int max, int div)
{
if (mvolt < min)
mvolt = min;
else if (mvolt > max)
mvolt = max;
return (mvolt - min) / div;
}
static int axp_set_dcdc(int dcdc_num, unsigned int mvolt)
{
int ret;
u8 cfg, enable_mask = 1U << (dcdc_num - 1);
int volt_reg = AXP313_DCDC1_CTRL + dcdc_num - 1;
int max_mV;
switch (dcdc_num) {
case 1:
case 2:
max_mV = 1540;
break;
case 3:
/*
* The manual defines a different split point, but tests
* show that it's the same 1200mV as for DCDC1/2.
*/
max_mV = 1840;
break;
default:
return -EINVAL;
}
if (mvolt > AXP313_DCDC_SPLIT_MVOLT)
cfg = AXP313_DCDC_SPLIT_OFFSET + mvolt_to_cfg(mvolt,
AXP313_DCDC_SPLIT_MVOLT + 20, max_mV, 20);
else
cfg = mvolt_to_cfg(mvolt, 500, AXP313_DCDC_SPLIT_MVOLT, 10);
if (mvolt == 0)
return pmic_bus_clrbits(AXP313_OUTPUT_CTRL, enable_mask);
debug("DCDC%d: writing 0x%x to reg 0x%x\n", dcdc_num, cfg, volt_reg);
ret = pmic_bus_write(volt_reg, cfg);
if (ret)
return ret;
return pmic_bus_setbits(AXP313_OUTPUT_CTRL, enable_mask);
}
int axp_set_dcdc2(unsigned int mvolt)
{
return axp_set_dcdc(2, mvolt);
}
int axp_set_dcdc3(unsigned int mvolt)
{
return axp_set_dcdc(3, mvolt);
}
int axp_init(void)
{
u8 axp_chip_id;
int ret;
ret = pmic_bus_init();
if (ret)
return ret;
ret = pmic_bus_read(AXP313_CHIP_VERSION, &axp_chip_id);
if (ret)
return ret;
axp_chip_id &= AXP313_CHIP_VERSION_MASK;
switch (axp_chip_id) {
case AXP313_CHIP_VERSION_AXP1530:
case AXP313_CHIP_VERSION_AXP313A:
case AXP313_CHIP_VERSION_AXP313B:
break;
default:
debug("unknown PMIC: 0x%x\n", axp_chip_id);
return -EINVAL;
}
return ret;
}
#if !CONFIG_IS_ENABLED(ARM_PSCI_FW) && !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF)
int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
pmic_bus_write(AXP313_SHUTDOWN, AXP313_POWEROFF);
/* infinite loop during shutdown */
while (1) {}
/* not reached */
return 0;
}
#endif

View file

@ -87,6 +87,7 @@ static const struct udevice_id axp_pmic_ids[] = {
{ .compatible = "x-powers,axp209", .data = AXP209_ID },
{ .compatible = "x-powers,axp221", .data = AXP221_ID },
{ .compatible = "x-powers,axp223", .data = AXP223_ID },
{ .compatible = "x-powers,axp313a", .data = AXP313_ID },
{ .compatible = "x-powers,axp803", .data = AXP803_ID },
{ .compatible = "x-powers,axp806", .data = AXP806_ID },
{ .compatible = "x-powers,axp809", .data = AXP809_ID },

View file

@ -173,6 +173,22 @@ static const struct axp_regulator_plat axp22x_regulators[] = {
{ }
};
/*
* The "dcdc1" regulator has another range, beyond 1.54V up to 3.4V, in
* steps of 100mV. We cannot model this easily, but also don't need that,
* since it's typically only used for ~1.1V anyway, so just ignore it.
* Also the DCDC3 regulator is described wrongly in the (available) manual,
* experiments show that the split point is at 1200mV, as for DCDC1/2.
*/
static const struct axp_regulator_plat axp313_regulators[] = {
{ "dcdc1", 0x10, BIT(0), 0x13, 0x7f, 500, 1540, 10, 70 },
{ "dcdc2", 0x10, BIT(1), 0x14, 0x7f, 500, 1540, 10, 70 },
{ "dcdc3", 0x10, BIT(2), 0x15, 0x7f, 500, 1840, 10, 70 },
{ "aldo1", 0x10, BIT(3), 0x16, 0x1f, 500, 3500, 100, NA },
{ "dldo1", 0x10, BIT(4), 0x17, 0x1f, 500, 3500, 100, NA },
{ }
};
static const struct axp_regulator_plat axp803_regulators[] = {
{ "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA },
{ "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 },
@ -274,6 +290,7 @@ static const struct axp_regulator_plat *const axp_regulators[] = {
[AXP209_ID] = axp20x_regulators,
[AXP221_ID] = axp22x_regulators,
[AXP223_ID] = axp22x_regulators,
[AXP313_ID] = axp313_regulators,
[AXP803_ID] = axp803_regulators,
[AXP806_ID] = axp806_regulators,
[AXP809_ID] = axp809_regulators,

View file

@ -32,6 +32,7 @@ enum {
AXP209_ID,
AXP221_ID,
AXP223_ID,
AXP313_ID,
AXP803_ID,
AXP806_ID,
AXP809_ID,