aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/adc/Kconfig1
-rw-r--r--drivers/ata/Makefile2
-rw-r--r--drivers/ata/ahci_sunxi.c9
-rw-r--r--drivers/ata/sata.c38
-rw-r--r--drivers/ata/sata_bootdev.c62
-rw-r--r--drivers/clk/sunxi/Kconfig7
-rw-r--r--drivers/clk/sunxi/Makefile1
-rw-r--r--drivers/clk/sunxi/clk_d1.c84
-rw-r--r--drivers/clk/sunxi/clk_sunxi.c5
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/axp_gpio.c1
-rw-r--r--drivers/gpio/sunxi_gpio.c189
-rw-r--r--drivers/i2c/mvtwsi.c3
-rw-r--r--drivers/i2c/sun6i_p2wi.c2
-rw-r--r--drivers/i2c/sun8i_rsb.c2
-rw-r--r--drivers/misc/i2c_eeprom.c8
-rw-r--r--drivers/mmc/sunxi_mmc.c9
-rw-r--r--drivers/net/sun8i_emac.c7
-rw-r--r--drivers/net/sunxi_emac.c38
-rw-r--r--drivers/phy/meson-g12a-usb2.c6
-rw-r--r--drivers/pinctrl/sunxi/Kconfig5
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c47
-rw-r--r--drivers/ram/Kconfig3
-rw-r--r--drivers/ram/Makefile3
-rw-r--r--drivers/ram/sunxi/Kconfig60
-rw-r--r--drivers/ram/sunxi/Makefile3
-rw-r--r--drivers/ram/sunxi/dram_sun20i_d1.c1441
-rw-r--r--drivers/ram/sunxi/dram_sun20i_d1.h73
-rw-r--r--drivers/serial/serial_meson.c1
-rw-r--r--drivers/video/hitachi_tx18d42vm_lcd.c1
-rw-r--r--drivers/video/pwm_backlight.c13
-rw-r--r--drivers/video/simple_panel.c10
-rw-r--r--drivers/video/ssd2828.c1
-rw-r--r--drivers/video/sunxi/sunxi_display.c1
-rw-r--r--drivers/video/sunxi/sunxi_lcd.c1
-rw-r--r--drivers/video/tegra20/tegra-dsi.c8
37 files changed, 2086 insertions, 67 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 74f940a57d..bf73b7718c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_$(SPL_)ALTERA_SDRAM) += ddr/altera/
obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/
obj-$(CONFIG_IMX8ULP_DRAM) += ddr/imx/imx8ulp/
obj-$(CONFIG_ARCH_IMX9) += ddr/imx/imx9/
+obj-$(CONFIG_DRAM_SUN20I_D1) += ram/
obj-$(CONFIG_SPL_DM_RESET) += reset/
obj-$(CONFIG_SPL_MUSB_NEW) += usb/musb-new/
obj-$(CONFIG_SPL_USB_GADGET) += usb/gadget/
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index 4336732dee..a01d73846b 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -66,6 +66,7 @@ config STM32_ADC
config ADC_IMX93
bool "Enable NXP IMX93 ADC driver"
+ depends on ADC
help
This enables basic driver for NXP IMX93 ADC.
It provides:
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6e30180b8b..0b6f91098a 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_SCSI_AHCI) += ahci.o
obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o
obj-$(CONFIG_FSL_SATA) += fsl_sata.o
obj-$(CONFIG_LIBATA) += libata.o
-obj-$(CONFIG_SATA) += sata.o
+obj-$(CONFIG_SATA) += sata.o sata_bootdev.o
obj-$(CONFIG_SATA_CEVA) += sata_ceva.o
obj-$(CONFIG_SATA_MV) += sata_mv.o
obj-$(CONFIG_SATA_SIL) += sata_sil.o
diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c
index 94a3379c53..9064774e66 100644
--- a/drivers/ata/ahci_sunxi.c
+++ b/drivers/ata/ahci_sunxi.c
@@ -7,6 +7,7 @@
#include <asm/io.h>
#include <asm/gpio.h>
#include <linux/delay.h>
+#include <power/regulator.h>
#define AHCI_PHYCS0R 0x00c0
#define AHCI_PHYCS1R 0x00c4
@@ -74,6 +75,7 @@ static int sunxi_ahci_phy_init(u8 *reg_base)
static int sunxi_sata_probe(struct udevice *dev)
{
+ struct udevice *reg_dev;
ulong base;
u8 *reg;
int ret;
@@ -89,6 +91,13 @@ static int sunxi_sata_probe(struct udevice *dev)
debug("%s: Failed to init phy (err=%d)\n", __func__, ret);
return ret;
}
+
+ ret = device_get_supply_regulator(dev, "target-supply", &reg_dev);
+ if (ret == 0) {
+ regulator_set_enable(reg_dev, true);
+ mdelay(500);
+ }
+
ret = ahci_probe_scsi(dev, base);
if (ret) {
debug("%s: Failed to probe (err=%d)\n", __func__, ret);
diff --git a/drivers/ata/sata.c b/drivers/ata/sata.c
index ce3e9b5a40..dcb5fcf476 100644
--- a/drivers/ata/sata.c
+++ b/drivers/ata/sata.c
@@ -15,6 +15,8 @@
#include <dm.h>
#include <part.h>
#include <sata.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
#ifndef CONFIG_AHCI
struct blk_desc sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
@@ -50,6 +52,42 @@ int sata_scan(struct udevice *dev)
return ops->scan(dev);
}
+int sata_rescan(bool verbose)
+{
+ int ret;
+ struct udevice *dev;
+
+ if (verbose)
+ printf("Removing devices on SATA bus...\n");
+
+ blk_unbind_all(UCLASS_AHCI);
+
+ ret = uclass_find_first_device(UCLASS_AHCI, &dev);
+ if (ret || !dev) {
+ printf("Cannot find SATA device (err=%d)\n", ret);
+ return -ENOSYS;
+ }
+
+ ret = device_remove(dev, DM_REMOVE_NORMAL);
+ if (ret) {
+ printf("Cannot remove SATA device '%s' (err=%d)\n", dev->name, ret);
+ return -ENOSYS;
+ }
+
+ if (verbose)
+ printf("Rescanning SATA bus for devices...\n");
+
+ ret = uclass_probe_all(UCLASS_AHCI);
+
+ if (ret == -ENODEV) {
+ if (verbose)
+ printf("No SATA block device found\n");
+ return 0;
+ }
+
+ return ret;
+}
+
#ifndef CONFIG_AHCI
#ifdef CONFIG_PARTITIONS
struct blk_desc *sata_get_dev(int dev)
diff --git a/drivers/ata/sata_bootdev.c b/drivers/ata/sata_bootdev.c
new file mode 100644
index 0000000000..f638493ce0
--- /dev/null
+++ b/drivers/ata/sata_bootdev.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootdev for sata
+ *
+ * Copyright 2023 Tony Dinh <mibodhi@gmail.com>
+ */
+
+#include <common.h>
+#include <ahci.h>
+#include <bootdev.h>
+#include <dm.h>
+#include <init.h>
+#include <sata.h>
+
+static int sata_bootdev_bind(struct udevice *dev)
+{
+ struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+ ucp->prio = BOOTDEVP_4_SCAN_FAST;
+
+ return 0;
+}
+
+static int sata_bootdev_hunt(struct bootdev_hunter *info, bool show)
+{
+ int ret;
+
+ if (IS_ENABLED(CONFIG_PCI)) {
+ ret = pci_init();
+ if (ret)
+ return ret;
+ }
+
+ ret = sata_rescan(true);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct bootdev_ops sata_bootdev_ops = {
+};
+
+static const struct udevice_id sata_bootdev_ids[] = {
+ { .compatible = "u-boot,bootdev-sata" },
+ { }
+};
+
+U_BOOT_DRIVER(sata_bootdev) = {
+ .name = "sata_bootdev",
+ .id = UCLASS_BOOTDEV,
+ .ops = &sata_bootdev_ops,
+ .bind = sata_bootdev_bind,
+ .of_match = sata_bootdev_ids,
+};
+
+BOOTDEV_HUNTER(sata_bootdev_hunter) = {
+ .prio = BOOTDEVP_4_SCAN_FAST,
+ .uclass = UCLASS_AHCI,
+ .hunt = sata_bootdev_hunt,
+ .drv = DM_DRIVER_REF(sata_bootdev),
+};
diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
index bf11fad6ee..8bdc094489 100644
--- a/drivers/clk/sunxi/Kconfig
+++ b/drivers/clk/sunxi/Kconfig
@@ -87,6 +87,13 @@ config CLK_SUN8I_H3
This enables common clock driver support for platforms based
on Allwinner H3/H5 SoC.
+config CLK_SUN20I_D1
+ bool "Clock driver for Allwinner D1"
+ default MACH_SUN8I_R528
+ help
+ This enables common clock driver support for platforms based
+ on Allwinner D1 SoC.
+
config CLK_SUN50I_H6
bool "Clock driver for Allwinner H6"
default MACH_SUN50I_H6
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 895da02ebe..90a277489d 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_CLK_SUN8I_R40) += clk_r40.o
obj-$(CONFIG_CLK_SUN8I_V3S) += clk_v3s.o
obj-$(CONFIG_CLK_SUN9I_A80) += clk_a80.o
obj-$(CONFIG_CLK_SUN8I_H3) += clk_h3.o
+obj-$(CONFIG_CLK_SUN20I_D1) += clk_d1.o
obj-$(CONFIG_CLK_SUN50I_H6) += clk_h6.o
obj-$(CONFIG_CLK_SUN50I_H6_R) += clk_h6_r.o
obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o
diff --git a/drivers/clk/sunxi/clk_d1.c b/drivers/clk/sunxi/clk_d1.c
new file mode 100644
index 0000000000..9dae761de8
--- /dev/null
+++ b/drivers/clk/sunxi/clk_d1.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <clk/sunxi.h>
+#include <dt-bindings/clock/sun20i-d1-ccu.h>
+#include <dt-bindings/reset/sun20i-d1-ccu.h>
+#include <linux/bitops.h>
+
+static struct ccu_clk_gate d1_gates[] = {
+ [CLK_APB0] = GATE_DUMMY,
+
+ [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)),
+ [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)),
+ [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)),
+ [CLK_BUS_UART0] = GATE(0x90c, BIT(0)),
+ [CLK_BUS_UART1] = GATE(0x90c, BIT(1)),
+ [CLK_BUS_UART2] = GATE(0x90c, BIT(2)),
+ [CLK_BUS_UART3] = GATE(0x90c, BIT(3)),
+ [CLK_BUS_UART4] = GATE(0x90c, BIT(4)),
+ [CLK_BUS_UART5] = GATE(0x90c, BIT(5)),
+ [CLK_BUS_I2C0] = GATE(0x91c, BIT(0)),
+ [CLK_BUS_I2C1] = GATE(0x91c, BIT(1)),
+ [CLK_BUS_I2C2] = GATE(0x91c, BIT(2)),
+ [CLK_BUS_I2C3] = GATE(0x91c, BIT(3)),
+ [CLK_SPI0] = GATE(0x940, BIT(31)),
+ [CLK_SPI1] = GATE(0x944, BIT(31)),
+ [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)),
+ [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)),
+
+ [CLK_BUS_EMAC] = GATE(0x97c, BIT(0)),
+
+ [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)),
+ [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)),
+ [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)),
+ [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)),
+ [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)),
+ [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)),
+ [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)),
+ [CLK_BUS_LRADC] = GATE(0xa9c, BIT(0)),
+
+ [CLK_RISCV] = GATE(0xd04, BIT(31)),
+};
+
+static struct ccu_reset d1_resets[] = {
+ [RST_BUS_MMC0] = RESET(0x84c, BIT(16)),
+ [RST_BUS_MMC1] = RESET(0x84c, BIT(17)),
+ [RST_BUS_MMC2] = RESET(0x84c, BIT(18)),
+ [RST_BUS_UART0] = RESET(0x90c, BIT(16)),
+ [RST_BUS_UART1] = RESET(0x90c, BIT(17)),
+ [RST_BUS_UART2] = RESET(0x90c, BIT(18)),
+ [RST_BUS_UART3] = RESET(0x90c, BIT(19)),
+ [RST_BUS_UART4] = RESET(0x90c, BIT(20)),
+ [RST_BUS_UART5] = RESET(0x90c, BIT(21)),
+ [RST_BUS_I2C0] = RESET(0x91c, BIT(16)),
+ [RST_BUS_I2C1] = RESET(0x91c, BIT(17)),
+ [RST_BUS_I2C2] = RESET(0x91c, BIT(18)),
+ [RST_BUS_I2C3] = RESET(0x91c, BIT(19)),
+ [RST_BUS_SPI0] = RESET(0x96c, BIT(16)),
+ [RST_BUS_SPI1] = RESET(0x96c, BIT(17)),
+
+ [RST_BUS_EMAC] = RESET(0x97c, BIT(16)),
+
+ [RST_USB_PHY0] = RESET(0xa70, BIT(30)),
+ [RST_USB_PHY1] = RESET(0xa74, BIT(30)),
+ [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)),
+ [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)),
+ [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)),
+ [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)),
+ [RST_BUS_OTG] = RESET(0xa8c, BIT(24)),
+ [RST_BUS_LRADC] = RESET(0xa9c, BIT(16)),
+};
+
+const struct ccu_desc d1_ccu_desc = {
+ .gates = d1_gates,
+ .resets = d1_resets,
+ .num_gates = ARRAY_SIZE(d1_gates),
+ .num_resets = ARRAY_SIZE(d1_resets),
+};
diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
index ec02a2d037..1782cffc40 100644
--- a/drivers/clk/sunxi/clk_sunxi.c
+++ b/drivers/clk/sunxi/clk_sunxi.c
@@ -118,6 +118,7 @@ extern const struct ccu_desc a64_ccu_desc;
extern const struct ccu_desc a80_ccu_desc;
extern const struct ccu_desc a80_mmc_clk_desc;
extern const struct ccu_desc a83t_ccu_desc;
+extern const struct ccu_desc d1_ccu_desc;
extern const struct ccu_desc f1c100s_ccu_desc;
extern const struct ccu_desc h3_ccu_desc;
extern const struct ccu_desc h6_ccu_desc;
@@ -195,6 +196,10 @@ static const struct udevice_id sunxi_clk_ids[] = {
{ .compatible = "allwinner,sun50i-h5-ccu",
.data = (ulong)&h3_ccu_desc },
#endif
+#ifdef CONFIG_CLK_SUN20I_D1
+ { .compatible = "allwinner,sun20i-d1-ccu",
+ .data = (ulong)&d1_ccu_desc },
+#endif
#ifdef CONFIG_CLK_SUN50I_H6
{ .compatible = "allwinner,sun50i-h6-ccu",
.data = (ulong)&h6_ccu_desc },
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 74baa98d3c..ba42b0768e 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -372,6 +372,13 @@ config SUNXI_GPIO
help
Support the GPIO device in Allwinner SoCs.
+config SUNXI_NEW_PINCTRL
+ bool
+ depends on SUNXI_GPIO
+ ---help---
+ The Allwinner D1 and other new SoCs use a different register map
+ for the GPIO block, which we need to know about in the SPL.
+
config XILINX_GPIO
bool "Xilinx GPIO driver"
depends on DM_GPIO
diff --git a/drivers/gpio/axp_gpio.c b/drivers/gpio/axp_gpio.c
index 49672193ff..af6631697f 100644
--- a/drivers/gpio/axp_gpio.c
+++ b/drivers/gpio/axp_gpio.c
@@ -14,6 +14,7 @@
#include <dm/lists.h>
#include <dm/root.h>
#include <errno.h>
+#include <sunxi_gpio.h>
static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val);
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index f0b42e4fdb..e4463a223f 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -17,37 +17,165 @@
#include <asm/io.h>
#include <asm/gpio.h>
#include <dt-bindings/gpio/gpio.h>
+#include <sunxi_gpio.h>
-#if !CONFIG_IS_ENABLED(DM_GPIO)
-static int sunxi_gpio_output(u32 pin, u32 val)
+/*
+ * =======================================================================
+ * Low level GPIO/pin controller access functions, to be shared by non-DM
+ * SPL code and the DM pinctrl/GPIO drivers.
+ * The functions ending in "bank" take a base pointer to a GPIO bank, and
+ * the pin offset is relative to that bank.
+ * The functions without "bank" in their name take a linear GPIO number,
+ * covering all ports, and starting at 0 for PortA.
+ * =======================================================================
+ */
+
+#define GPIO_BANK(pin) ((pin) >> 5)
+#define GPIO_NUM(pin) ((pin) & 0x1f)
+
+#define GPIO_CFG_REG_OFFSET 0x00
+#define GPIO_CFG_INDEX(pin) (((pin) & 0x1f) >> 3)
+#define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1f) & 0x7) << 2)
+
+#define GPIO_DAT_REG_OFFSET 0x10
+
+#define GPIO_DRV_REG_OFFSET 0x14
+
+/* Newer SoCs use a slightly different register layout */
+#ifdef CONFIG_SUNXI_NEW_PINCTRL
+/* pin drive strength: 4 bits per pin */
+#define GPIO_DRV_INDEX(pin) ((pin) / 8)
+#define GPIO_DRV_OFFSET(pin) (((pin) % 8) * 4)
+
+#define GPIO_PULL_REG_OFFSET 0x24
+
+#else /* older generation pin controllers */
+/* pin drive strength: 2 bits per pin */
+#define GPIO_DRV_INDEX(pin) ((pin) / 16)
+#define GPIO_DRV_OFFSET(pin) (((pin) % 16) * 2)
+
+#define GPIO_PULL_REG_OFFSET 0x1c
+#endif
+
+#define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4)
+#define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
+
+static void* BANK_TO_GPIO(int bank)
+{
+ void *pio_base;
+
+ if (bank < SUNXI_GPIO_L) {
+ pio_base = (void *)(uintptr_t)SUNXI_PIO_BASE;
+ } else {
+ pio_base = (void *)(uintptr_t)SUNXI_R_PIO_BASE;
+ bank -= SUNXI_GPIO_L;
+ }
+
+ return pio_base + bank * SUNXI_PINCTRL_BANK_SIZE;
+}
+
+void sunxi_gpio_set_cfgbank(void *bank_base, int pin_offset, u32 val)
+{
+ u32 index = GPIO_CFG_INDEX(pin_offset);
+ u32 offset = GPIO_CFG_OFFSET(pin_offset);
+
+ clrsetbits_le32(bank_base + GPIO_CFG_REG_OFFSET + index * 4,
+ 0xfU << offset, val << offset);
+}
+
+void sunxi_gpio_set_cfgpin(u32 pin, u32 val)
{
- u32 dat;
u32 bank = GPIO_BANK(pin);
- u32 num = GPIO_NUM(pin);
- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
+ void *pio = BANK_TO_GPIO(bank);
- dat = readl(&pio->dat);
- if (val)
- dat |= 0x1 << num;
- else
- dat &= ~(0x1 << num);
+ sunxi_gpio_set_cfgbank(pio, GPIO_NUM(pin), val);
+}
+
+int sunxi_gpio_get_cfgbank(void *bank_base, int pin_offset)
+{
+ u32 index = GPIO_CFG_INDEX(pin_offset);
+ u32 offset = GPIO_CFG_OFFSET(pin_offset);
+ u32 cfg;
- writel(dat, &pio->dat);
+ cfg = readl(bank_base + GPIO_CFG_REG_OFFSET + index * 4);
+ cfg >>= offset;
- return 0;
+ return cfg & 0xf;
+}
+
+int sunxi_gpio_get_cfgpin(u32 pin)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *bank_base = BANK_TO_GPIO(bank);
+
+ return sunxi_gpio_get_cfgbank(bank_base, GPIO_NUM(pin));
+}
+
+static void sunxi_gpio_set_value_bank(void *bank_base, int pin, bool set)
+{
+ u32 mask = 1U << pin;
+
+ clrsetbits_le32(bank_base + GPIO_DAT_REG_OFFSET,
+ set ? 0 : mask, set ? mask : 0);
+}
+
+static int sunxi_gpio_get_value_bank(void *bank_base, int pin)
+{
+ return !!(readl(bank_base + GPIO_DAT_REG_OFFSET) & (1U << pin));
+}
+
+void sunxi_gpio_set_drv(u32 pin, u32 val)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *bank_base = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_drv_bank(bank_base, GPIO_NUM(pin), val);
+}
+
+void sunxi_gpio_set_drv_bank(void *bank_base, u32 pin_offset, u32 val)
+{
+ u32 index = GPIO_DRV_INDEX(pin_offset);
+ u32 offset = GPIO_DRV_OFFSET(pin_offset);
+
+ clrsetbits_le32(bank_base + GPIO_DRV_REG_OFFSET + index * 4,
+ 0x3U << offset, val << offset);
+}
+
+void sunxi_gpio_set_pull(u32 pin, u32 val)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *bank_base = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_pull_bank(bank_base, GPIO_NUM(pin), val);
}
-static int sunxi_gpio_input(u32 pin)
+void sunxi_gpio_set_pull_bank(void *bank_base, int pin_offset, u32 val)
+{
+ u32 index = GPIO_PULL_INDEX(pin_offset);
+ u32 offset = GPIO_PULL_OFFSET(pin_offset);
+
+ clrsetbits_le32(bank_base + GPIO_PULL_REG_OFFSET + index * 4,
+ 0x3U << offset, val << offset);
+}
+
+
+/* =========== Non-DM code, used by the SPL. ============ */
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+static void sunxi_gpio_set_value(u32 pin, bool set)
{
- u32 dat;
u32 bank = GPIO_BANK(pin);
- u32 num = GPIO_NUM(pin);
- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
+ void *pio = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_value_bank(pio, GPIO_NUM(pin), set);
+}
- dat = readl(&pio->dat);
- dat >>= num;
+static int sunxi_gpio_get_value(u32 pin)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *pio = BANK_TO_GPIO(bank);
- return dat & 0x1;
+ return sunxi_gpio_get_value_bank(pio, GPIO_NUM(pin));
}
int gpio_request(unsigned gpio, const char *label)
@@ -70,18 +198,21 @@ int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
{
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
+ sunxi_gpio_set_value(gpio, value);
- return sunxi_gpio_output(gpio, value);
+ return 0;
}
int gpio_get_value(unsigned gpio)
{
- return sunxi_gpio_input(gpio);
+ return sunxi_gpio_get_value(gpio);
}
int gpio_set_value(unsigned gpio, int value)
{
- return sunxi_gpio_output(gpio, value);
+ sunxi_gpio_set_value(gpio, value);
+
+ return 0;
}
int sunxi_name_to_gpio(const char *name)
@@ -106,7 +237,9 @@ int sunxi_name_to_gpio(const char *name)
return -1;
return group * 32 + pin;
}
-#endif /* DM_GPIO */
+#endif /* !DM_GPIO */
+
+/* =========== DM code, used by U-Boot proper. ============ */
#if CONFIG_IS_ENABLED(DM_GPIO)
/* TODO(sjg@chromium.org): Remove this function and use device tree */
@@ -131,13 +264,8 @@ int sunxi_name_to_gpio(const char *name)
static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct sunxi_gpio_plat *plat = dev_get_plat(dev);
- u32 num = GPIO_NUM(offset);
- unsigned dat;
-
- dat = readl(&plat->regs->dat);
- dat >>= num;
- return dat & 0x1;
+ return sunxi_gpio_get_value_bank(plat->regs, offset);
}
static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset)
@@ -175,9 +303,8 @@ static int sunxi_gpio_set_flags(struct udevice *dev, unsigned int offset,
if (flags & GPIOD_IS_OUT) {
u32 value = !!(flags & GPIOD_IS_OUT_ACTIVE);
- u32 num = GPIO_NUM(offset);
- clrsetbits_le32(&plat->regs->dat, 1 << num, value << num);
+ sunxi_gpio_set_value_bank(plat->regs, offset, value);
sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT);
} else if (flags & GPIOD_IS_IN) {
u32 pull = 0;
diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c
index 14cdb0f663..c38330f758 100644
--- a/drivers/i2c/mvtwsi.c
+++ b/drivers/i2c/mvtwsi.c
@@ -124,7 +124,8 @@ enum mvtwsi_ctrl_register_fields {
* on other platforms, it is a normal r/w bit, which is cleared by writing 0.
*/
-#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
+#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || \
+ defined(CONFIG_SUNXI_GEN_NCAT2)
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000008
#else
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000000
diff --git a/drivers/i2c/sun6i_p2wi.c b/drivers/i2c/sun6i_p2wi.c
index d221323295..b8e07a533c 100644
--- a/drivers/i2c/sun6i_p2wi.c
+++ b/drivers/i2c/sun6i_p2wi.c
@@ -20,10 +20,10 @@
#include <errno.h>
#include <i2c.h>
#include <reset.h>
+#include <sunxi_gpio.h>
#include <time.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/sys_proto.h>
diff --git a/drivers/i2c/sun8i_rsb.c b/drivers/i2c/sun8i_rsb.c
index 47fa05b6d1..f36f2c7afa 100644
--- a/drivers/i2c/sun8i_rsb.c
+++ b/drivers/i2c/sun8i_rsb.c
@@ -14,10 +14,10 @@
#include <dm.h>
#include <errno.h>
#include <i2c.h>
+#include <sunxi_gpio.h>
#include <reset.h>
#include <time.h>
#include <asm/arch/cpu.h>
-#include <asm/arch/gpio.h>
#include <asm/arch/prcm.h>
#include <asm/arch/rsb.h>
diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c
index bdd7e018cc..e1d0b8f918 100644
--- a/drivers/misc/i2c_eeprom.c
+++ b/drivers/misc/i2c_eeprom.c
@@ -227,6 +227,13 @@ static const struct i2c_eeprom_drv_data atmel24c32_data = {
.offset_len = 2,
};
+static const struct i2c_eeprom_drv_data atmel24c32d_wlp_data = {
+ .size = 32,
+ .pagesize = 32,
+ .addr_offset_mask = 0,
+ .offset_len = 2,
+};
+
static const struct i2c_eeprom_drv_data atmel24c64_data = {
.size = 8192,
.pagesize = 32,
@@ -266,6 +273,7 @@ static const struct udevice_id i2c_eeprom_std_ids[] = {
{ .compatible = "atmel,24c16a", (ulong)&atmel24c16a_data },
{ .compatible = "atmel,24mac402", (ulong)&atmel24mac402_data },
{ .compatible = "atmel,24c32", (ulong)&atmel24c32_data },
+ { .compatible = "atmel,24c32d-wl", (ulong)&atmel24c32d_wlp_data },
{ .compatible = "atmel,24c64", (ulong)&atmel24c64_data },
{ .compatible = "atmel,24c128", (ulong)&atmel24c128_data },
{ .compatible = "atmel,24c256", (ulong)&atmel24c256_data },
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 23bc7da917..4d6351bf27 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -27,6 +27,7 @@
#include <asm/arch/cpu.h>
#include <asm/arch/mmc.h>
#include <linux/delay.h>
+#include <sunxi_gpio.h>
#ifndef CCM_MMC_CTRL_MODE_SEL_NEW
#define CCM_MMC_CTRL_MODE_SEL_NEW 0
@@ -56,6 +57,7 @@ static bool sunxi_mmc_can_calibrate(void)
return IS_ENABLED(CONFIG_MACH_SUN50I) ||
IS_ENABLED(CONFIG_MACH_SUN50I_H5) ||
IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
+ IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) ||
IS_ENABLED(CONFIG_MACH_SUN8I_R40);
}
@@ -190,7 +192,7 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
writel(rval, &priv->reg->clkcr);
-#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
+#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
/* A64 supports calibration of delays on MMC controller and we
* have to set delay of zero before starting calibration.
* Allwinner BSP driver sets a delay only in the case of
@@ -543,7 +545,7 @@ struct mmc *sunxi_mmc_init(int sdc_no)
/* config ahb clock */
debug("init mmc %d clock and io\n", sdc_no);
-#if !defined(CONFIG_SUN50I_GEN_H6)
+#if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2)
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
#ifdef CONFIG_SUNXI_GEN_SUN6I
@@ -618,7 +620,7 @@ static unsigned get_mclk_offset(void)
if (IS_ENABLED(CONFIG_MACH_SUN9I_A80))
return 0x410;
- if (IS_ENABLED(CONFIG_SUN50I_GEN_H6))
+ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
return 0x830;
return 0x88;
@@ -705,6 +707,7 @@ static const struct udevice_id sunxi_mmc_ids[] = {
{ .compatible = "allwinner,sun50i-h6-emmc" },
{ .compatible = "allwinner,sun50i-a100-mmc" },
{ .compatible = "allwinner,sun50i-a100-emmc" },
+ { .compatible = "allwinner,sun20i-d1-mmc" },
{ /* sentinel */ }
};
diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
index 04c3274fbe..4ba9ee1529 100644
--- a/drivers/net/sun8i_emac.c
+++ b/drivers/net/sun8i_emac.c
@@ -29,6 +29,7 @@
#include <net.h>
#include <reset.h>
#include <wait_bit.h>
+#include <power/regulator.h>
#define MDIO_CMD_MII_BUSY BIT(0)
#define MDIO_CMD_MII_WRITE BIT(1)
@@ -170,6 +171,7 @@ struct emac_eth_dev {
#if CONFIG_IS_ENABLED(DM_GPIO)
struct gpio_desc reset_gpio;
#endif
+ struct udevice *phy_reg;
};
@@ -720,6 +722,9 @@ static int sun8i_emac_eth_probe(struct udevice *dev)
sun8i_emac_set_syscon(sun8i_pdata, priv);
+ if (priv->phy_reg)
+ regulator_set_enable(priv->phy_reg, true);
+
sun8i_mdio_init(dev->name, dev);
priv->bus = miiphy_get_dev_by_name(dev->name);
@@ -829,6 +834,8 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
priv->sysctl_reg = (void *)syscon_base + priv->variant->syscon_offset;
+ device_get_supply_regulator(dev, "phy-supply", &priv->phy_reg);
+
pdata->phy_interface = -1;
priv->phyaddr = -1;
priv->use_internal_phy = false;
diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c
index 4c90d4b498..f546ad1fe8 100644
--- a/drivers/net/sunxi_emac.c
+++ b/drivers/net/sunxi_emac.c
@@ -17,6 +17,7 @@
#include <net.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
+#include <power/regulator.h>
/* EMAC register */
struct emac_regs {
@@ -165,6 +166,7 @@ struct emac_eth_dev {
struct phy_device *phydev;
int link_printed;
uchar rx_buf[EMAC_RX_BUFSIZE];
+ struct udevice *phy_reg;
};
struct emac_rxhdr {
@@ -572,6 +574,9 @@ static int sunxi_emac_eth_probe(struct udevice *dev)
if (ret)
return ret;
+ if (priv->phy_reg)
+ regulator_set_enable(priv->phy_reg, true);
+
return sunxi_emac_init_phy(priv, dev);
}
@@ -585,9 +590,42 @@ static const struct eth_ops sunxi_emac_eth_ops = {
static int sunxi_emac_eth_of_to_plat(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_plat(dev);
+ struct emac_eth_dev *priv = dev_get_priv(dev);
+ struct ofnode_phandle_args args;
+ ofnode phy_node, mdio_node;
+ int ret;
pdata->iobase = dev_read_addr(dev);
+ phy_node = dev_get_phy_node(dev);
+ if (!ofnode_valid(phy_node)) {
+ dev_err(dev, "failed to get PHY node\n");
+ return -ENOENT;
+ }
+ /*
+ * The PHY regulator is in the MDIO node, not the EMAC or PHY node.
+ * U-Boot does not have (and does not need) a device driver for the
+ * MDIO device, so just "pass through" that DT node to get to the
+ * regulator phandle.
+ * The PHY regulator is optional, though: ignore if we cannot find
+ * a phy-supply property.
+ */
+ mdio_node = ofnode_get_parent(phy_node);
+ ret= ofnode_parse_phandle_with_args(mdio_node, "phy-supply", NULL, 0, 0,
+ &args);
+ if (ret && ret != -ENOENT) {
+ dev_err(dev, "failed to get PHY supply node\n");
+ return ret;
+ }
+ if (!ret) {
+ ret = uclass_get_device_by_ofnode(UCLASS_REGULATOR, args.node,
+ &priv->phy_reg);
+ if (ret) {
+ dev_err(dev, "failed to get PHY regulator node\n");
+ return ret;
+ }
+ }
+
return 0;
}
diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c
index 4ba3992bda..3958d2404b 100644
--- a/drivers/phy/meson-g12a-usb2.c
+++ b/drivers/phy/meson-g12a-usb2.c
@@ -328,12 +328,12 @@ int meson_g12a_usb2_phy_probe(struct udevice *dev)
#if CONFIG_IS_ENABLED(POWER_DOMAIN)
ret = power_domain_get(dev, &priv->pwrdm);
- if (ret < 0 && ret != -ENODEV) {
- pr_err("failed to get power domain\n");
+ if (ret < 0 && ret != -ENODEV && ret != -ENOENT) {
+ pr_err("failed to get power domain : %d\n", ret);
return ret;
}
- if (ret != -ENODEV) {
+ if (ret != -ENODEV && ret != -ENOENT) {
ret = power_domain_on(&priv->pwrdm);
if (ret < 0) {
pr_err("failed to enable power domain\n");
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
index 77da90836b..cbd6179598 100644
--- a/drivers/pinctrl/sunxi/Kconfig
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -124,4 +124,9 @@ config PINCTRL_SUN50I_H616_R
default MACH_SUN50I_H616
select PINCTRL_SUNXI
+config PINCTRL_SUN20I_D1
+ bool "Support for the Allwinner D1/R528 PIO"
+ default MACH_SUN8I_R528
+ select PINCTRL_SUNXI
+
endif
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index e510218090..bdf6360f17 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -7,6 +7,7 @@
#include <dm/pinctrl.h>
#include <errno.h>
#include <malloc.h>
+#include <sunxi_gpio.h>
#include <asm/gpio.h>
@@ -35,7 +36,7 @@ struct sunxi_pinctrl_desc {
};
struct sunxi_pinctrl_plat {
- struct sunxi_gpio __iomem *base;
+ void __iomem *base;
};
static int sunxi_pinctrl_get_pins_count(struct udevice *dev)
@@ -86,8 +87,8 @@ static int sunxi_pinctrl_pinmux_set(struct udevice *dev, uint pin_selector,
sunxi_pinctrl_get_function_name(dev, func_selector),
desc->functions[func_selector].mux);
- sunxi_gpio_set_cfgbank(plat->base + bank, pin,
- desc->functions[func_selector].mux);
+ sunxi_gpio_set_cfgbank(plat->base + bank * SUNXI_PINCTRL_BANK_SIZE,
+ pin, desc->functions[func_selector].mux);
return 0;
}
@@ -102,7 +103,7 @@ static const struct pinconf_param sunxi_pinctrl_pinconf_params[] = {
static int sunxi_pinctrl_pinconf_set_pull(struct sunxi_pinctrl_plat *plat,
uint bank, uint pin, uint bias)
{
- struct sunxi_gpio *regs = &plat->base[bank];
+ void *regs = plat->base + bank * SUNXI_PINCTRL_BANK_SIZE;
sunxi_gpio_set_pull_bank(regs, pin, bias);
@@ -112,7 +113,7 @@ static int sunxi_pinctrl_pinconf_set_pull(struct sunxi_pinctrl_plat *plat,
static int sunxi_pinctrl_pinconf_set_drive(struct sunxi_pinctrl_plat *plat,
uint bank, uint pin, uint drive)
{
- struct sunxi_gpio *regs = &plat->base[bank];
+ void *regs = plat->base + bank * SUNXI_PINCTRL_BANK_SIZE;
if (drive < 10 || drive > 40)
return -EINVAL;
@@ -148,7 +149,7 @@ static int sunxi_pinctrl_get_pin_muxing(struct udevice *dev, uint pin_selector,
struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
int bank = pin_selector / SUNXI_GPIOS_PER_BANK;
int pin = pin_selector % SUNXI_GPIOS_PER_BANK;
- int mux = sunxi_gpio_get_cfgbank(plat->base + bank, pin);
+ int mux = sunxi_gpio_get_cfgbank(plat->base + bank * SUNXI_PINCTRL_BANK_SIZE, pin);
switch (mux) {
case SUNXI_GPIO_INPUT:
@@ -206,7 +207,7 @@ static int sunxi_pinctrl_bind(struct udevice *dev)
if (!gpio_plat)
return -ENOMEM;
- gpio_plat->regs = plat->base + i;
+ gpio_plat->regs = plat->base + i * SUNXI_PINCTRL_BANK_SIZE;
gpio_plat->bank_name[0] = 'P';
gpio_plat->bank_name[1] = 'A' + desc->first_bank + i;
gpio_plat->bank_name[2] = '\0';
@@ -597,6 +598,32 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun9i_a80_r_pinctrl_desc =
.num_banks = 3,
};
+static const struct sunxi_pinctrl_function sun20i_d1_pinctrl_functions[] = {
+ { "emac", 8 }, /* PE0-PE15 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 4 }, /* PB10-PB11 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC2-PC7 */
+ { "spi0", 2 }, /* PC2-PC7 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2,PF4 */
+#else
+ { "uart0", 6 }, /* PB0-PB1, PB8-PB9, PE2-PE3 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 7 }, /* PB0-PB1 */
+ { "uart3", 7 }, /* PB6-PB7 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun20i_d1_pinctrl_desc = {
+ .functions = sun20i_d1_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun20i_d1_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 7,
+};
+
static const struct sunxi_pinctrl_function sun50i_a64_pinctrl_functions[] = {
{ "emac", 4 }, /* PD8-PD23 */
{ "gpio_in", 0 },
@@ -862,6 +889,12 @@ static const struct udevice_id sunxi_pinctrl_ids[] = {
.data = (ulong)&sun9i_a80_r_pinctrl_desc,
},
#endif
+#ifdef CONFIG_PINCTRL_SUN20I_D1
+ {
+ .compatible = "allwinner,sun20i-d1-pinctrl",
+ .data = (ulong)&sun20i_d1_pinctrl_desc,
+ },
+#endif
#ifdef CONFIG_PINCTRL_SUN50I_A64
{
.compatible = "allwinner,sun50i-a64-pinctrl",
diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index bf99964577..5b07e92030 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -109,8 +109,9 @@ config IMXRT_SDRAM
source "drivers/ram/aspeed/Kconfig"
source "drivers/ram/cadence/Kconfig"
+source "drivers/ram/octeon/Kconfig"
source "drivers/ram/rockchip/Kconfig"
source "drivers/ram/sifive/Kconfig"
source "drivers/ram/stm32mp1/Kconfig"
-source "drivers/ram/octeon/Kconfig"
source "drivers/ram/starfive/Kconfig"
+source "drivers/ram/sunxi/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index 6eb1a24135..985990ab5a 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -23,6 +23,9 @@ obj-$(CONFIG_RAM_SIFIVE) += sifive/
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_STARFIVE_DDR) += starfive/
endif
+
+obj-$(CONFIG_DRAM_SUN20I_D1) += sunxi/
+
obj-$(CONFIG_ARCH_OCTEON) += octeon/
obj-$(CONFIG_ARCH_RMOBILE) += renesas/
diff --git a/drivers/ram/sunxi/Kconfig b/drivers/ram/sunxi/Kconfig
new file mode 100644
index 0000000000..1775cb0d78
--- /dev/null
+++ b/drivers/ram/sunxi/Kconfig
@@ -0,0 +1,60 @@
+config DRAM_SUN20I_D1
+ bool
+ depends on ARCH_SUNXI
+ help
+ This enables support for the DRAM controller driver covering
+ the Allwinner D1/R528/T113s SoCs.
+
+if DRAM_SUN20I_D1
+
+config DRAM_SUNXI_ODT_EN
+ hex "DRAM ODT EN parameter"
+ help
+ ODT EN value from vendor DRAM settings.
+
+config DRAM_SUNXI_TPR0
+ hex "DRAM TPR0 parameter"
+ help
+ TPR0 value from vendor DRAM settings.
+
+config DRAM_SUNXI_TPR11
+ hex "DRAM TPR11 parameter"
+ help
+ TPR11 value from vendor DRAM settings.
+
+config DRAM_SUNXI_TPR12
+ hex "DRAM TPR12 parameter"
+ help
+ TPR12 value from vendor DRAM settings.
+
+config DRAM_SUNXI_TPR13
+ hex "DRAM TPR13 parameter"
+ help
+ TPR13 value from vendor DRAM settings. It tells which features
+ should be configured.
+
+choice
+ prompt "DRAM chip type"
+ default SUNXI_DRAM_TYPE_DDR3 if DRAM_SUN20I_D1
+
+config SUNXI_DRAM_TYPE_DDR2
+ bool "DDR2 chips"
+
+config SUNXI_DRAM_TYPE_DDR3
+ bool "DDR3 chips"
+
+config SUNXI_DRAM_TYPE_LPDDR2
+ bool "LPDDR2 chips"
+
+config SUNXI_DRAM_TYPE_LPDDR3
+ bool "LPDDR3 chips"
+endchoice
+
+config SUNXI_DRAM_TYPE
+ int
+ default 2 if SUNXI_DRAM_TYPE_DDR2
+ default 3 if SUNXI_DRAM_TYPE_DDR3
+ default 6 if SUNXI_DRAM_TYPE_LPDDR2
+ default 7 if SUNXI_DRAM_TYPE_LPDDR3
+
+endif
diff --git a/drivers/ram/sunxi/Makefile b/drivers/ram/sunxi/Makefile
new file mode 100644
index 0000000000..86ea0b9ae9
--- /dev/null
+++ b/drivers/ram/sunxi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_DRAM_SUN20I_D1) += dram_sun20i_d1.o
diff --git a/drivers/ram/sunxi/dram_sun20i_d1.c b/drivers/ram/sunxi/dram_sun20i_d1.c
new file mode 100644
index 0000000000..38379281d7
--- /dev/null
+++ b/drivers/ram/sunxi/dram_sun20i_d1.c
@@ -0,0 +1,1441 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Allwinner D1/D1s/R528/T113-sx DRAM initialisation
+ *
+ * As usual there is no documentation for the memory controller or PHY IP
+ * used here. The baseline of this code was lifted from awboot[1], which
+ * seems to be based on some form of de-compilation of some original Allwinner
+ * code bits (with a GPL2 license tag from the very beginning).
+ * This version here is a reworked version, to match the U-Boot coding style
+ * and style of the other Allwinner DRAM drivers.
+ *
+ * [1] https://github.com/szemzoa/awboot.git
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#ifdef CONFIG_RAM
+ #include <dm.h>
+ #include <ram.h>
+#endif
+#include <linux/delay.h>
+
+#include "dram_sun20i_d1.h"
+
+#ifndef SUNXI_SID_BASE
+#define SUNXI_SID_BASE 0x3006200
+#endif
+
+#ifndef SUNXI_CCM_BASE
+#define SUNXI_CCM_BASE 0x2001000
+#endif
+
+static void sid_read_ldoB_cal(const dram_para_t *para)
+{
+ uint32_t reg;
+
+ reg = (readl(SUNXI_SID_BASE + 0x1c) & 0xff00) >> 8;
+
+ if (reg == 0)
+ return;
+
+ switch (para->dram_type) {
+ case SUNXI_DRAM_TYPE_DDR2:
+ break;
+ case SUNXI_DRAM_TYPE_DDR3:
+ if (reg > 0x20)
+ reg -= 0x16;
+ break;
+ default:
+ reg = 0;
+ break;
+ }
+
+ clrsetbits_le32(0x3000150, 0xff00, reg << 8);
+}
+
+static void dram_voltage_set(const dram_para_t *para)
+{
+ int vol;
+
+ switch (para->dram_type) {
+ case SUNXI_DRAM_TYPE_DDR2:
+ vol = 47;
+ break;
+ case SUNXI_DRAM_TYPE_DDR3:
+ vol = 25;
+ break;
+ default:
+ vol = 0;
+ break;
+ }
+
+ clrsetbits_le32(0x3000150, 0x20ff00, vol << 8);
+
+ udelay(1);
+
+ sid_read_ldoB_cal(para);
+}
+
+static void dram_enable_all_master(void)
+{
+ writel(~0, 0x3102020);
+ writel(0xff, 0x3102024);
+ writel(0xffff, 0x3102028);
+ udelay(10);
+}
+
+static void dram_disable_all_master(void)
+{
+ writel(1, 0x3102020);
+ writel(0, 0x3102024);
+ writel(0, 0x3102028);
+ udelay(10);
+}
+
+static void eye_delay_compensation(const dram_para_t *para)
+{
+ uint32_t delay;
+ unsigned long ptr;
+
+ // DATn0IOCR, n = 0...7
+ delay = (para->dram_tpr11 & 0xf) << 9;
+ delay |= (para->dram_tpr12 & 0xf) << 1;
+ for (ptr = 0x3103310; ptr < 0x3103334; ptr += 4)
+ setbits_le32(ptr, delay);
+
+ // DATn1IOCR, n = 0...7
+ delay = (para->dram_tpr11 & 0xf0) << 5;
+ delay |= (para->dram_tpr12 & 0xf0) >> 3;
+ for (ptr = 0x3103390; ptr != 0x31033b4; ptr += 4)
+ setbits_le32(ptr, delay);
+
+ // PGCR0: assert AC loopback FIFO reset
+ clrbits_le32(0x3103100, 0x04000000);
+
+ // ??
+
+ delay = (para->dram_tpr11 & 0xf0000) >> 7;
+ delay |= (para->dram_tpr12 & 0xf0000) >> 15;
+ setbits_le32(0x3103334, delay);
+ setbits_le32(0x3103338, delay);
+
+ delay = (para->dram_tpr11 & 0xf00000) >> 11;
+ delay |= (para->dram_tpr12 & 0xf00000) >> 19;
+ setbits_le32(0x31033b4, delay);
+ setbits_le32(0x31033b8, delay);
+
+ setbits_le32(0x310333c, (para->dram_tpr11 & 0xf0000) << 9);
+ setbits_le32(0x31033bc, (para->dram_tpr11 & 0xf00000) << 5);
+
+ // PGCR0: release AC loopback FIFO reset
+ setbits_le32(0x3103100, BIT(26));
+
+ udelay(1);
+
+ delay = (para->dram_tpr10 & 0xf0) << 4;
+ for (ptr = 0x3103240; ptr != 0x310327c; ptr += 4)
+ setbits_le32(ptr, delay);
+ for (ptr = 0x3103228; ptr != 0x3103240; ptr += 4)
+ setbits_le32(ptr, delay);
+
+ setbits_le32(0x3103218, (para->dram_tpr10 & 0x0f) << 8);
+ setbits_le32(0x310321c, (para->dram_tpr10 & 0x0f) << 8);
+
+ setbits_le32(0x3103280, (para->dram_tpr10 & 0xf00) >> 4);
+}
+
+/*
+ * Main purpose of the auto_set_timing routine seems to be to calculate all
+ * timing settings for the specific type of sdram used. Read together with
+ * an sdram datasheet for context on the various variables.
+ */
+static void mctl_set_timing_params(const dram_para_t *para,
+ const dram_config_t *config)
+{
+ /* DRAM_TPR0 */
+ u8 tccd = 2;
+ u8 tfaw;
+ u8 trrd;
+ u8 trcd;
+ u8 trc;
+
+ /* DRAM_TPR1 */
+ u8 txp;
+ u8 twtr;
+ u8 trtp = 4;
+ u8 twr;
+ u8 trp;
+ u8 tras;
+
+ /* DRAM_TPR2 */
+ u16 trefi;
+ u16 trfc;
+
+ u8 tcksrx;
+ u8 tckesr;
+ u8 trd2wr;
+ u8 twr2rd;
+ u8 trasmax;
+ u8 twtp;
+ u8 tcke;
+ u8 tmod;
+ u8 tmrd;
+ u8 tmrw;
+
+ u8 tcl;
+ u8 tcwl;
+ u8 t_rdata_en;
+ u8 wr_latency;
+
+ u32 mr0;
+ u32 mr1;
+ u32 mr2;
+ u32 mr3;
+
+ u32 tdinit0;
+ u32 tdinit1;
+ u32 tdinit2;
+ u32 tdinit3;
+
+ switch (para->dram_type) {
+ case SUNXI_DRAM_TYPE_DDR2:
+ /* DRAM_TPR0 */
+ tfaw = ns_to_t(50);
+ trrd = ns_to_t(10);
+ trcd = ns_to_t(20);
+ trc = ns_to_t(65);
+
+ /* DRAM_TPR1 */
+ txp = 2;
+ twtr = ns_to_t(8);
+ twr = ns_to_t(15);
+ trp = ns_to_t(15);
+ tras = ns_to_t(45);
+
+ /* DRAM_TRP2 */
+ trfc = ns_to_t(328);
+ trefi = ns_to_t(7800) / 32;
+
+ trasmax = CONFIG_DRAM_CLK / 30;
+ if (CONFIG_DRAM_CLK < 409) {
+ t_rdata_en = 1;
+ tcl = 3;
+ mr0 = 0x06a3;
+ } else {
+ t_rdata_en = 2;
+ tcl = 4;
+ mr0 = 0x0e73;
+ }
+ tmrd = 2;
+ twtp = twr + 5;
+ tcksrx = 5;
+ tckesr = 4;
+ trd2wr = 4;
+ tcke = 3;
+ tmod = 12;
+ wr_latency = 1;
+ tmrw = 0;
+ twr2rd = twtr + 5;
+ tcwl = 0;
+
+ mr1 = para->dram_mr1;
+ mr2 = 0;
+ mr3 = 0;
+
+ tdinit0 = 200 * CONFIG_DRAM_CLK + 1;
+ tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1;
+ tdinit2 = 200 * CONFIG_DRAM_CLK + 1;
+ tdinit3 = 1 * CONFIG_DRAM_CLK + 1;
+
+ break;
+ case SUNXI_DRAM_TYPE_DDR3:
+ trfc = ns_to_t(350);
+ trefi = ns_to_t(7800) / 32 + 1; // XXX
+
+ twtr = ns_to_t(8) + 2; // + 2 ? XXX
+ /* Only used by trd2wr calculation, which gets discard below */
+// twr = max(ns_to_t(15), 2);
+ trrd = max(ns_to_t(10), 2);
+ txp = max(ns_to_t(10), 2);
+
+ if (CONFIG_DRAM_CLK <= 800) {
+ tfaw = ns_to_t(50);
+ trcd = ns_to_t(15);
+ trp = ns_to_t(15);
+ trc = ns_to_t(53);
+ tras = ns_to_t(38);
+
+ mr0 = 0x1c70;
+ mr2 = 0x18;
+ tcl = 6;
+ wr_latency = 2;
+ tcwl = 4;
+ t_rdata_en = 4;
+ } else {
+ tfaw = ns_to_t(35);
+ trcd = ns_to_t(14);
+ trp = ns_to_t(14);
+ trc = ns_to_t(48);
+ tras = ns_to_t(34);
+
+ mr0 = 0x1e14;
+ mr2 = 0x20;
+ tcl = 7;
+ wr_latency = 3;
+ tcwl = 5;
+ t_rdata_en = 5;
+ }
+
+ trasmax = CONFIG_DRAM_CLK / 30;
+ twtp = tcwl + 2 + twtr; // WL+BL/2+tWTR
+ /* Gets overwritten below */
+// trd2wr = tcwl + 2 + twr; // WL+BL/2+tWR
+ twr2rd = tcwl + twtr; // WL+tWTR
+
+ tdinit0 = 500 * CONFIG_DRAM_CLK + 1; // 500 us
+ tdinit1 = 360 * CONFIG_DRAM_CLK / 1000 + 1; // 360 ns
+ tdinit2 = 200 * CONFIG_DRAM_CLK + 1; // 200 us
+ tdinit3 = 1 * CONFIG_DRAM_CLK + 1; // 1 us
+
+ mr1 = para->dram_mr1;
+ mr3 = 0;
+ tcke = 3;
+ tcksrx = 5;
+ tckesr = 4;
+ if (((config->dram_tpr13 & 0xc) == 0x04) || CONFIG_DRAM_CLK < 912)
+ trd2wr = 5;
+ else
+ trd2wr = 6;
+
+ tmod = 12;
+ tmrd = 4;
+ tmrw = 0;
+
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR2:
+ tfaw = max(ns_to_t(50), 4);
+ trrd = max(ns_to_t(10), 1);
+ trcd = max(ns_to_t(24), 2);
+ trc = ns_to_t(70);
+ txp = ns_to_t(8);
+ if (txp < 2) {
+ txp++;
+ twtr = 2;
+ } else {
+ twtr = txp;
+ }
+ twr = max(ns_to_t(15), 2);
+ trp = ns_to_t(17);
+ tras = ns_to_t(42);
+ trefi = ns_to_t(3900) / 32;
+ trfc = ns_to_t(210);
+
+ trasmax = CONFIG_DRAM_CLK / 60;
+ mr3 = para->dram_mr3;
+ twtp = twr + 5;
+ mr2 = 6;
+ mr1 = 5;
+ tcksrx = 5;
+ tckesr = 5;
+ trd2wr = 10;
+ tcke = 2;
+ tmod = 5;
+ tmrd = 5;
+ tmrw = 3;
+ tcl = 4;
+ wr_latency = 1;
+ t_rdata_en = 1;
+
+ tdinit0 = 200 * CONFIG_DRAM_CLK + 1;
+ tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1;
+ tdinit2 = 11 * CONFIG_DRAM_CLK + 1;
+ tdinit3 = 1 * CONFIG_DRAM_CLK + 1;
+ twr2rd = twtr + 5;
+ tcwl = 2;
+ mr1 = 195;
+ mr0 = 0;
+
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ tfaw = max(ns_to_t(50), 4);
+ trrd = max(ns_to_t(10), 1);
+ trcd = max(ns_to_t(24), 2);
+ trc = ns_to_t(70);
+ twtr = max(ns_to_t(8), 2);
+ twr = max(ns_to_t(15), 2);
+ trp = ns_to_t(17);
+ tras = ns_to_t(42);
+ trefi = ns_to_t(3900) / 32;
+ trfc = ns_to_t(210);
+ txp = twtr;
+
+ trasmax = CONFIG_DRAM_CLK / 60;
+ if (CONFIG_DRAM_CLK < 800) {
+ tcwl = 4;
+ wr_latency = 3;
+ t_rdata_en = 6;
+ mr2 = 12;
+ } else {
+ tcwl = 3;
+ tcke = 6;
+ wr_latency = 2;
+ t_rdata_en = 5;
+ mr2 = 10;
+ }
+ twtp = tcwl + 5;
+ tcl = 7;
+ mr3 = para->dram_mr3;
+ tcksrx = 5;
+ tckesr = 5;
+ trd2wr = 13;
+ tcke = 3;
+ tmod = 12;
+ tdinit0 = 400 * CONFIG_DRAM_CLK + 1;
+ tdinit1 = 500 * CONFIG_DRAM_CLK / 1000 + 1;
+ tdinit2 = 11 * CONFIG_DRAM_CLK + 1;
+ tdinit3 = 1 * CONFIG_DRAM_CLK + 1;
+ tmrd = 5;
+ tmrw = 5;
+ twr2rd = tcwl + twtr + 5;
+ mr1 = 195;
+ mr0 = 0;
+
+ break;
+ default:
+ trfc = 128;
+ trp = 6;
+ trefi = 98;
+ txp = 10;
+ twr = 8;
+ twtr = 3;
+ tras = 14;
+ tfaw = 16;
+ trc = 20;
+ trcd = 6;
+ trrd = 3;
+
+ twr2rd = 8;
+ tcksrx = 4;
+ tckesr = 3;
+ trd2wr = 4;
+ trasmax = 27;
+ twtp = 12;
+ tcke = 2;
+ tmod = 6;
+ tmrd = 2;
+ tmrw = 0;
+ tcwl = 3;
+ tcl = 3;
+ wr_latency = 1;
+ t_rdata_en = 1;
+ mr3 = 0;
+ mr2 = 0;
+ mr1 = 0;
+ mr0 = 0;
+ tdinit3 = 0;
+ tdinit2 = 0;
+ tdinit1 = 0;
+ tdinit0 = 0;
+
+ break;
+ }
+
+ /* Set mode registers */
+ writel(mr0, 0x3103030);
+ writel(mr1, 0x3103034);
+ writel(mr2, 0x3103038);
+ writel(mr3, 0x310303c);
+ /* TODO: dram_odt_en is either 0x0 or 0x1, so right shift looks weird */
+ writel((para->dram_odt_en >> 4) & 0x3, 0x310302c);
+
+ /* Set dram timing DRAMTMG0 - DRAMTMG5 */
+ writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0),
+ 0x3103058);
+ writel((txp << 16) | (trtp << 8) | (trc << 0),
+ 0x310305c);
+ writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0),
+ 0x3103060);
+ writel((tmrw << 16) | (tmrd << 12) | (tmod << 0),
+ 0x3103064);
+ writel((trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0),
+ 0x3103068);
+ writel((tcksrx << 24) | (tcksrx << 16) | (tckesr << 8) | (tcke << 0),
+ 0x310306c);
+
+ /* Set dual rank timing */
+ clrsetbits_le32(0x3103078, 0xf000ffff,
+ (CONFIG_DRAM_CLK < 800) ? 0xf0006610 : 0xf0007610);
+
+ /* Set phy interface time PITMG0, PTR3, PTR4 */
+ writel((0x2 << 24) | (t_rdata_en << 16) | BIT(8) | (wr_latency << 0),
+ 0x3103080);
+ writel(((tdinit0 << 0) | (tdinit1 << 20)), 0x3103050);
+ writel(((tdinit2 << 0) | (tdinit3 << 20)), 0x3103054);
+
+ /* Set refresh timing and mode */
+ writel((trefi << 16) | (trfc << 0), 0x3103090);
+ writel((trefi << 15) & 0x0fff0000, 0x3103094);
+}
+
+// Purpose of this routine seems to be to initialize the PLL driving
+// the MBUS and sdram.
+//
+static int ccu_set_pll_ddr_clk(int index, const dram_para_t *para,
+ const dram_config_t *config)
+{
+ unsigned int val, clk, n;
+
+ if (config->dram_tpr13 & BIT(6))
+ clk = para->dram_tpr9;
+ else
+ clk = para->dram_clk;
+
+ // set VCO clock divider
+ n = (clk * 2) / 24;
+
+ val = readl(SUNXI_CCM_BASE + 0x10);
+ val &= ~0x0007ff03; // clear dividers
+ val |= (n - 1) << 8; // set PLL division
+ val |= BIT(31) | BIT(30); // enable PLL and LDO
+ writel(val | BIT(29), SUNXI_CCM_BASE + 0x10);
+
+ // wait for PLL to lock
+ while ((readl(SUNXI_CCM_BASE + 0x10) & BIT(28)) == 0)
+ ;
+
+ udelay(20);
+
+ // enable PLL output
+ setbits_le32(SUNXI_CCM_BASE + 0x0, BIT(27));
+
+ // turn clock gate on
+ val = readl(SUNXI_CCM_BASE + 0x800);
+ val &= ~0x03000303; // select DDR clk source, n=1, m=1
+ val |= BIT(31); // turn clock on
+ writel(val, SUNXI_CCM_BASE + 0x800);
+
+ return n * 24;
+}
+
+/* Set up the PLL and clock gates for the DRAM controller and MBUS clocks. */
+static void mctl_sys_init(const dram_para_t *para, const dram_config_t *config)
+{
+ // assert MBUS reset
+ clrbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30));
+
+ // turn off sdram clock gate, assert sdram reset
+ clrbits_le32(SUNXI_CCM_BASE + 0x80c, 0x10001);
+ clrsetbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(30), BIT(27));
+ udelay(10);
+
+ // set ddr pll clock
+ ccu_set_pll_ddr_clk(0, para, config);
+ udelay(100);
+ dram_disable_all_master();
+
+ // release sdram reset
+ setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(16));
+
+ // release MBUS reset
+ setbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30));
+ setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(30));
+
+ udelay(5);
+
+ // turn on sdram clock gate
+ setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(0));
+
+ // turn dram clock gate on, trigger sdr clock update
+ setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(27));
+ udelay(5);
+
+ // mCTL clock enable
+ writel(0x8000, 0x310300c);
+ udelay(10);
+}
+
+// The main purpose of this routine seems to be to copy an address configuration
+// from the dram_para1 and dram_para2 fields to the PHY configuration registers
+// (0x3102000, 0x3102004).
+//
+static void mctl_com_init(const dram_para_t *para, const dram_config_t *config)
+{
+ uint32_t val, width;
+ unsigned long ptr;
+ int i;
+
+ // purpose ??
+ clrsetbits_le32(0x3102008, 0x3f00, 0x2000);
+
+ // set SDRAM type and word width
+ val = readl(0x3102000) & ~0x00fff000;
+ val |= (para->dram_type & 0x7) << 16; // DRAM type
+ val |= (~config->dram_para2 & 0x1) << 12; // DQ width
+ val |= BIT(22); // ??
+ if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 ||
+ para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) {
+ val |= BIT(19); // type 6 and 7 must use 1T
+ } else {
+ if (config->dram_tpr13 & BIT(5))
+ val |= BIT(19);
+ }
+ writel(val, 0x3102000);
+
+ // init rank / bank / row for single/dual or two different ranks
+ if ((config->dram_para2 & BIT(8)) &&
+ ((config->dram_para2 & 0xf000) != 0x1000))
+ width = 32;
+ else
+ width = 16;
+
+ ptr = 0x3102000;
+ for (i = 0; i < width; i += 16) {
+ val = readl(ptr) & 0xfffff000;
+
+ val |= (config->dram_para2 >> 12) & 0x3; // rank
+ val |= ((config->dram_para1 >> (i + 12)) << 2) & 0x4; // bank - 2
+ val |= (((config->dram_para1 >> (i + 4)) - 1) << 4) & 0xff; // row - 1
+
+ // convert from page size to column addr width - 3
+ switch ((config->dram_para1 >> i) & 0xf) {
+ case 8: val |= 0xa00; break;
+ case 4: val |= 0x900; break;
+ case 2: val |= 0x800; break;
+ case 1: val |= 0x700; break;
+ default: val |= 0x600; break;
+ }
+ writel(val, ptr);
+ ptr += 4;
+ }
+
+ // set ODTMAP based on number of ranks in use
+ val = (readl(0x3102000) & 0x1) ? 0x303 : 0x201;
+ writel(val, 0x3103120);
+
+ // set mctl reg 3c4 to zero when using half DQ
+ if (config->dram_para2 & BIT(0))
+ writel(0, 0x31033c4);
+
+ // purpose ??
+ if (para->dram_tpr4) {
+ setbits_le32(0x3102000, (para->dram_tpr4 & 0x3) << 25);
+ setbits_le32(0x3102004, (para->dram_tpr4 & 0x7fc) << 10);
+ }
+}
+
+static const uint8_t ac_remapping_tables[][22] = {
+ [0] = { 0 },
+ [1] = { 1, 9, 3, 7, 8, 18, 4, 13, 5, 6, 10,
+ 2, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 },
+ [2] = { 4, 9, 3, 7, 8, 18, 1, 13, 2, 6, 10,
+ 5, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 },
+ [3] = { 1, 7, 8, 12, 10, 18, 4, 13, 5, 6, 3,
+ 2, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 },
+ [4] = { 4, 12, 10, 7, 8, 18, 1, 13, 2, 6, 3,
+ 5, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 },
+ [5] = { 13, 2, 7, 9, 12, 19, 5, 1, 6, 3, 4,
+ 8, 10, 0, 0, 0, 21, 22, 18, 17, 11, 20 },
+ [6] = { 3, 10, 7, 13, 9, 11, 1, 2, 4, 6, 8,
+ 5, 12, 0, 0, 0, 20, 1, 0, 21, 22, 17 },
+ [7] = { 3, 2, 4, 7, 9, 1, 17, 12, 18, 14, 13,
+ 8, 15, 6, 10, 5, 19, 22, 16, 21, 20, 11 },
+};
+
+/*
+ * This routine chooses one of several remapping tables for 22 lines.
+ * It is unclear which lines are being remapped. It seems to pick
+ * table cfg7 for the Nezha board.
+ */
+static void mctl_phy_ac_remapping(const dram_para_t *para,
+ const dram_config_t *config)
+{
+ const uint8_t *cfg;
+ uint32_t fuse, val;
+
+ /*
+ * It is unclear whether the LPDDRx types don't need any remapping,
+ * or whether the original code just didn't provide tables.
+ */
+ if (para->dram_type != SUNXI_DRAM_TYPE_DDR2 &&
+ para->dram_type != SUNXI_DRAM_TYPE_DDR3)
+ return;
+
+ fuse = (readl(SUNXI_SID_BASE + 0x28) & 0xf00) >> 8;
+ debug("DDR efuse: 0x%x\n", fuse);
+
+ if (para->dram_type == SUNXI_DRAM_TYPE_DDR2) {
+ if (fuse == 15)
+ return;
+ cfg = ac_remapping_tables[6];
+ } else {
+ if (config->dram_tpr13 & 0xc0000) {
+ cfg = ac_remapping_tables[7];
+ } else {
+ switch (fuse) {
+ case 8: cfg = ac_remapping_tables[2]; break;
+ case 9: cfg = ac_remapping_tables[3]; break;
+ case 10: cfg = ac_remapping_tables[5]; break;
+ case 11: cfg = ac_remapping_tables[4]; break;
+ default:
+ case 12: cfg = ac_remapping_tables[1]; break;
+ case 13:
+ case 14: cfg = ac_remapping_tables[0]; break;
+ }
+ }
+ }
+
+ val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) |
+ (cfg[1] << 10) | (cfg[0] << 5);
+ writel(val, 0x3102500);
+
+ val = (cfg[10] << 25) | (cfg[9] << 20) | (cfg[8] << 15) |
+ (cfg[ 7] << 10) | (cfg[6] << 5) | cfg[5];
+ writel(val, 0x3102504);
+
+ val = (cfg[15] << 20) | (cfg[14] << 15) | (cfg[13] << 10) |
+ (cfg[12] << 5) | cfg[11];
+ writel(val, 0x3102508);
+
+ val = (cfg[21] << 25) | (cfg[20] << 20) | (cfg[19] << 15) |
+ (cfg[18] << 10) | (cfg[17] << 5) | cfg[16];
+ writel(val, 0x310250c);
+
+ val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) |
+ (cfg[1] << 10) | (cfg[0] << 5) | 1;
+ writel(val, 0x3102500);
+}
+
+// Init the controller channel. The key part is placing commands in the main
+// command register (PIR, 0x3103000) and checking command status (PGSR0, 0x3103010).
+//
+static unsigned int mctl_channel_init(unsigned int ch_index,
+ const dram_para_t *para,
+ const dram_config_t *config)
+{
+ unsigned int val, dqs_gating_mode;
+
+ dqs_gating_mode = (config->dram_tpr13 & 0xc) >> 2;
+
+ // set DDR clock to half of CPU clock
+ clrsetbits_le32(0x310200c, 0xfff, (para->dram_clk / 2) - 1);
+
+ // MRCTRL0 nibble 3 undocumented
+ clrsetbits_le32(0x3103108, 0xf00, 0x300);
+
+ if (para->dram_odt_en)
+ val = 0;
+ else
+ val = BIT(5);
+
+ // DX0GCR0
+ if (para->dram_clk > 672)
+ clrsetbits_le32(0x3103344, 0xf63e, val);
+ else
+ clrsetbits_le32(0x3103344, 0xf03e, val);
+
+ // DX1GCR0
+ if (para->dram_clk > 672) {
+ setbits_le32(0x3103344, 0x400);
+ clrsetbits_le32(0x31033c4, 0xf63e, val);
+ } else {
+ clrsetbits_le32(0x31033c4, 0xf03e, val);
+ }
+
+ // 0x3103208 undocumented
+ setbits_le32(0x3103208, BIT(1));
+
+ eye_delay_compensation(para);
+
+ // set PLL SSCG ?
+ val = readl(0x3103108);
+ if (dqs_gating_mode == 1) {
+ clrsetbits_le32(0x3103108, 0xc0, 0);
+ clrbits_le32(0x31030bc, 0x107);
+ } else if (dqs_gating_mode == 2) {
+ clrsetbits_le32(0x3103108, 0xc0, 0x80);
+
+ clrsetbits_le32(0x31030bc, 0x107,
+ (((config->dram_tpr13 >> 16) & 0x1f) - 2) | 0x100);
+ clrsetbits_le32(0x310311c, BIT(31), BIT(27));
+ } else {
+ clrbits_le32(0x3103108, 0x40);
+ udelay(10);
+ setbits_le32(0x3103108, 0xc0);
+ }
+
+ if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 ||
+ para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) {
+ if (dqs_gating_mode == 1)
+ clrsetbits_le32(0x310311c, 0x080000c0, 0x80000000);
+ else
+ clrsetbits_le32(0x310311c, 0x77000000, 0x22000000);
+ }
+
+ clrsetbits_le32(0x31030c0, 0x0fffffff,
+ (config->dram_para2 & BIT(12)) ? 0x03000001 : 0x01000007);
+
+ if (readl(0x70005d4) & BIT(16)) {
+ clrbits_le32(0x7010250, 0x2);
+ udelay(10);
+ }
+
+ // Set ZQ config
+ clrsetbits_le32(0x3103140, 0x3ffffff,
+ (para->dram_zq & 0x00ffffff) | BIT(25));
+
+ // Initialise DRAM controller
+ if (dqs_gating_mode == 1) {
+ //writel(0x52, 0x3103000); // prep PHY reset + PLL init + z-cal
+ writel(0x53, 0x3103000); // Go
+
+ while ((readl(0x3103010) & 0x1) == 0) {
+ } // wait for IDONE
+ udelay(10);
+
+ // 0x520 = prep DQS gating + DRAM init + d-cal
+ if (para->dram_type == SUNXI_DRAM_TYPE_DDR3)
+ writel(0x5a0, 0x3103000); // + DRAM reset
+ else
+ writel(0x520, 0x3103000);
+ } else {
+ if ((readl(0x70005d4) & (1 << 16)) == 0) {
+ // prep DRAM init + PHY reset + d-cal + PLL init + z-cal
+ if (para->dram_type == SUNXI_DRAM_TYPE_DDR3)
+ writel(0x1f2, 0x3103000); // + DRAM reset
+ else
+ writel(0x172, 0x3103000);
+ } else {
+ // prep PHY reset + d-cal + z-cal
+ writel(0x62, 0x3103000);
+ }
+ }
+
+ setbits_le32(0x3103000, 0x1); // GO
+
+ udelay(10);
+ while ((readl(0x3103010) & 0x1) == 0) {
+ } // wait for IDONE
+
+ if (readl(0x70005d4) & BIT(16)) {
+ clrsetbits_le32(0x310310c, 0x06000000, 0x04000000);
+ udelay(10);
+
+ setbits_le32(0x3103004, 0x1);
+
+ while ((readl(0x3103018) & 0x7) != 0x3) {
+ }
+
+ clrbits_le32(0x7010250, 0x1);
+ udelay(10);
+
+ clrbits_le32(0x3103004, 0x1);
+
+ while ((readl(0x3103018) & 0x7) != 0x1) {
+ }
+
+ udelay(15);
+
+ if (dqs_gating_mode == 1) {
+ clrbits_le32(0x3103108, 0xc0);
+ clrsetbits_le32(0x310310c, 0x06000000, 0x02000000);
+ udelay(1);
+ writel(0x401, 0x3103000);
+
+ while ((readl(0x3103010) & 0x1) == 0) {
+ }
+ }
+ }
+
+ // Check for training error
+ if (readl(0x3103010) & BIT(20)) {
+ printf("ZQ calibration error, check external 240 ohm resistor\n");
+ return 0;
+ }
+
+ // STATR = Zynq STAT? Wait for status 'normal'?
+ while ((readl(0x3103018) & 0x1) == 0) {
+ }
+
+ setbits_le32(0x310308c, BIT(31));
+ udelay(10);
+ clrbits_le32(0x310308c, BIT(31));
+ udelay(10);
+ setbits_le32(0x3102014, BIT(31));
+ udelay(10);
+
+ clrbits_le32(0x310310c, 0x06000000);
+
+ if (dqs_gating_mode == 1)
+ clrsetbits_le32(0x310311c, 0xc0, 0x40);
+
+ return 1;
+}
+
+static unsigned int calculate_rank_size(uint32_t regval)
+{
+ unsigned int bits;
+
+ bits = (regval >> 8) & 0xf; /* page size - 3 */
+ bits += (regval >> 4) & 0xf; /* row width - 1 */
+ bits += (regval >> 2) & 0x3; /* bank count - 2 */
+ bits -= 14; /* 1MB = 20 bits, minus above 6 = 14 */
+
+ return 1U << bits;
+}
+
+/*
+ * The below routine reads the dram config registers and extracts
+ * the number of address bits in each rank available. It then calculates
+ * total memory size in MB.
+ */
+static unsigned int DRAMC_get_dram_size(void)
+{
+ uint32_t val;
+ unsigned int size;
+
+ val = readl(0x3102000); /* MC_WORK_MODE0 */
+ size = calculate_rank_size(val);
+ if ((val & 0x3) == 0) /* single rank? */
+ return size;
+
+ val = readl(0x3102004); /* MC_WORK_MODE1 */
+ if ((val & 0x3) == 0) /* two identical ranks? */
+ return size * 2;
+
+ /* add sizes of both ranks */
+ return size + calculate_rank_size(val);
+}
+
+/*
+ * The below routine reads the command status register to extract
+ * DQ width and rank count. This follows the DQS training command in
+ * channel_init. If error bit 22 is reset, we have two ranks and full DQ.
+ * If there was an error, figure out whether it was half DQ, single rank,
+ * or both. Set bit 12 and 0 in dram_para2 with the results.
+ */
+static int dqs_gate_detect(dram_config_t *config)
+{
+ uint32_t dx0, dx1;
+
+ if ((readl(0x3103010) & BIT(22)) == 0) {
+ config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12);
+ debug("dual rank and full DQ\n");
+
+ return 1;
+ }
+
+ dx0 = (readl(0x3103348) & 0x3000000) >> 24;
+ if (dx0 == 0) {
+ config->dram_para2 = (config->dram_para2 & ~0xf) | 0x1001;
+ debug("dual rank and half DQ\n");
+
+ return 1;
+ }
+
+ if (dx0 == 2) {
+ dx1 = (readl(0x31033c8) & 0x3000000) >> 24;
+ if (dx1 == 2) {
+ config->dram_para2 = config->dram_para2 & ~0xf00f;
+ debug("single rank and full DQ\n");
+ } else {
+ config->dram_para2 = (config->dram_para2 & ~0xf00f) | BIT(0);
+ debug("single rank and half DQ\n");
+ }
+
+ return 1;
+ }
+
+ if ((config->dram_tpr13 & BIT(29)) == 0)
+ return 0;
+
+ debug("DX0 state: %d\n", dx0);
+ debug("DX1 state: %d\n", dx1);
+
+ return 0;
+}
+
+static int dramc_simple_wr_test(unsigned int mem_mb, int len)
+{
+ unsigned int offs = (mem_mb / 2) << 18; // half of memory size
+ unsigned int patt1 = 0x01234567;
+ unsigned int patt2 = 0xfedcba98;
+ unsigned int *addr, v1, v2, i;
+
+ addr = (unsigned int *)CFG_SYS_SDRAM_BASE;
+ for (i = 0; i != len; i++, addr++) {
+ writel(patt1 + i, (unsigned long)addr);
+ writel(patt2 + i, (unsigned long)(addr + offs));
+ }
+
+ addr = (unsigned int *)CFG_SYS_SDRAM_BASE;
+ for (i = 0; i != len; i++) {
+ v1 = readl((unsigned long)(addr + i));
+ v2 = patt1 + i;
+ if (v1 != v2) {
+ printf("DRAM: simple test FAIL\n");
+ printf("%x != %x at address %p\n", v1, v2, addr + i);
+ return 1;
+ }
+ v1 = readl((unsigned long)(addr + offs + i));
+ v2 = patt2 + i;
+ if (v1 != v2) {
+ printf("DRAM: simple test FAIL\n");
+ printf("%x != %x at address %p\n", v1, v2, addr + offs + i);
+ return 1;
+ }
+ }
+
+ debug("DRAM: simple test OK\n");
+ return 0;
+}
+
+// Set the Vref mode for the controller
+//
+static void mctl_vrefzq_init(const dram_para_t *para, const dram_config_t *config)
+{
+ if (config->dram_tpr13 & BIT(17))
+ return;
+
+ clrsetbits_le32(0x3103110, 0x7f7f7f7f, para->dram_tpr5);
+
+ // IOCVR1
+ if ((config->dram_tpr13 & BIT(16)) == 0)
+ clrsetbits_le32(0x3103114, 0x7f, para->dram_tpr6 & 0x7f);
+}
+
+// Perform an init of the controller. This is actually done 3 times. The first
+// time to establish the number of ranks and DQ width. The second time to
+// establish the actual ram size. The third time is final one, with the final
+// settings.
+//
+static int mctl_core_init(const dram_para_t *para, const dram_config_t *config)
+{
+ mctl_sys_init(para, config);
+
+ mctl_vrefzq_init(para, config);
+
+ mctl_com_init(para, config);
+
+ mctl_phy_ac_remapping(para, config);
+
+ mctl_set_timing_params(para, config);
+
+ return mctl_channel_init(0, para, config);
+}
+
+/*
+ * This routine sizes a DRAM device by cycling through address lines and
+ * figuring out if they are connected to a real address line, or if the
+ * address is a mirror.
+ * First the column and bank bit allocations are set to low values (2 and 9
+ * address lines). Then a maximum allocation (16 lines) is set for rows and
+ * this is tested.
+ * Next the BA2 line is checked. This seems to be placed above the column,
+ * BA0-1 and row addresses. Finally, the column address is allocated 13 lines
+ * and these are tested. The results are placed in dram_para1 and dram_para2.
+ */
+
+static uint32_t get_payload(bool odd, unsigned long int ptr)
+{
+ if (odd)
+ return (uint32_t)ptr;
+ else
+ return ~((uint32_t)ptr);
+}
+
+static int auto_scan_dram_size(const dram_para_t *para, dram_config_t *config)
+{
+ unsigned int rval, i, j, rank, maxrank, offs;
+ unsigned int shft;
+ unsigned long ptr, mc_work_mode, chk;
+
+ if (mctl_core_init(para, config) == 0) {
+ printf("DRAM initialisation error : 0\n");
+ return 0;
+ }
+
+ maxrank = (config->dram_para2 & 0xf000) ? 2 : 1;
+ mc_work_mode = 0x3102000;
+ offs = 0;
+
+ /* write test pattern */
+ for (i = 0, ptr = CFG_SYS_SDRAM_BASE; i < 64; i++, ptr += 4)
+ writel(get_payload(i & 0x1, ptr), ptr);
+
+ for (rank = 0; rank < maxrank;) {
+ /* set row mode */
+ clrsetbits_le32(mc_work_mode, 0xf0c, 0x6f0);
+ udelay(1);
+
+ // Scan per address line, until address wraps (i.e. see shadow)
+ for (i = 11; i < 17; i++) {
+ chk = CFG_SYS_SDRAM_BASE + (1U << (i + 11));
+ ptr = CFG_SYS_SDRAM_BASE;
+ for (j = 0; j < 64; j++) {
+ if (readl(chk) != get_payload(j & 0x1, ptr))
+ break;
+ ptr += 4;
+ chk += 4;
+ }
+ if (j == 64)
+ break;
+ }
+ if (i > 16)
+ i = 16;
+ debug("rank %d row = %d\n", rank, i);
+
+ /* Store rows in para 1 */
+ shft = offs + 4;
+ rval = config->dram_para1;
+ rval &= ~(0xff << shft);
+ rval |= i << shft;
+ config->dram_para1 = rval;
+
+ if (rank == 1) /* Set bank mode for rank0 */
+ clrsetbits_le32(0x3102000, 0xffc, 0x6a4);
+
+ /* Set bank mode for current rank */
+ clrsetbits_le32(mc_work_mode, 0xffc, 0x6a4);
+ udelay(1);
+
+ // Test if bit A23 is BA2 or mirror XXX A22?
+ chk = CFG_SYS_SDRAM_BASE + (1U << 22);
+ ptr = CFG_SYS_SDRAM_BASE;
+ for (i = 0, j = 0; i < 64; i++) {
+ if (readl(chk) != get_payload(i & 1, ptr)) {
+ j = 1;
+ break;
+ }
+ ptr += 4;
+ chk += 4;
+ }
+
+ debug("rank %d bank = %d\n", rank, (j + 1) << 2); /* 4 or 8 */
+
+ /* Store banks in para 1 */
+ shft = 12 + offs;
+ rval = config->dram_para1;
+ rval &= ~(0xf << shft);
+ rval |= j << shft;
+ config->dram_para1 = rval;
+
+ if (rank == 1) /* Set page mode for rank0 */
+ clrsetbits_le32(0x3102000, 0xffc, 0xaa0);
+
+ /* Set page mode for current rank */
+ clrsetbits_le32(mc_work_mode, 0xffc, 0xaa0);
+ udelay(1);
+
+ // Scan per address line, until address wraps (i.e. see shadow)
+ for (i = 9; i < 14; i++) {
+ chk = CFG_SYS_SDRAM_BASE + (1U << i);
+ ptr = CFG_SYS_SDRAM_BASE;
+ for (j = 0; j < 64; j++) {
+ if (readl(chk) != get_payload(j & 1, ptr))
+ break;
+ ptr += 4;
+ chk += 4;
+ }
+ if (j == 64)
+ break;
+ }
+ if (i > 13)
+ i = 13;
+
+ unsigned int pgsize = (i == 9) ? 0 : (1 << (i - 10));
+ debug("rank %d page size = %d KB\n", rank, pgsize);
+
+ /* Store page size */
+ shft = offs;
+ rval = config->dram_para1;
+ rval &= ~(0xf << shft);
+ rval |= pgsize << shft;
+ config->dram_para1 = rval;
+
+ // Move to next rank
+ rank++;
+ if (rank != maxrank) {
+ if (rank == 1) {
+ /* MC_WORK_MODE */
+ clrsetbits_le32(0x3202000, 0xffc, 0x6f0);
+
+ /* MC_WORK_MODE2 */
+ clrsetbits_le32(0x3202004, 0xffc, 0x6f0);
+ }
+ /* store rank1 config in upper half of para1 */
+ offs += 16;
+ mc_work_mode += 4; /* move to MC_WORK_MODE2 */
+ }
+ }
+ if (maxrank == 2) {
+ config->dram_para2 &= 0xfffff0ff;
+ /* note: rval is equal to para->dram_para1 here */
+ if ((rval & 0xffff) == (rval >> 16)) {
+ debug("rank1 config same as rank0\n");
+ } else {
+ config->dram_para2 |= BIT(8);
+ debug("rank1 config different from rank0\n");
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * This routine sets up parameters with dqs_gating_mode equal to 1 and two
+ * ranks enabled. It then configures the core and tests for 1 or 2 ranks and
+ * full or half DQ width. It then resets the parameters to the original values.
+ * dram_para2 is updated with the rank and width findings.
+ */
+static int auto_scan_dram_rank_width(const dram_para_t *para,
+ dram_config_t *config)
+{
+ unsigned int s1 = config->dram_tpr13;
+ unsigned int s2 = config->dram_para1;
+
+ config->dram_para1 = 0x00b000b0;
+ config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12);
+
+ /* set DQS probe mode */
+ config->dram_tpr13 = (config->dram_tpr13 & ~0x8) | BIT(2) | BIT(0);
+
+ mctl_core_init(para, config);
+
+ if (readl(0x3103010) & BIT(20))
+ return 0;
+
+ if (dqs_gate_detect(config) == 0)
+ return 0;
+
+ config->dram_tpr13 = s1;
+ config->dram_para1 = s2;
+
+ return 1;
+}
+
+/*
+ * This routine determines the SDRAM topology. It first establishes the number
+ * of ranks and the DQ width. Then it scans the SDRAM address lines to establish
+ * the size of each rank. It then updates dram_tpr13 to reflect that the sizes
+ * are now known: a re-init will not repeat the autoscan.
+ */
+static int auto_scan_dram_config(const dram_para_t *para,
+ dram_config_t *config)
+{
+ if (((config->dram_tpr13 & BIT(14)) == 0) &&
+ (auto_scan_dram_rank_width(para, config) == 0)) {
+ printf("ERROR: auto scan dram rank & width failed\n");
+ return 0;
+ }
+
+ if (((config->dram_tpr13 & BIT(0)) == 0) &&
+ (auto_scan_dram_size(para, config) == 0)) {
+ printf("ERROR: auto scan dram size failed\n");
+ return 0;
+ }
+
+ if ((config->dram_tpr13 & BIT(15)) == 0)
+ config->dram_tpr13 |= BIT(14) | BIT(13) | BIT(1) | BIT(0);
+
+ return 1;
+}
+
+static int init_DRAM(int type, const dram_para_t *para)
+{
+ dram_config_t config = {
+ .dram_para1 = 0x000010d2,
+ .dram_para2 = 0,
+ .dram_tpr13 = CONFIG_DRAM_SUNXI_TPR13,
+ };
+ u32 rc, mem_size_mb;
+
+ debug("DRAM BOOT DRIVE INFO: %s\n", "V0.24");
+ debug("DRAM CLK = %d MHz\n", para->dram_clk);
+ debug("DRAM Type = %d (2:DDR2,3:DDR3)\n", para->dram_type);
+ if ((para->dram_odt_en & 0x1) == 0)
+ debug("DRAMC read ODT off\n");
+ else
+ debug("DRAMC ZQ value: 0x%x\n", para->dram_zq);
+
+ /* Test ZQ status */
+ if (config.dram_tpr13 & BIT(16)) {
+ debug("DRAM only have internal ZQ\n");
+ setbits_le32(0x3000160, BIT(8));
+ writel(0, 0x3000168);
+ udelay(10);
+ } else {
+ clrbits_le32(0x3000160, 0x3);
+ writel(config.dram_tpr13 & BIT(16), 0x7010254);
+ udelay(10);
+ clrsetbits_le32(0x3000160, 0x108, BIT(1));
+ udelay(10);
+ setbits_le32(0x3000160, BIT(0));
+ udelay(20);
+ debug("ZQ value = 0x%x\n", readl(0x300016c));
+ }
+
+ dram_voltage_set(para);
+
+ /* Set SDRAM controller auto config */
+ if ((config.dram_tpr13 & BIT(0)) == 0) {
+ if (auto_scan_dram_config(para, &config) == 0) {
+ printf("auto_scan_dram_config() FAILED\n");
+ return 0;
+ }
+ }
+
+ /* report ODT */
+ rc = para->dram_mr1;
+ if ((rc & 0x44) == 0)
+ debug("DRAM ODT off\n");
+ else
+ debug("DRAM ODT value: 0x%x\n", rc);
+
+ /* Init core, final run */
+ if (mctl_core_init(para, &config) == 0) {
+ printf("DRAM initialisation error: 1\n");
+ return 0;
+ }
+
+ /* Get SDRAM size */
+ /* TODO: who ever puts a negative number in the top half? */
+ rc = config.dram_para2;
+ if (rc & BIT(31)) {
+ rc = (rc >> 16) & ~BIT(15);
+ } else {
+ rc = DRAMC_get_dram_size();
+ debug("DRAM: size = %dMB\n", rc);
+ config.dram_para2 = (config.dram_para2 & 0xffffU) | rc << 16;
+ }
+ mem_size_mb = rc;
+
+ /* Purpose ?? */
+ if (config.dram_tpr13 & BIT(30)) {
+ rc = para->dram_tpr8;
+ if (rc == 0)
+ rc = 0x10000200;
+ writel(rc, 0x31030a0);
+ writel(0x40a, 0x310309c);
+ setbits_le32(0x3103004, BIT(0));
+ debug("Enable Auto SR\n");
+ } else {
+ clrbits_le32(0x31030a0, 0xffff);
+ clrbits_le32(0x3103004, 0x1);
+ }
+
+ /* Purpose ?? */
+ if (config.dram_tpr13 & BIT(9)) {
+ clrsetbits_le32(0x3103100, 0xf000, 0x5000);
+ } else {
+ if (para->dram_type != SUNXI_DRAM_TYPE_LPDDR2)
+ clrbits_le32(0x3103100, 0xf000);
+ }
+
+ setbits_le32(0x3103140, BIT(31));
+
+ /* CHECK: is that really writing to a different register? */
+ if (config.dram_tpr13 & BIT(8))
+ writel(readl(0x3103140) | 0x300, 0x31030b8);
+
+ if (config.dram_tpr13 & BIT(16))
+ clrbits_le32(0x3103108, BIT(13));
+ else
+ setbits_le32(0x3103108, BIT(13));
+
+ /* Purpose ?? */
+ if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR3)
+ clrsetbits_le32(0x310307c, 0xf0000, 0x1000);
+
+ dram_enable_all_master();
+ if (config.dram_tpr13 & BIT(28)) {
+ if ((readl(0x70005d4) & BIT(16)) ||
+ dramc_simple_wr_test(mem_size_mb, 4096))
+ return 0;
+ }
+
+ return mem_size_mb;
+}
+
+static const dram_para_t para = {
+ .dram_clk = CONFIG_DRAM_CLK,
+ .dram_type = CONFIG_SUNXI_DRAM_TYPE,
+ .dram_zq = CONFIG_DRAM_ZQ,
+ .dram_odt_en = CONFIG_DRAM_SUNXI_ODT_EN,
+ .dram_mr0 = 0x1c70,
+ .dram_mr1 = 0x42,
+ .dram_mr2 = 0x18,
+ .dram_mr3 = 0,
+ .dram_tpr0 = 0x004a2195,
+ .dram_tpr1 = 0x02423190,
+ .dram_tpr2 = 0x0008b061,
+ .dram_tpr3 = 0xb4787896, // unused
+ .dram_tpr4 = 0,
+ .dram_tpr5 = 0x48484848,
+ .dram_tpr6 = 0x00000048,
+ .dram_tpr7 = 0x1620121e, // unused
+ .dram_tpr8 = 0,
+ .dram_tpr9 = 0, // clock?
+ .dram_tpr10 = 0,
+ .dram_tpr11 = CONFIG_DRAM_SUNXI_TPR11,
+ .dram_tpr12 = CONFIG_DRAM_SUNXI_TPR12,
+};
+
+unsigned long sunxi_dram_init(void)
+{
+ return init_DRAM(0, &para) * 1024UL * 1024;
+};
+
+#ifdef CONFIG_RAM /* using the driver model */
+struct sunxi_ram_priv {
+ size_t size;
+};
+
+static int sunxi_ram_probe(struct udevice *dev)
+{
+ struct sunxi_ram_priv *priv = dev_get_priv(dev);
+ unsigned long dram_size;
+
+ debug("%s: %s: probing\n", __func__, dev->name);
+
+ dram_size = sunxi_dram_init();
+ if (!dram_size) {
+ printf("DRAM init failed\n");
+ return -ENODEV;
+ }
+
+ priv->size = dram_size;
+
+ return 0;
+}
+
+static int sunxi_ram_get_info(struct udevice *dev, struct ram_info *info)
+{
+ struct sunxi_ram_priv *priv = dev_get_priv(dev);
+
+ debug("%s: %s: getting info\n", __func__, dev->name);
+
+ info->base = CFG_SYS_SDRAM_BASE;
+ info->size = priv->size;
+
+ return 0;
+}
+
+static struct ram_ops sunxi_ram_ops = {
+ .get_info = sunxi_ram_get_info,
+};
+
+static const struct udevice_id sunxi_ram_ids[] = {
+ { .compatible = "allwinner,sun20i-d1-mbus" },
+ { }
+};
+
+U_BOOT_DRIVER(sunxi_ram) = {
+ .name = "sunxi_ram",
+ .id = UCLASS_RAM,
+ .of_match = sunxi_ram_ids,
+ .ops = &sunxi_ram_ops,
+ .probe = sunxi_ram_probe,
+ .priv_auto = sizeof(struct sunxi_ram_priv),
+};
+#endif /* CONFIG_RAM (using driver model) */
diff --git a/drivers/ram/sunxi/dram_sun20i_d1.h b/drivers/ram/sunxi/dram_sun20i_d1.h
new file mode 100644
index 0000000000..91383f6cf1
--- /dev/null
+++ b/drivers/ram/sunxi/dram_sun20i_d1.h
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * D1/R528/T113 DRAM controller register and constant defines
+ *
+ * (C) Copyright 2022 Arm Ltd.
+ * Based on H6 and H616 header, which are:
+ * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
+ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ */
+
+#ifndef _SUNXI_DRAM_SUN20I_D1_H
+#define _SUNXI_DRAM_SUN20I_D1_H
+
+enum sunxi_dram_type {
+ SUNXI_DRAM_TYPE_DDR2 = 2,
+ SUNXI_DRAM_TYPE_DDR3 = 3,
+ SUNXI_DRAM_TYPE_LPDDR2 = 6,
+ SUNXI_DRAM_TYPE_LPDDR3 = 7,
+};
+
+/*
+ * This structure contains a mixture of fixed configuration settings,
+ * variables that are used at runtime to communicate settings between
+ * different stages and functions, and unused values.
+ * This is copied from Allwinner's boot0 data structure, which can be
+ * found at offset 0x38 in any boot0 binary. To allow matching up some
+ * board specific settings, this struct is kept compatible, even though
+ * we don't need all members in our code.
+ */
+typedef struct dram_para {
+ /* normal configuration */
+ const u32 dram_clk;
+ const u32 dram_type;
+ const u32 dram_zq;
+ const u32 dram_odt_en;
+
+ /* timing configuration */
+ const u32 dram_mr0;
+ const u32 dram_mr1;
+ const u32 dram_mr2;
+ const u32 dram_mr3;
+ const u32 dram_tpr0; //DRAMTMG0
+ const u32 dram_tpr1; //DRAMTMG1
+ const u32 dram_tpr2; //DRAMTMG2
+ const u32 dram_tpr3; //DRAMTMG3
+ const u32 dram_tpr4; //DRAMTMG4
+ const u32 dram_tpr5; //DRAMTMG5
+ const u32 dram_tpr6; //DRAMTMG8
+ const u32 dram_tpr7;
+ const u32 dram_tpr8;
+ const u32 dram_tpr9;
+ const u32 dram_tpr10;
+ const u32 dram_tpr11;
+ const u32 dram_tpr12;
+} dram_para_t;
+
+typedef struct dram_config {
+ /* control configuration */
+ u32 dram_para1;
+ u32 dram_para2;
+ /* contains a bitfield of DRAM setup settings */
+ u32 dram_tpr13;
+} dram_config_t;
+
+static inline int ns_to_t(int nanoseconds)
+{
+ const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2;
+
+ return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000);
+}
+
+#endif /* _SUNXI_DRAM_SUN20I_D1_H */
diff --git a/drivers/serial/serial_meson.c b/drivers/serial/serial_meson.c
index 934de2ab23..be5f380f85 100644
--- a/drivers/serial/serial_meson.c
+++ b/drivers/serial/serial_meson.c
@@ -232,6 +232,7 @@ static const struct dm_serial_ops meson_serial_ops = {
static const struct udevice_id meson_serial_ids[] = {
{ .compatible = "amlogic,meson-uart" },
{ .compatible = "amlogic,meson-gx-uart" },
+ { .compatible = "amlogic,meson-a1-uart" },
{ }
};
diff --git a/drivers/video/hitachi_tx18d42vm_lcd.c b/drivers/video/hitachi_tx18d42vm_lcd.c
index 87c4d27438..95984fe3d3 100644
--- a/drivers/video/hitachi_tx18d42vm_lcd.c
+++ b/drivers/video/hitachi_tx18d42vm_lcd.c
@@ -10,6 +10,7 @@
#include <linux/delay.h>
#include <asm/gpio.h>
+#include <sunxi_gpio.h>
#include <errno.h>
/*
diff --git a/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c
index aa0e292866..1c747d98d7 100644
--- a/drivers/video/pwm_backlight.c
+++ b/drivers/video/pwm_backlight.c
@@ -100,8 +100,8 @@ static int enable_sequence(struct udevice *dev, int seq)
plat = dev_get_uclass_plat(priv->reg);
log_debug("Enable '%s', regulator '%s'/'%s'\n",
dev->name, priv->reg->name, plat->name);
- ret = regulator_set_enable(priv->reg, true);
- if (ret) {
+ ret = regulator_set_enable_if_allowed(priv->reg, true);
+ if (ret && ret != -ENOSYS) {
log_debug("Cannot enable regulator for PWM '%s'\n",
dev->name);
return log_ret(ret);
@@ -181,11 +181,10 @@ static int pwm_backlight_set_brightness(struct udevice *dev, int percent)
}
if (disable) {
dm_gpio_set_value(&priv->enable, 0);
- if (priv->reg) {
- ret = regulator_set_enable(priv->reg, false);
- if (ret)
- return log_ret(ret);
- }
+ ret = regulator_set_enable_if_allowed(priv->reg, false);
+ if (ret && ret != -ENOSYS)
+ return log_ret(ret);
+
priv->enabled = false;
}
diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index 6a6473eb0e..efb122b534 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -114,11 +114,11 @@ static int simple_panel_probe(struct udevice *dev)
const u32 dsi_data = dev_get_driver_data(dev);
int ret;
- if (CONFIG_IS_ENABLED(DM_REGULATOR) && priv->reg) {
- debug("%s: Enable regulator '%s'\n", __func__, priv->reg->name);
- ret = regulator_set_enable(priv->reg, true);
- if (ret)
- return ret;
+ ret = regulator_set_enable_if_allowed(priv->reg, true);
+ if (ret && ret != -ENOSYS) {
+ debug("%s: failed to enable regulator '%s' %d\n",
+ __func__, priv->reg->name, ret);
+ return ret;
}
switch (dsi_data) {
diff --git a/drivers/video/ssd2828.c b/drivers/video/ssd2828.c
index 4cdcbe7755..948f5e74d0 100644
--- a/drivers/video/ssd2828.c
+++ b/drivers/video/ssd2828.c
@@ -12,7 +12,6 @@
#include <common.h>
#include <malloc.h>
#include <mipi_display.h>
-#include <asm/arch/gpio.h>
#include <asm/gpio.h>
#include <linux/delay.h>
diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
index 9110a48482..8da44a1bb6 100644
--- a/drivers/video/sunxi/sunxi_display.c
+++ b/drivers/video/sunxi/sunxi_display.c
@@ -31,6 +31,7 @@
#include <malloc.h>
#include <video.h>
#include <dm/uclass-internal.h>
+#include <sunxi_gpio.h>
#include "../videomodes.h"
#include "../anx9804.h"
#include "../hitachi_tx18d42vm_lcd.h"
diff --git a/drivers/video/sunxi/sunxi_lcd.c b/drivers/video/sunxi/sunxi_lcd.c
index 8b9c3b2bfa..7a01cc343c 100644
--- a/drivers/video/sunxi/sunxi_lcd.c
+++ b/drivers/video/sunxi/sunxi_lcd.c
@@ -17,6 +17,7 @@
#include <asm/arch/lcdc.h>
#include <asm/global_data.h>
#include <asm/gpio.h>
+#include <sunxi_gpio.h>
struct sunxi_lcd_priv {
struct display_timing timing;
diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c
index 8c3404e085..b4cf4fad5e 100644
--- a/drivers/video/tegra20/tegra-dsi.c
+++ b/drivers/video/tegra20/tegra-dsi.c
@@ -831,11 +831,9 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
tegra_dsi_get_format(device->format, &priv->format);
- if (priv->avdd) {
- ret = regulator_set_enable(priv->avdd, true);
- if (ret)
- return ret;
- }
+ ret = regulator_set_enable_if_allowed(priv->avdd, true);
+ if (ret && ret != -ENOSYS)
+ return ret;
tegra_dsi_init_clocks(dev);