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 <[email protected]>
---
 arch/arm/mach-omap/include/mach/gpmc_nand.h |   3 +
 drivers/bus/Kconfig                         |   6 +
 drivers/bus/Makefile                        |   1 +
 drivers/bus/omap-gpmc.c                     | 481 ++++++++++++++++++++++++++++
 4 files changed, 491 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..6fdaeb0
--- /dev/null
+++ b/drivers/bus/omap-gpmc.c
@@ -0,0 +1,481 @@
+/*
+ * OMAP GPMC driver. Based upon the corresponding Linux Code
+ *
+ * Copyright (C) 2013 Sascha Hauer, Pengutronix, <[email protected]>
+ *
+ * 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 <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_nand_data = xzalloc(sizeof(*gpmc_nand_data));
+       if (!gpmc_nand_data)
+               return -ENOMEM;
+
+       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;
+       printf("%s: 0x%08x 0x%08x\n", __func__, res.start, res.end);
+
+       gpmc_cs_config(gpmc_nand_data->cs, gpmc_nand_data->nand_cfg);
+
+       add_generic_device("gpmc_nand", DEVICE_ID_DYNAMIC, NULL, 
(resource_size_t)gpmc_base,
+                       1024 * 4, IORESOURCE_MEM, gpmc_nand_data);
+
+       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


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to