From: jean-jacques hiblot <jean-jacques.hib...@jdsu.com>
The EBI/SMC external interface is used to access external peripherals
(NAND
and Ethernet controller in the case of sam9261ek). Different
configurations and
timings are required for those peripherals. This bus driver can be used
to
setup the bus timings/configuration from the device tree.
Signed-off-by: Jean-Jacques Hiblot <jjhib...@traphandler.com>
---
drivers/bus/Kconfig | 9 +++
drivers/bus/Makefile | 1 +
drivers/bus/atmel-smc.c | 182
++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 192 insertions(+)
create mode 100644 drivers/bus/atmel-smc.c
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 552373c..8c944db5 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -12,6 +12,15 @@ config IMX_WEIM
The WEIM(Wireless External Interface Module) works like a bus.
You can attach many different devices on it, such as NOR,
onenand.
+config ATMEL_SMC
+ bool "Atmel SMC/EBI driver"
+ depends on SOC_AT91SAM9 && OF
+ help
+ Driver for Atmel SMC/EBI controller.
+ Used to configure the EBI (external bus interface) when the
device-
+ tree is used. This bus supports NANDs, external ethernet
controller,
+ SRAMs, ATA devices.
+
config MVEBU_MBUS
bool
depends on PLAT_ORION
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 8947bdd..4364003 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -2,6 +2,7 @@
# Makefile for the bus drivers.
#
+obj-$(CONFIG_ATMEL_SMC) += atmel-smc.o
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
diff --git a/drivers/bus/atmel-smc.c b/drivers/bus/atmel-smc.c
new file mode 100644
index 0000000..06e530d
--- /dev/null
+++ b/drivers/bus/atmel-smc.c
@@ -0,0 +1,182 @@
+/*
+ * EBI driver for Atmel SAM9 chips
+ * inspired by the fsl weim bus driver
+ *
+ * Copyright (C) 2013 JJ Hiblot.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <mach/at91sam9_smc.h>
+
+struct at91_smc_devtype {
+ unsigned int cs_count;
+};
+
+static const struct at91_smc_devtype sam9261_smc_devtype = {
+ .cs_count = 6,
+};
+
+static const struct of_device_id smc_id_table[] = {
+ { .compatible = "atmel,at91sam9261-smc", .data =
&sam9261_smc_devtype},
+ { }
+};
+MODULE_DEVICE_TABLE(of, smc_id_table);
+
+struct smc_parameters_type {
+ const char *name;
+ u16 reg;
+ u16 width;
+ u16 shift;
+};
+
+#define SETUP 0
+#define PULSE 1
+#define CYCLE 2
+#define MODE 3
+static const struct smc_parameters_type smc_parameters[] = {
+ {"smc,ncs_read_setup", SETUP, 6, 24},
+ {"smc,nrd_setup", SETUP, 6, 16},
+ {"smc,ncs_write_setup", SETUP, 6, 8},
+ {"smc,nwe_setup", SETUP, 6, 0},
+ {"smc,ncs_read_pulse", PULSE, 6, 24},
+ {"smc,nrd_pulse", PULSE, 6, 16},
+ {"smc,ncs_write_pulse", PULSE, 6, 8},
+ {"smc,nwe_pulse", PULSE, 6, 0},
+ {"smc,read_cycle", CYCLE, 9, 16},
+ {"smc,write_cycle", CYCLE, 9, 0},
+ {"smc,burst_size", MODE, 2, 28},
+ {"smc,burst_enabled", MODE, 1, 24},
+ {"smc,tdf_mode", MODE, 1, 20},
+ {"smc,tdf_cycles", MODE, 4, 16},
+ {"smc,bus_width", MODE, 2, 12},
+ {"smc,byte_access_type", MODE, 1, 8},
+ {"smc,nwait_mode", MODE, 2, 4},
+ {"smc,write_mode", MODE, 1, 0},
+ {"smc,read_mode", MODE, 1, 1},
+ {NULL}
+};
+
+/* Parse and set the timing for this device. */
+static int __init smc_timing_setup(struct device *dev, struct device_node
*np,
+ void __iomem *base, const struct at91_smc_devtype
*devtype)
+{
+ u32 val;
+ int ret;
+ u32 cs;
+ const struct smc_parameters_type *p = smc_parameters;
+ u32 shadow_smc_regs[5];
+
+ ret = of_property_read_u32(np, "smc,cs" , &cs);
+ if (ret < 0) {
+ dev_err(dev, "missing mandatory property : smc,cs\n");
+ return ret;
+ }
+ if (val >= devtype->cs_count) {
+ dev_err(dev, "invalid value for property smc,cs (=%d)."
+ "Must be in range 0 to %d\n", cs, devtype->cs_count-1);
+ return -EINVAL;
+ }
+
+ /* set the timing for EBI */
+ base += (0x10 * cs);
+ shadow_smc_regs[SETUP] = readl_relaxed(base + AT91_SMC_SETUP);
+ shadow_smc_regs[PULSE] = readl_relaxed(base + AT91_SMC_PULSE);
+ shadow_smc_regs[CYCLE] = readl_relaxed(base + AT91_SMC_CYCLE);
+ shadow_smc_regs[MODE] = readl_relaxed(base + AT91_SMC_MODE);
+
+ while (p->name) {
+ ret = of_property_read_u32(np, p->name , &val);
+ if (ret == -EINVAL) {
+ dev_dbg(dev, "cs %d: property %s not set.\n", cs,
+ p->name);
+ p++;
+ continue;
+ } else if (ret) {
+ dev_err(dev, "cs %d: can't get property %s.\n",
cs,
+ p->name);
+ return ret;
+ }
+ if (val >= (1<<p->width)) {
+ dev_err(dev, "cs %d: property %s out of range.\n",
cs,
+ p->name);
+ return -ERANGE;
+ }
+ shadow_smc_regs[p->reg] &= ~(((1<<p->width)-1) <<
p->shift);
+ shadow_smc_regs[p->reg] |= (val << p->shift);
+ p++;
+ }
+ writel_relaxed(shadow_smc_regs[SETUP], base + AT91_SMC_SETUP);
+ writel_relaxed(shadow_smc_regs[PULSE], base + AT91_SMC_PULSE);
+ writel_relaxed(shadow_smc_regs[CYCLE], base + AT91_SMC_CYCLE);
+ writel_relaxed(shadow_smc_regs[MODE], base + AT91_SMC_MODE);
+ return 0;
+}