summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/imx8-ddrc.c
diff options
context:
space:
mode:
authorAndrey Smirnov <andrew.smirnov@gmail.com>2018-06-14 20:44:03 -0700
committerSascha Hauer <s.hauer@pengutronix.de>2018-06-15 09:01:09 +0200
commitca1a60b1d254ae18d72e6f3e70c041c313f02756 (patch)
tree23e65504fe8db23a50081af08acb9db883b321b7 /arch/arm/mach-imx/imx8-ddrc.c
parentf51469032fd6bd36133c779491e080e3ff9fb3a5 (diff)
downloadbarebox-ca1a60b1d254ae18d72e6f3e70c041c313f02756.tar.gz
barebox-ca1a60b1d254ae18d72e6f3e70c041c313f02756.tar.xz
ARM: i.MX8: Add DDRC PHY support code
Add DDRC PHY support code needed to upload DDR training firwmare as well as to wait for the training process to complete. Those are needed to support board specific DDR initialization code. Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/arm/mach-imx/imx8-ddrc.c')
-rw-r--r--arch/arm/mach-imx/imx8-ddrc.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/imx8-ddrc.c b/arch/arm/mach-imx/imx8-ddrc.c
new file mode 100644
index 0000000000..18454a9153
--- /dev/null
+++ b/arch/arm/mach-imx/imx8-ddrc.c
@@ -0,0 +1,107 @@
+#include <common.h>
+#include <linux/iopoll.h>
+#include <mach/imx8-ddrc.h>
+#include <debug_ll.h>
+
+void ddrc_phy_load_firmware(void __iomem *phy,
+ enum ddrc_phy_firmware_offset offset,
+ const u16 *blob, size_t size)
+{
+ while (size) {
+ writew(*blob++, phy + DDRC_PHY_REG(offset));
+ offset++;
+ size -= sizeof(*blob);
+ }
+}
+
+enum pmc_constants {
+ PMC_MESSAGE_ID,
+ PMC_MESSAGE_STREAM,
+
+ PMC_TRAIN_SUCCESS = 0x07,
+ PMC_TRAIN_STREAM_START = 0x08,
+ PMC_TRAIN_FAIL = 0xff,
+};
+
+static u32 ddrc_phy_get_message(void __iomem *phy, int type)
+{
+ u32 r, message;
+
+ /*
+ * When BIT0 set to 0, the PMU has a message for the user
+ * 10ms seems not enough for poll message, so use 1s here.
+ */
+ readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004),
+ r, !(r & BIT(0)), 0);
+
+ switch (type) {
+ case PMC_MESSAGE_ID:
+ /*
+ * Get the major message ID
+ */
+ message = readl(phy + DDRC_PHY_REG(0xd0032));
+ break;
+ case PMC_MESSAGE_STREAM:
+ message = readl(phy + DDRC_PHY_REG(0xd0034));
+ message <<= 16;
+ message |= readl(phy + DDRC_PHY_REG(0xd0032));
+ break;
+ }
+
+ /*
+ * By setting this register to 0, the user acknowledges the
+ * receipt of the message.
+ */
+ writel(0x00000000, phy + DDRC_PHY_REG(0xd0031));
+ /*
+ * When BIT0 set to 0, the PMU has a message for the user
+ */
+ readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004),
+ r, r & BIT(0), 0);
+
+ writel(0x00000001, phy + DDRC_PHY_REG(0xd0031));
+
+ return message;
+}
+
+static void ddrc_phy_fetch_streaming_message(void __iomem *phy)
+{
+ const u16 index = ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM);
+ u16 i;
+
+ putc_ll('|');
+ puthex_ll(index);
+
+ for (i = 0; i < index; i++) {
+ const u32 arg = ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM);
+
+ putc_ll('|');
+ puthex_ll(arg);
+ }
+}
+
+void ddrc_phy_wait_training_complete(void __iomem *phy)
+{
+ for (;;) {
+ const u32 m = ddrc_phy_get_message(phy, PMC_MESSAGE_ID);
+
+ puthex_ll(m);
+
+ switch (m) {
+ case PMC_TRAIN_STREAM_START:
+ ddrc_phy_fetch_streaming_message(phy);
+ break;
+ case PMC_TRAIN_SUCCESS:
+ putc_ll('P');
+ putc_ll('\r');
+ putc_ll('\n');
+ return;
+ case PMC_TRAIN_FAIL:
+ putc_ll('F');
+ hang();
+ }
+
+ putc_ll('\r');
+ putc_ll('\n');
+ }
+} \ No newline at end of file