From: Paul Kocialkowski <[email protected]>

In previous generations of Allwinner SoCs, the memory bus (MBUS) access
arbitration was configured as part of the DRAM top registers. This is no
longer the case starting with the A133, which has a dedicated base
address for the bus arbiter that is now called NSI instead of MBUS.

NSI appears to be a later iteration of MBUS design, with new dedicated
registers that resemble the previous MBUS ones. Despite NSI not being
documented in the manual, the A133 BSP includes a nsi driver with some
description of the registers. Like previous generations, it implements
port arbitration priority for DRAM access and also supports an optional
QoS mode based on bandwidth limits.

Configuring port arbitration priority is especially important to make
sure that critical masters are not starved by other less important
ones. This is especially the case with the display engine that needs
to be able to fetch pixels in time for scanout and can easily be
starved by CPU or GPU access.

This introduces support for the NSI arbiter in the A133 DRAM init
code. The list and order of available ports are highly SoC-specific
and the default config values are set to match the BSP's defaults.

Signed-off-by: Paul Kocialkowski <[email protected]>
Sponsored-by: MEC Electronics GmbH <https://www.mec.at/>
---
 .../include/asm/arch-sunxi/cpu_sun50i_h6.h    |  4 ++
 .../include/asm/arch-sunxi/dram_sun50i_a133.h | 36 ++++++++++
 arch/arm/mach-sunxi/dram_sun50i_a133.c        | 67 ++++++++++++++++++-
 3 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h 
b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
index 2a9b086991c3..8e80c520706b 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
@@ -16,6 +16,10 @@
 
 #define SUNXI_GIC400_BASE              0x03020000
 
+#ifdef CONFIG_MACH_SUN50I_A133
+#define SUNXI_NSI_BASE                 0x03100000
+#endif
+
 #ifdef CONFIG_MACH_SUN50I_H6
 #define SUNXI_DRAM_COM_BASE            0x04002000
 #define SUNXI_DRAM_CTL0_BASE           0x04003000
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h 
b/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h
index a5fc6ad36560..eadec74cc2b6 100644
--- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h
+++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h
@@ -24,6 +24,42 @@ static inline int ns_to_t(int nanoseconds)
        return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000);
 }
 
+#define SUNXI_NSI_MODE_REG(i)          ((i) * 0x200 + 0x10)
+#define SUNXI_NSI_PRI_CFG_REG(i)       ((i) * 0x200 + 0x14)
+#define SUNXI_NSI_PRI_CFG_RD(v)                (((v) & 0x3) << 2)
+#define SUNXI_NSI_PRI_CFG_WR(v)                ((v) & 0x3)
+#define SUNXI_NSI_PRI_CFG_LOWEST       0
+#define SUNXI_NSI_PRI_CFG_LOW          1
+#define SUNXI_NSI_PRI_CFG_HIGH         2
+#define SUNXI_NSI_PRI_CFG_HIGHEST      3
+#define SUNXI_NSI_IO_CFG_REG(i)                ((i) * 0x200 + 0x18)
+#define SUNXI_NSI_IO_CFG_QOS_SEL_OUTPUT        0
+#define SUNXI_NSI_IO_CFG_QOS_SEL_INPUT 1
+#define SUNXI_NSI_ENABLE_REG(i)                ((i) * 0x200 + 0xc0)
+
+enum sunxi_nsi_port {
+       SUNXI_NSI_PORT_CPU      = 0,
+       SUNXI_NSI_PORT_GPU,
+       SUNXI_NSI_PORT_SD1,
+       SUNXI_NSI_PORT_MSTG,
+       SUNXI_NSI_PORT_GMAC0,
+       SUNXI_NSI_PORT_GMAC1,
+       SUNXI_NSI_PORT_USB0,
+       SUNXI_NSI_PORT_USB1,
+       SUNXI_NSI_PORT_NDFC,
+       SUNXI_NSI_PORT_DMAC,
+       SUNXI_NSI_PORT_CE,
+       SUNXI_NSI_PORT_DE0,
+       SUNXI_NSI_PORT_DE1,
+       SUNXI_NSI_PORT_VE,
+       SUNXI_NSI_PORT_CSI,
+       SUNXI_NSI_PORT_ISP,
+       SUNXI_NSI_PORT_G2D,
+       SUNXI_NSI_PORT_EINK,
+       SUNXI_NSI_PORT_IOMMU,
+       SUNXI_NSI_PORT_CPUS,
+};
+
 /* MBUS part is largely the same as in H6, except for one special register */
 #define MCTL_COM_UNK_008       0x008
 /* NOTE: This register has the same importance as mctl_ctl->clken in H616 */
