[PATCH 3/3] bus: Add omap gpmc driver

Sascha Hauer s.hauer at pengutronix.de
Tue Nov 26 07:17:20 EST 2013


This adds a devicetree-only driver for to configure the gpmc and its
child devices from dt. Currently only NAND is supported.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 arch/arm/mach-omap/include/mach/gpmc_nand.h |   3 +
 drivers/bus/Kconfig                         |   6 +
 drivers/bus/Makefile                        |   1 +
 drivers/bus/omap-gpmc.c                     | 480 ++++++++++++++++++++++++++++
 4 files changed, 490 insertions(+)
 create mode 100644 drivers/bus/omap-gpmc.c

diff --git a/arch/arm/mach-omap/include/mach/gpmc_nand.h b/arch/arm/mach-omap/include/mach/gpmc_nand.h
index 8d138ec..8839486 100644
--- a/arch/arm/mach-omap/include/mach/gpmc_nand.h
+++ b/arch/arm/mach-omap/include/mach/gpmc_nand.h
@@ -59,6 +59,9 @@ struct gpmc_nand_platform_data {
 	struct nand_ecclayout *oob;
 	/** gpmc config for nand */
 	struct gpmc_config *nand_cfg;
+
+	struct device_node *of_node;
+	struct device_node *elm_of_node;
 };
 
 int omap_add_gpmc_nand_device(struct gpmc_nand_platform_data *pdata);
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 5938d3f..b0d3c1f 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -4,4 +4,10 @@ config IMX_WEIM
 	depends on ARCH_IMX
 	bool "i.MX WEIM driver"
 
+config BUS_OMAP_GPMC
+	depends on ARCH_OMAP
+	depends on OFDEVICE
+	depends on OMAP_GPMC
+	bool "TI OMAP/AM33xx GPMC support"
+
 endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 42a8d49..f1c5ac7 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_IMX_WEIM) += imx-weim.o
