summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/esdctl.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2012-10-16 09:43:55 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2012-12-06 13:43:08 +0100
commit4fa54b7e5d201a3962dbed318540855ef3553f19 (patch)
treeb084340b9f987e057739990cafba1cd7974b07fc /arch/arm/mach-imx/esdctl.c
parent65c1ee9cc0dcd49526403da9244128781596ecfc (diff)
downloadbarebox-4fa54b7e5d201a3962dbed318540855ef3553f19.tar.gz
barebox-4fa54b7e5d201a3962dbed318540855ef3553f19.tar.xz
ARM i.MX: Add driver to get sdram base and size
The code initializing the SDRAM controller is not at the same place where SDRAM is registered with barebox. To reduce the risk of registering wrong SDRAM sizes this patch adds a driver for the ESDCTL which reads back the configured SDRAM size and registers the memory found with barebox. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/arm/mach-imx/esdctl.c')
-rw-r--r--arch/arm/mach-imx/esdctl.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/esdctl.c b/arch/arm/mach-imx/esdctl.c
new file mode 100644
index 0000000000..fbb6564612
--- /dev/null
+++ b/arch/arm/mach-imx/esdctl.c
@@ -0,0 +1,344 @@
+/*
+ * esdctl.c - i.MX sdram controller functions
+ *
+ * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <io.h>
+#include <sizes.h>
+#include <init.h>
+#include <asm/barebox-arm.h>
+#include <asm/memory.h>
+#include <mach/esdctl.h>
+#include <mach/imx1-regs.h>
+#include <mach/imx21-regs.h>
+#include <mach/imx25-regs.h>
+#include <mach/imx27-regs.h>
+#include <mach/imx31-regs.h>
+#include <mach/imx35-regs.h>
+#include <mach/imx51-regs.h>
+#include <mach/imx53-regs.h>
+
+struct imx_esdctl_data {
+ unsigned long base0;
+ unsigned long base1;
+ void (*add_mem)(void *esdctlbase, struct imx_esdctl_data *);
+};
+
+/*
+ * v1 - found on i.MX1
+ */
+static inline unsigned long imx_v1_sdram_size(void __iomem *esdctlbase, int num)
+{
+ void __iomem *esdctl = esdctlbase + (num ? 4 : 0);
+ u32 ctlval = readl(esdctl);
+ unsigned long size;
+ int rows, cols, width = 2, banks = 4;
+
+ if (!(ctlval & ESDCTL0_SDE))
+ /* SDRAM controller disabled, so no RAM here */
+ return 0;
+
+ rows = ((ctlval >> 24) & 0x3) + 11;
+ cols = ((ctlval >> 20) & 0x3) + 8;
+
+ if (ctlval & (1 << 17))
+ width = 4;
+
+ size = (1 << cols) * (1 << rows) * banks * width;
+
+ if (size > SZ_64M)
+ size = SZ_64M;
+
+ return size;
+}
+
+/*
+ * v2 - found on i.MX25, i.MX27, i.MX31 and i.MX35
+ */
+static inline unsigned long imx_v2_sdram_size(void __iomem *esdctlbase, int num)
+{
+ void __iomem *esdctl = esdctlbase + (num ? IMX_ESDCTL1 : IMX_ESDCTL0);
+ u32 ctlval = readl(esdctl);
+ unsigned long size;
+ int rows, cols, width = 2, banks = 4;
+
+ if (!(ctlval & ESDCTL0_SDE))
+ /* SDRAM controller disabled, so no RAM here */
+ return 0;
+
+ rows = ((ctlval >> 24) & 0x7) + 11;
+ cols = ((ctlval >> 20) & 0x3) + 8;
+
+ if ((ctlval & ESDCTL0_DSIZ_MASK) == ESDCTL0_DSIZ_31_0)
+ width = 4;
+
+ size = (1 << cols) * (1 << rows) * banks * width;
+
+ if (size > SZ_256M)
+ size = SZ_256M;
+
+ return size;
+}
+
+/*
+ * v3 - found on i.MX51
+ */
+static inline unsigned long imx_v3_sdram_size(void __iomem *esdctlbase, int num)
+{
+ unsigned long size;
+
+ size = imx_v2_sdram_size(esdctlbase, num);
+
+ if (readl(esdctlbase + IMX_ESDMISC) & (1 << 6))
+ size *= 2;
+
+ if (size > SZ_256M)
+ size = SZ_256M;
+
+ return size;
+}
+
+#define IMX_ESDCTL_V4_ESDCTL 0x0
+#define IMX_ESDCTL_V4_ESDMISC 0x18
+
+#define ESDCTL_V4_ESDCTL_DSIZ (1 << 16)
+#define ESDCTL_V4_ESDMISC_DDR_4_BANK (1 << 5)
+#define ESDCTL_V4_ESDMISC_ONECS (1 << 20)
+
+#define ESDCTL_V4_ESDCTL_SDE_0 (1 << 31)
+#define ESDCTL_V4_ESDCTL_SDE_1 (1 << 30)
+#define ESDCTL_V4_ESDMISC_BI (1 << 12)
+
+/*
+ * v4 - found on i.MX53
+ */
+static inline unsigned long imx_v4_sdram_size(void __iomem *esdctlbase, int cs)
+{
+ u32 ctlval = readl(esdctlbase + IMX_ESDCTL_V4_ESDCTL);
+ u32 esdmisc = readl(esdctlbase + IMX_ESDCTL_V4_ESDMISC);
+ unsigned long size;
+ int rows, cols, width = 2, banks = 8;
+
+ if (cs == 0 && !(ctlval & ESDCTL_V4_ESDCTL_SDE_0))
+ return 0;
+ if (cs == 1 && !(ctlval & ESDCTL_V4_ESDCTL_SDE_1))
+ return 0;
+
+ /* one 2GiB cs, memory is returned for cs0 only */
+ if (cs == 1 && (esdmisc & ESDCTL_V4_ESDMISC_ONECS))
+ return 9;
+
+ rows = ((ctlval >> 24) & 0x7) + 11;
+ switch ((ctlval >> 20) & 0x7) {
+ case 0:
+ cols = 9;
+ break;
+ case 1:
+ cols = 10;
+ break;
+ case 2:
+ cols = 11;
+ break;
+ case 3:
+ cols = 8;
+ break;
+ case 4:
+ cols = 12;
+ break;
+ default:
+ cols = 0;
+ break;
+ }
+
+ if (ctlval & ESDCTL_V4_ESDCTL_DSIZ)
+ width = 4;
+
+ if (esdmisc & ESDCTL_V4_ESDMISC_DDR_4_BANK)
+ banks = 4;
+
+ size = (1 << cols) * (1 << rows) * banks * width;
+
+ return size;
+}
+
+static void add_mem(unsigned long base0, unsigned long size0,
+ unsigned long base1, unsigned long size1)
+{
+ debug("%s: cs0 base: 0x%08x cs0 size: 0x%08x\n", __func__, base0, size0);
+ debug("%s: cs1 base: 0x%08x cs1 size: 0x%08x\n", __func__, base1, size1);
+
+ if (base0 + size0 == base1 && size1 > 0) {
+ /*
+ * concatenate both chip selects to a single bank
+ */
+ arm_add_mem_device("ram0", base0, size0 + size1);
+
+ return;
+ }
+
+ if (size0)
+ arm_add_mem_device("ram0", base0, size0);
+
+ if (size1)
+ arm_add_mem_device(size0 ? "ram1" : "ram0", base1, size1);
+}
+
+static void imx_esdctl_v1_add_mem(void *esdctlbase, struct imx_esdctl_data *data)
+{
+ add_mem(data->base0, imx_v1_sdram_size(esdctlbase, 0),
+ data->base1, imx_v1_sdram_size(esdctlbase, 1));
+}
+
+static void imx_esdctl_v2_add_mem(void *esdctlbase, struct imx_esdctl_data *data)
+{
+ add_mem(data->base0, imx_v2_sdram_size(esdctlbase, 0),
+ data->base1, imx_v2_sdram_size(esdctlbase, 1));
+}
+
+static void imx_esdctl_v3_add_mem(void *esdctlbase, struct imx_esdctl_data *data)
+{
+ add_mem(data->base0, imx_v3_sdram_size(esdctlbase, 0),
+ data->base1, imx_v3_sdram_size(esdctlbase, 1));
+}
+
+static void imx_esdctl_v4_add_mem(void *esdctlbase, struct imx_esdctl_data *data)
+{
+ add_mem(data->base0, imx_v4_sdram_size(esdctlbase, 0),
+ data->base1, imx_v4_sdram_size(esdctlbase, 1));
+}
+
+static int imx_esdctl_probe(struct device_d *dev)
+{
+ struct imx_esdctl_data *data;
+ int ret;
+ void *base;
+
+ ret = dev_get_drvdata(dev, (unsigned long *)&data);
+ if (ret)
+ return ret;
+
+ base = dev_request_mem_region(dev, 0);
+ if (!base)
+ return -ENOMEM;
+
+ data->add_mem(base, data);
+
+ return 0;
+}
+
+static __maybe_unused struct imx_esdctl_data imx1_data = {
+ .base0 = MX1_CSD0_BASE_ADDR,
+ .base1 = MX1_CSD1_BASE_ADDR,
+ .add_mem = imx_esdctl_v1_add_mem,
+};
+
+static __maybe_unused struct imx_esdctl_data imx25_data = {
+ .base0 = MX25_CSD0_BASE_ADDR,
+ .base1 = MX25_CSD1_BASE_ADDR,
+ .add_mem = imx_esdctl_v2_add_mem,
+};
+
+static __maybe_unused struct imx_esdctl_data imx27_data = {
+ .base0 = MX27_CSD0_BASE_ADDR,
+ .base1 = MX27_CSD1_BASE_ADDR,
+ .add_mem = imx_esdctl_v2_add_mem,
+};
+
+static __maybe_unused struct imx_esdctl_data imx31_data = {
+ .base0 = MX31_CSD0_BASE_ADDR,
+ .base1 = MX31_CSD1_BASE_ADDR,
+ .add_mem = imx_esdctl_v2_add_mem,
+};
+
+static __maybe_unused struct imx_esdctl_data imx35_data = {
+ .base0 = MX35_CSD0_BASE_ADDR,
+ .base1 = MX35_CSD1_BASE_ADDR,
+ .add_mem = imx_esdctl_v2_add_mem,
+};
+
+static __maybe_unused struct imx_esdctl_data imx51_data = {
+ .base0 = MX51_CSD0_BASE_ADDR,
+ .base1 = MX51_CSD1_BASE_ADDR,
+ .add_mem = imx_esdctl_v3_add_mem,
+};
+
+static __maybe_unused struct imx_esdctl_data imx53_data = {
+ .base0 = MX53_CSD0_BASE_ADDR,
+ .base1 = MX53_CSD1_BASE_ADDR,
+ .add_mem = imx_esdctl_v4_add_mem,
+};
+
+static struct platform_device_id imx_esdctl_ids[] = {
+#ifdef CONFIG_ARCH_IMX1
+ {
+ .name = "imx1-sdramc",
+ .driver_data = (unsigned long)&imx1_data,
+ },
+#endif
+#ifdef CONFIG_ARCH_IMX25
+ {
+ .name = "imx25-esdctl",
+ .driver_data = (unsigned long)&imx25_data,
+ },
+#endif
+#ifdef CONFIG_ARCH_IMX27
+ {
+ .name = "imx27-esdctl",
+ .driver_data = (unsigned long)&imx27_data,
+ },
+#endif
+#ifdef CONFIG_ARCH_IMX31
+ {
+ .name = "imx31-esdctl",
+ .driver_data = (unsigned long)&imx31_data,
+ },
+#endif
+#ifdef CONFIG_ARCH_IMX35
+ {
+ .name = "imx35-esdctl",
+ .driver_data = (unsigned long)&imx35_data,
+ },
+#endif
+#ifdef CONFIG_ARCH_IMX51
+ {
+ .name = "imx51-esdctl",
+ .driver_data = (unsigned long)&imx51_data,
+ },
+#endif
+#ifdef CONFIG_ARCH_IMX53
+ {
+ .name = "imx53-esdctl",
+ .driver_data = (unsigned long)&imx53_data,
+ },
+#endif
+ {
+ /* sentinel */
+ },
+};
+
+static struct driver_d imx_serial_driver = {
+ .name = "imx-esdctl",
+ .probe = imx_esdctl_probe,
+ .id_table = imx_esdctl_ids,
+};
+
+static int imx_esdctl_init(void)
+{
+ return platform_driver_register(&imx_serial_driver);
+}
+
+mem_initcall(imx_esdctl_init);