diff --git a/arch/arm/mach-sunxi/dram_sun50i_a133.c 
b/arch/arm/mach-sunxi/dram_sun50i_a133.c
index 1496f99624dd..204810aecf2a 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_a133.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_a133.c
@@ -69,6 +69,66 @@ static const u8 phy_init[] = {
 };
 #endif
 
+static void nsi_configure_port(unsigned int port, u8 pri, u8 qos_sel)
+{
+       void *base = (void *)SUNXI_NSI_BASE;
+       u32 pri_cfg;
+
+       /* QoS with bandwidth limits is not supported, disable it. */
+       writel(0, base + SUNXI_NSI_MODE_REG(port));
+       writel(0, base + SUNXI_NSI_ENABLE_REG(port));
+
+       /*
+        * QoS direction selection should not be in use, but set it nevertheless
+        * to match the BSP behavior (in case it has some other meaning).
+        */
+       writel(qos_sel, base + SUNXI_NSI_IO_CFG_REG(port));
+
+       /* Port priority is always active. */
+       pri_cfg = SUNXI_NSI_PRI_CFG_RD(pri) | SUNXI_NSI_PRI_CFG_WR(pri);
+
+       writel(pri_cfg, base + SUNXI_NSI_PRI_CFG_REG(port));
+}
+
+#define NSI_CONF(port, pri, qos_sel) \
+       { SUNXI_NSI_PORT_ ## port, SUNXI_NSI_PRI_CFG_ ## pri, \
+         SUNXI_NSI_IO_CFG_QOS_SEL_ ## qos_sel }
+
+static void nsi_set_master_priority(void)
+{
+       struct {
+               unsigned int port;
+               u8 pri;
+               u8 qos_sel;
+       } ports[] = {
+               NSI_CONF(CPU,   LOWEST,         INPUT),
+               NSI_CONF(GPU,   LOWEST,         INPUT),
+               NSI_CONF(SD1,   LOWEST,         OUTPUT),
+               NSI_CONF(MSTG,  LOWEST,         OUTPUT),
+               NSI_CONF(GMAC0, LOWEST,         OUTPUT),
+               NSI_CONF(GMAC1, LOWEST,         OUTPUT),
+               NSI_CONF(USB0,  LOWEST,         OUTPUT),
+               NSI_CONF(USB1,  LOWEST,         OUTPUT),
+               NSI_CONF(NDFC,  LOWEST,         OUTPUT),
+               NSI_CONF(DMAC,  LOWEST,         OUTPUT),
+               NSI_CONF(CE,    LOWEST,         OUTPUT),
+               NSI_CONF(DE0,   HIGH,           INPUT),
+               NSI_CONF(DE1,   HIGH,           INPUT),
+               NSI_CONF(VE,    LOWEST,         INPUT),
+               NSI_CONF(CSI,   HIGH,           INPUT),
+               NSI_CONF(ISP,   HIGH,           INPUT),
+               NSI_CONF(G2D,   LOWEST,         INPUT),
+               NSI_CONF(EINK,  LOWEST,         OUTPUT),
+               NSI_CONF(IOMMU, HIGHEST,        INPUT),
+               NSI_CONF(CPUS,  LOWEST,         OUTPUT),
+       };
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(ports); i++)
+               nsi_configure_port(ports[i].port, ports[i].pri,
+                                  ports[i].qos_sel);
+}
+
 static void mctl_clk_init(u32 clk)
 {
        void * const ccm = (void *)SUNXI_CCM_BASE;
@@ -1184,6 +1244,7 @@ static const struct dram_para para = {
 unsigned long sunxi_dram_init(void)
 {
        struct dram_config config;
+       unsigned long size;
 
        /* Writing to undocumented SYS_CFG area, according to user manual. */
        setbits_le32(0x03000160, BIT(8));
@@ -1200,5 +1261,9 @@ unsigned long sunxi_dram_init(void)
              1U << config.bankgrps, 1U << config.ranks,
              16U << config.bus_full_width);
 
-       return calculate_dram_size(&config);
+       size = calculate_dram_size(&config);
+
+       nsi_set_master_priority();
+
+       return size;
 }
-- 
2.52.0

Reply via email to