+obj-$(CONFIG_BUS_OMAP_GPMC) += omap-gpmc.o
diff --git a/drivers/bus/omap-gpmc.c b/drivers/bus/omap-gpmc.c
new file mode 100644
index 0000000..6424785
--- /dev/null
+++ b/drivers/bus/omap-gpmc.c
@@ -0,0 +1,480 @@
+/*
+ * OMAP GPMC driver. Based upon the corresponding Linux Code
+ *
+ * Copyright (C) 2013 Sascha Hauer, Pengutronix, <s.hauer at pengutronix.de>
+ *
+ * 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 <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <init.h>
+#include <sizes.h>
+#include <io.h>
+#include <of.h>
+#include <of_address.h>
+#include <of_mtd.h>
+#include <linux/clk.h>
+#include <mach/gpmc_nand.h>
+#include <mach/gpmc.h>
+
+#define GPMC_CS_NUM	8
+#define GPMC_NR_WAITPINS		4
+
+#define GPMC_CONFIG1_WRAPBURST_SUPP		(1 << 31)
+#define GPMC_CONFIG1_READMULTIPLE_SUPP		(1 << 30)
+#define GPMC_CONFIG1_READTYPE_ASYNC		(0 << 29)
+#define GPMC_CONFIG1_READTYPE_SYNC		(1 << 29)
+#define GPMC_CONFIG1_WRITEMULTIPLE_SUPP		(1 << 28)
+#define GPMC_CONFIG1_WRITETYPE_ASYNC		(0 << 27)
+#define GPMC_CONFIG1_WRITETYPE_SYNC		(1 << 27)
+#define GPMC_CONFIG1_CLKACTIVATIONTIME(val)	((val & 3) << 25)
+#define GPMC_CONFIG1_PAGE_LEN(val)		((val & 3) << 23)
+#define GPMC_CONFIG1_WAIT_READ_MON		(1 << 22)
+#define GPMC_CONFIG1_WAIT_WRITE_MON		(1 << 21)
+#define GPMC_CONFIG1_WAIT_MON_IIME(val)		((val & 3) << 18)
+#define GPMC_CONFIG1_WAIT_PIN_SEL(val)		((val & 3) << 16)
+#define GPMC_CONFIG1_DEVICESIZE(val)		((val & 3) << 12)
+#define GPMC_CONFIG1_DEVICESIZE_16		GPMC_CONFIG1_DEVICESIZE(1)
+#define GPMC_CONFIG1_DEVICETYPE(val)		((val & 3) << 10)
+#define GPMC_CONFIG1_DEVICETYPE_NOR		GPMC_CONFIG1_DEVICETYPE(0)
+#define GPMC_CONFIG1_MUXTYPE(val)		((val & 3) << 8)
+#define GPMC_CONFIG1_TIME_PARA_GRAN		(1 << 4)
+#define GPMC_CONFIG1_FCLK_DIV(val)		(val & 3)
+#define GPMC_CONFIG1_FCLK_DIV2			(GPMC_CONFIG1_FCLK_DIV(1))
+#define GPMC_CONFIG1_FCLK_DIV3			(GPMC_CONFIG1_FCLK_DIV(2))
+#define GPMC_CONFIG1_FCLK_DIV4			(GPMC_CONFIG1_FCLK_DIV(3))
+#define	GPMC_CONFIG2_CSEXTRADELAY		(1 << 7)
+#define	GPMC_CONFIG3_ADVEXTRADELAY		(1 << 7)
+#define	GPMC_CONFIG4_OEEXTRADELAY		(1 << 7)
+#define	GPMC_CONFIG4_WEEXTRADELAY		(1 << 23)
+#define	GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN	(1 << 6)
+#define	GPMC_CONFIG6_CYCLE2CYCLESAMECSEN	(1 << 7)
+#define GPMC_CONFIG7_CSVALID			(1 << 6)
+
+static unsigned int gpmc_cs_num = GPMC_CS_NUM;
+static unsigned int gpmc_nr_waitpins;
+static unsigned long gpmc_l3_clk = 100000000; /* This should be a proper clock */
+static void __iomem *gpmc_base;
+
+/* bool type time settings */
+struct gpmc_bool_timings {
+	bool cycle2cyclediffcsen;
+	bool cycle2cyclesamecsen;
+	bool we_extra_delay;
+	bool oe_extra_delay;
+	bool adv_extra_delay;
+	bool cs_extra_delay;
+	bool time_para_granularity;
+};
+
+/*
+ * Note that all values in this struct are in nanoseconds except sync_clk
+ * (which is in picoseconds), while the register values are in gpmc_fck cycles.
+ */
+struct gpmc_timings {
+	/* Minimum clock period for synchronous mode (in picoseconds) */
+	u32 sync_clk;
+
+	/* Chip-select signal timings corresponding to 1 */
+	u32 cs_on;		/* Assertion time */
+	u32 cs_rd_off;		/* Read deassertion time */
+	u32 cs_wr_off;		/* Write deassertion time */
+
+	/* ADV signal timings corresponding to GPMC_CONFIG3 */
+	u32 adv_on;		/* Assertion time */
+	u32 adv_rd_off;		/* Read deassertion time */
+	u32 adv_wr_off;		/* Write deassertion time */
+
+	/* WE signals timings corresponding to GPMC_CONFIG4 */
+	u32 we_on;		/* WE assertion time */
+	u32 we_off;		/* WE deassertion time */
+
+	/* OE signals timings corresponding to GPMC_CONFIG4 */
+	u32 oe_on;		/* OE assertion time */
+	u32 oe_off;		/* OE deassertion time */
+
+	/* Access time and cycle time timings corresponding to GPMC_CONFIG5 */
+	u32 page_burst_access;	/* Multiple access word delay */
+	u32 access;		/* Start-cycle to first data valid delay */
+	u32 rd_cycle;		/* Total read cycle time */
+	u32 wr_cycle;		/* Total write cycle time */
+
+	u32 bus_turnaround;
+	u32 cycle2cycle_delay;
+
+	u32 wait_monitoring;
+	u32 clk_activation;
+
+	/* The following are only on OMAP3430 */
+	u32 wr_access;		/* WRACCESSTIME */
+	u32 wr_data_mux_bus;	/* WRDATAONADMUXBUS */
+
+	struct gpmc_bool_timings bool_timings;
+};
+
+struct gpmc_settings {
+	bool burst_wrap;	/* enables wrap bursting */
+	bool burst_read;	/* enables read page/burst mode */
+	bool burst_write;	/* enables write page/burst mode */
+	bool device_nand;	/* device is NAND */
+	bool sync_read;		/* enables synchronous reads */
+	bool sync_write;	/* enables synchronous writes */
+	bool wait_on_read;	/* monitor wait on reads */
+	bool wait_on_write;	/* monitor wait on writes */
+	u32 burst_len;		/* page/burst length */
+	u32 device_width;	/* device bus width (8 or 16 bit) */
+	u32 mux_add_data;	/* multiplex address & data */
+	u32 wait_pin;		/* wait-pin to be used */
+};
+
+struct imx_gpmc {
+	struct device_d *dev;
+	void __iomem *base;
+	struct imx_gpmc_devtype *devtype;
+};
+
+static void gpmc_cs_bool_timings(struct gpmc_config *gpmc_config, const struct gpmc_bool_timings *p)
+{
+	if (p->time_para_granularity)
+		gpmc_config->cfg[0] |= GPMC_CONFIG1_TIME_PARA_GRAN;
+
+	if (p->cs_extra_delay)
+		gpmc_config->cfg[1] |= GPMC_CONFIG2_CSEXTRADELAY;
+	if (p->adv_extra_delay)
+		gpmc_config->cfg[2] |= GPMC_CONFIG3_ADVEXTRADELAY;
+	if (p->oe_extra_delay)
+		gpmc_config->cfg[3] |= GPMC_CONFIG4_OEEXTRADELAY;
+	if (p->we_extra_delay)
+		gpmc_config->cfg[3] |= GPMC_CONFIG4_OEEXTRADELAY;
+	if (p->cycle2cyclesamecsen)
+		gpmc_config->cfg[5] |= GPMC_CONFIG6_CYCLE2CYCLESAMECSEN;
+	if (p->cycle2cyclediffcsen)
+		gpmc_config->cfg[5] |= GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN;
+}
+
+static unsigned long gpmc_get_fclk_period(void)
+{
+	unsigned long rate = gpmc_l3_clk;
+
+	rate /= 1000;
+	rate = 1000000000 / rate;	/* In picoseconds */
+
+	return rate;
+}
+
+static unsigned int gpmc_ns_to_ticks(unsigned int time_ns)
+{
+	unsigned long tick_ps;
+
+	/* Calculate in picosecs to yield more exact results */
+	tick_ps = gpmc_get_fclk_period();
+
+	return (time_ns * 1000 + tick_ps - 1) / tick_ps;
+}
+
+int gpmc_calc_divider(unsigned int sync_clk)
+{
+	int div;
+	u32 l;
+
+	l = sync_clk + (gpmc_get_fclk_period() - 1);
+	div = l / gpmc_get_fclk_period();
+	if (div > 4)
+		return -1;
+	if (div <= 0)
+		div = 1;
+
+	return div;
+}
+
+static int set_cfg(struct gpmc_config *gpmc_config, int reg,
+		int st_bit, int end_bit, int time)
+{
+	int ticks, mask, nr_bits;
+
+	if (time == 0)
+		ticks = 0;
+	else
+		ticks = gpmc_ns_to_ticks(time);
+
+	nr_bits = end_bit - st_bit + 1;
+	if (ticks >= 1 << nr_bits)
+		return -EINVAL;
+
+	mask = (1 << nr_bits) - 1;
+	gpmc_config->cfg[reg] &= ~(mask << st_bit);
+	gpmc_config->cfg[reg] |= ticks << st_bit;
+
+	return 0;
+}
+
+static int gpmc_timings_to_config(struct gpmc_config *gpmc_config, const struct gpmc_timings *t)
+{
+	int div, ret = 0;
+
+	div = gpmc_calc_divider(t->sync_clk);
+	if (div < 0)
+		return div;
+
+	ret |= set_cfg(gpmc_config, 0, 18, 19, t->wait_monitoring);
+	ret |= set_cfg(gpmc_config, 0, 25, 26, t->clk_activation);
+
+	ret |= set_cfg(gpmc_config, 1,  0,  3, t->cs_on);
+	ret |= set_cfg(gpmc_config, 1,  8, 12, t->cs_rd_off);
+	ret |= set_cfg(gpmc_config, 1, 16, 20, t->cs_wr_off);
+
+	ret |= set_cfg(gpmc_config, 2,  0,  3, t->adv_on);
+	ret |= set_cfg(gpmc_config, 2,  8, 12, t->adv_rd_off);
+	ret |= set_cfg(gpmc_config, 2, 16, 20, t->adv_wr_off);
+
+	ret |= set_cfg(gpmc_config, 3,  0,  3, t->oe_on);
+	ret |= set_cfg(gpmc_config, 3,  8, 12, t->oe_off);
+	ret |= set_cfg(gpmc_config, 3, 16, 19, t->we_on);
+	ret |= set_cfg(gpmc_config, 3, 24, 28, t->we_off);
+
+	ret |= set_cfg(gpmc_config, 4,  0,  4, t->rd_cycle);
+	ret |= set_cfg(gpmc_config, 4,  8, 12, t->wr_cycle);
+	ret |= set_cfg(gpmc_config, 4, 16, 20, t->access);
+
+	ret |= set_cfg(gpmc_config, 4, 24, 27, t->page_burst_access);
+
+	ret |= set_cfg(gpmc_config, 5, 0, 3, t->bus_turnaround);
+	ret |= set_cfg(gpmc_config, 5, 8, 11, t->cycle2cycle_delay);
+	ret |= set_cfg(gpmc_config, 5, 16, 19, t->wr_data_mux_bus);
+	ret |= set_cfg(gpmc_config, 5, 24, 28, t->wr_access);
+
+	if (ret)
+		return ret;
+
+	gpmc_cs_bool_timings(gpmc_config, &t->bool_timings);
+
+	return 0;
+}
+
+static void gpmc_read_timings_dt(struct device_node *np,
+						struct gpmc_timings *gpmc_t)
+{
+	struct gpmc_bool_timings *p;
+
+	if (!np || !gpmc_t)
+		return;
+
+	memset(gpmc_t, 0, sizeof(*gpmc_t));
+
+	/* minimum clock period for syncronous mode */
+	of_property_read_u32(np, "gpmc,sync-clk-ps", &gpmc_t->sync_clk);
+
+	/* chip select timtings */
+	of_property_read_u32(np, "gpmc,cs-on-ns", &gpmc_t->cs_on);
+	of_property_read_u32(np, "gpmc,cs-rd-off-ns", &gpmc_t->cs_rd_off);
+	of_property_read_u32(np, "gpmc,cs-wr-off-ns", &gpmc_t->cs_wr_off);
+
+	/* ADV signal timings */
+	of_property_read_u32(np, "gpmc,adv-on-ns", &gpmc_t->adv_on);
+	of_property_read_u32(np, "gpmc,adv-rd-off-ns", &gpmc_t->adv_rd_off);
+	of_property_read_u32(np, "gpmc,adv-wr-off-ns", &gpmc_t->adv_wr_off);
+
+	/* WE signal timings */
+	of_property_read_u32(np, "gpmc,we-on-ns", &gpmc_t->we_on);
+	of_property_read_u32(np, "gpmc,we-off-ns", &gpmc_t->we_off);
+
+	/* OE signal timings */
+	of_property_read_u32(np, "gpmc,oe-on-ns", &gpmc_t->oe_on);
+	of_property_read_u32(np, "gpmc,oe-off-ns", &gpmc_t->oe_off);
+
+	/* access and cycle timings */
+	of_property_read_u32(np, "gpmc,page-burst-access-ns",
+			     &gpmc_t->page_burst_access);
+	of_property_read_u32(np, "gpmc,access-ns", &gpmc_t->access);
+	of_property_read_u32(np, "gpmc,rd-cycle-ns", &gpmc_t->rd_cycle);
+	of_property_read_u32(np, "gpmc,wr-cycle-ns", &gpmc_t->wr_cycle);
+	of_property_read_u32(np, "gpmc,bus-turnaround-ns",
+			     &gpmc_t->bus_turnaround);
+	of_property_read_u32(np, "gpmc,cycle2cycle-delay-ns",
+			     &gpmc_t->cycle2cycle_delay);
+	of_property_read_u32(np, "gpmc,wait-monitoring-ns",
+			     &gpmc_t->wait_monitoring);
+	of_property_read_u32(np, "gpmc,clk-activation-ns",
+			     &gpmc_t->clk_activation);
+
+	/* only applicable to OMAP3+ */
+	of_property_read_u32(np, "gpmc,wr-access-ns", &gpmc_t->wr_access);
+	of_property_read_u32(np, "gpmc,wr-data-mux-bus-ns",
+			     &gpmc_t->wr_data_mux_bus);
+
+	/* bool timing parameters */
+	p = &gpmc_t->bool_timings;
+
+	p->cycle2cyclediffcsen =
+		of_property_read_bool(np, "gpmc,cycle2cycle-diffcsen");
+	p->cycle2cyclesamecsen =
+		of_property_read_bool(np, "gpmc,cycle2cycle-samecsen");
+	p->we_extra_delay = of_property_read_bool(np, "gpmc,we-extra-delay");
+	p->oe_extra_delay = of_property_read_bool(np, "gpmc,oe-extra-delay");
+	p->adv_extra_delay = of_property_read_bool(np, "gpmc,adv-extra-delay");
+	p->cs_extra_delay = of_property_read_bool(np, "gpmc,cs-extra-delay");
+	p->time_para_granularity =
+		of_property_read_bool(np, "gpmc,time-para-granularity");
+}
+
+struct dt_eccmode {
+	const char *name;
+	enum gpmc_ecc_mode mode;
+};
+
+static struct dt_eccmode modes[] = {
+	{
+		.name = "ham1",
+		.mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE,
+	}, {
+		.name = "sw",
+		.mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE,
+	}, {
+		.name = "hw",
+		.mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE,
+	}, {
+		.name = "hw-romcode",
+		.mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE,
+	}, {
+		.name = "bch4",
+		.mode = OMAP_ECC_BCH4_CODE_HW,
+	}, {
+		.name = "bch8",
+		.mode = OMAP_ECC_BCH8_CODE_HW,
+	}, {
+		.name = "bch8-romcode",
+		.mode = OMAP_ECC_BCH8_CODE_HW_ROMCODE,
+	},
+};
+
+static int gpmc_probe_nand_child(struct device_d *dev,
+				 struct device_node *child)
+{
+	u32 val;
+	const char *s;
+	struct gpmc_timings gpmc_t;
+	struct gpmc_nand_platform_data gpmc_nand_data = {};
+	struct resource res;
+	int ret, i;
+
+	if (of_property_read_u32(child, "reg", &val) < 0) {
+		dev_err(dev, "%s has no 'reg' property\n",
+			child->full_name);
+		return -ENODEV;
+	}
+
+	gpmc_base = dev_get_mem_region(dev, 0);
+	if (!gpmc_base)
+		return -ENODEV;
+
+	gpmc_nand_data.cs = val;
+	gpmc_nand_data.of_node = child;
+
+	/* Detect availability of ELM module */
+	gpmc_nand_data.elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
+	if (gpmc_nand_data.elm_of_node == NULL)
+		gpmc_nand_data.elm_of_node =
+					of_parse_phandle(child, "elm_id", 0);
+	if (gpmc_nand_data.elm_of_node == NULL)
+		pr_warn("%s: ti,elm-id property not found\n", __func__);
+
+	/* select ecc-scheme for NAND */
+	if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
+		pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		struct dt_eccmode *mode = &modes[i];
+		if (!strcmp(s, mode->name))
+			gpmc_nand_data.ecc_mode = mode->mode;
+	}
+
+	val = of_get_nand_bus_width(child);
+	if (val == 16)
+		gpmc_nand_data.device_width = NAND_BUSWIDTH_16;
+
+	gpmc_read_timings_dt(child, &gpmc_t);
+
+	gpmc_nand_data.nand_cfg = xzalloc(sizeof(*gpmc_nand_data.nand_cfg));
+
+	gpmc_timings_to_config(gpmc_nand_data.nand_cfg, &gpmc_t);
+
+	gpmc_nand_data.nand_cfg->cfg[0] |= 2 << 10;
+
+	ret = of_address_to_resource(child, 0, &res);
+	if (ret)
+		pr_err("of_address_to_resource failed\n");
+
+	gpmc_nand_data.nand_cfg->base = res.start;
+	gpmc_nand_data.nand_cfg->size = GPMC_SIZE_16M;
+
+	gpmc_cs_config(gpmc_nand_data.cs, gpmc_nand_data.nand_cfg);
+
+	dev = device_alloc("gpmc_nand", DEVICE_ID_DYNAMIC);
+	device_add_resource(dev, NULL, (resource_size_t)gpmc_base, SZ_4K, IORESOURCE_MEM);
+	device_add_data(dev, &gpmc_nand_data, sizeof(gpmc_nand_data));
+	dev->device_node = child;
+	platform_device_register(dev);
+
+	return 0;
+}
+
+static int gpmc_probe(struct device_d *dev)
+{
+	struct device_node *child, *node = dev->device_node;
+	int ret;
+
+	gpmc_generic_init(0x12);
+
+	ret = of_property_read_u32(node, "gpmc,num-cs",
+				   &gpmc_cs_num);
+	if (ret < 0)
+		return ret;
+
+	ret = of_property_read_u32(node, "gpmc,num-waitpins",
+				   &gpmc_nr_waitpins);
+	if (ret < 0)
+		return ret;
+
+	for_each_child_of_node(node, child) {
+
+		if (!child->name)
+			continue;
+
+		if (!strncmp(child->name, "nand", 4))
+			ret = gpmc_probe_nand_child(dev, child);
+		else
+			dev_warn(dev, "unhandled child %s\n", child->name);
+
+		if (ret)
+			break;
+	}
+
+	if (ret)
+		goto gpmc_err;
+
+	return 0;
+
+gpmc_err:
+	return ret;
+}
+
+static struct of_device_id gpmc_id_table[] = {
+	{ .compatible = "ti,omap2420-gpmc" },
+	{ .compatible = "ti,omap2430-gpmc" },
+	{ .compatible = "ti,omap3430-gpmc" },	/* omap3430 & omap3630 */
+	{ .compatible = "ti,omap4430-gpmc" },	/* omap4430 & omap4460 & omap543x */
+	{ .compatible = "ti,am3352-gpmc" },	/* am335x devices */
+	{ }
+};
+
+static struct driver_d gpmc_driver = {
+	.name = "omap-gpmc",
+	.of_compatible = DRV_OF_COMPAT(gpmc_id_table),
+	.probe   = gpmc_probe,
+};
+device_platform_driver(gpmc_driver);
-- 
1.8.4.2




More information about the barebox mailing list