summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/imx8-ddrc.c
diff options
context:
space:
mode:
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