summaryrefslogtreecommitdiff
path: root/drivers/memory/omap-gpmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/memory/omap-gpmc.c')
-rw-r--r--drivers/memory/omap-gpmc.c1063
1 files changed, 637 insertions, 426 deletions
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index a80e17de906d..d9e13c1f9b13 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* GPMC support functions
*
@@ -7,13 +8,12 @@
*
* Copyright (C) 2009 Texas Instruments
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
- *
- * 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.
*/
+#include <linux/cleanup.h>
+#include <linux/cpu_pm.h>
#include <linux/irq.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -21,6 +21,8 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h> /* GPIO descriptor enum */
+#include <linux/gpio/machine.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h>
@@ -30,11 +32,9 @@
#include <linux/of_platform.h>
#include <linux/omap-gpmc.h>
#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
#include <linux/platform_data/mtd-nand-omap2.h>
-#include <linux/platform_data/mtd-onenand-omap2.h>
-
-#include <asm/mach-types.h>
#define DEVICE_NAME "omap-gpmc"
@@ -110,8 +110,8 @@
#define ENABLE_PREFETCH (0x1 << 7)
#define DMA_MPU_MODE 2
-#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf)
-#define GPMC_REVISION_MINOR(l) (l & 0xf)
+#define GPMC_REVISION_MAJOR(l) (((l) >> 4) & 0xf)
+#define GPMC_REVISION_MINOR(l) ((l) & 0xf)
#define GPMC_HAS_WR_ACCESS 0x1
#define GPMC_HAS_WR_DATA_MUX_BUS 0x2
@@ -135,6 +135,7 @@
#define GPMC_CONFIG_DEV_SIZE 0x00000002
#define GPMC_CONFIG_DEV_TYPE 0x00000003
+#define GPMC_CONFIG_WAITPINPOLARITY(pin) (BIT(pin) << 8)
#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31)
#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30)
#define GPMC_CONFIG1_READTYPE_ASYNC (0 << 29)
@@ -142,27 +143,27 @@
#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_CLKACTIVATIONTIME(val) (((val) & 3) << 25)
/** CLKACTIVATIONTIME Max Ticks */
#define GPMC_CONFIG1_CLKACTIVATIONTIME_MAX 2
-#define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23)
+#define GPMC_CONFIG1_PAGE_LEN(val) (((val) & 3) << 23)
/** ATTACHEDDEVICEPAGELENGTH Max Value */
#define GPMC_CONFIG1_ATTACHEDDEVICEPAGELENGTH_MAX 2
#define GPMC_CONFIG1_WAIT_READ_MON (1 << 22)
#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21)
-#define GPMC_CONFIG1_WAIT_MON_TIME(val) ((val & 3) << 18)
+#define GPMC_CONFIG1_WAIT_MON_TIME(val) (((val) & 3) << 18)
/** WAITMONITORINGTIME Max Ticks */
#define GPMC_CONFIG1_WAITMONITORINGTIME_MAX 2
-#define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16)
-#define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12)
+#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)
/** DEVICESIZE Max Value */
#define GPMC_CONFIG1_DEVICESIZE_MAX 1
-#define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10)
+#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_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_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))
@@ -230,12 +231,23 @@ struct omap3_gpmc_regs {
struct gpmc_cs_config cs_context[GPMC_CS_NUM];
};
+struct gpmc_waitpin {
+ u32 pin;
+ u32 polarity;
+ struct gpio_desc *desc;
+};
+
struct gpmc_device {
struct device *dev;
int irq;
struct irq_chip irq_chip;
struct gpio_chip gpio_chip;
+ struct notifier_block nb;
+ struct omap3_gpmc_regs context;
+ struct gpmc_waitpin *waitpins;
int nirqs;
+ unsigned int is_suspended:1;
+ struct resource *data;
};
static struct irq_domain *gpmc_irq_domain;
@@ -246,8 +258,7 @@ static DEFINE_SPINLOCK(gpmc_mem_lock);
/* Define chip-selects as reserved by default until probe completes */
static unsigned int gpmc_cs_num = GPMC_CS_NUM;
static unsigned int gpmc_nr_waitpins;
-static resource_size_t phys_base, mem_size;
-static unsigned gpmc_capability;
+static unsigned int gpmc_capability;
static void __iomem *gpmc_base;
static struct clk *gpmc_l3_clk;
@@ -293,15 +304,14 @@ static unsigned long gpmc_get_fclk_period(void)
/**
* gpmc_get_clk_period - get period of selected clock domain in ps
- * @cs Chip Select Region.
- * @cd Clock Domain.
+ * @cs: Chip Select Region.
+ * @cd: Clock Domain.
*
* GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup
* prior to calling this function with GPMC_CD_CLK.
*/
static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd)
{
-
unsigned long tick_ps = gpmc_get_fclk_period();
u32 l;
int div;
@@ -315,13 +325,11 @@ static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd)
tick_ps *= div;
break;
case GPMC_CD_FCLK:
- /* FALL-THROUGH */
default:
break;
}
return tick_ps;
-
}
static unsigned int gpmc_ns_to_clk_ticks(unsigned int time_ns, int cs,
@@ -350,17 +358,6 @@ static unsigned int gpmc_ps_to_ticks(unsigned int time_ps)
return (time_ps + tick_ps - 1) / tick_ps;
}
-static unsigned int gpmc_clk_ticks_to_ns(unsigned int ticks, int cs,
- enum gpmc_clk_domain cd)
-{
- return ticks * gpmc_get_clk_period(cs, cd) / 1000;
-}
-
-unsigned int gpmc_ticks_to_ns(unsigned int ticks)
-{
- return gpmc_clk_ticks_to_ns(ticks, /* any CS */ 0, GPMC_CD_FCLK);
-}
-
static unsigned int gpmc_ticks_to_ps(unsigned int ticks)
{
return ticks * gpmc_get_fclk_period();
@@ -407,13 +404,20 @@ static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p)
}
#ifdef CONFIG_OMAP_GPMC_DEBUG
+
+static unsigned int gpmc_clk_ticks_to_ns(unsigned int ticks, int cs,
+ enum gpmc_clk_domain cd)
+{
+ return ticks * gpmc_get_clk_period(cs, cd) / 1000;
+}
+
/**
* get_gpmc_timing_reg - read a timing parameter and print DTS settings for it.
* @cs: Chip Select Region
* @reg: GPMC_CS_CONFIGn register offset.
* @st_bit: Start Bit
* @end_bit: End Bit. Must be >= @st_bit.
- * @ma:x Maximum parameter value (before optional @shift).
+ * @max: Maximum parameter value (before optional @shift).
* If 0, maximum is as high as @st_bit and @end_bit allow.
* @name: DTS node name, w/o "gpmc,"
* @cd: Clock Domain of timing parameter.
@@ -513,7 +517,7 @@ static void gpmc_cs_show_timings(int cs, const char *desc)
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 4, 4, "time-para-granularity");
GPMC_GET_RAW(GPMC_CS_CONFIG1, 8, 9, "mux-add-data");
GPMC_GET_RAW_SHIFT_MAX(GPMC_CS_CONFIG1, 12, 13, 1,
- GPMC_CONFIG1_DEVICESIZE_MAX, "device-width");
+ GPMC_CONFIG1_DEVICESIZE_MAX, "device-width");
GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read");
@@ -627,9 +631,8 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max
l = gpmc_cs_read_reg(cs, reg);
#ifdef CONFIG_OMAP_GPMC_DEBUG
- pr_info(
- "GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
- cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000,
+ pr_info("GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
+ cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000,
(l >> st_bit) & mask, time);
#endif
l &= ~(mask << st_bit);
@@ -639,14 +642,6 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max
return 0;
}
-#define GPMC_SET_ONE_CD_MAX(reg, st, end, max, field, cd) \
- if (set_gpmc_timing_reg(cs, (reg), (st), (end), (max), \
- t->field, (cd), #field) < 0) \
- return -1
-
-#define GPMC_SET_ONE(reg, st, end, field) \
- GPMC_SET_ONE_CD_MAX(reg, st, end, 0, field, GPMC_CD_FCLK)
-
/**
* gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME
* WAITMONITORINGTIME will be _at least_ as long as desired, i.e.
@@ -664,7 +659,6 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max
*/
static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring)
{
-
int div = gpmc_ns_to_ticks(wait_monitoring);
div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1;
@@ -676,7 +670,6 @@ static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring)
div = 1;
return div;
-
}
/**
@@ -707,12 +700,12 @@ int gpmc_calc_divider(unsigned int sync_clk)
int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t,
const struct gpmc_settings *s)
{
- int div;
+ int div, ret;
u32 l;
div = gpmc_calc_divider(t->sync_clk);
if (div < 0)
- return div;
+ return -EINVAL;
/*
* See if we need to change the divider for waitmonitoringtime.
@@ -730,64 +723,120 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t,
if (!s->sync_read && !s->sync_write &&
(s->wait_on_read || s->wait_on_write)
) {
-
div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring);
if (div < 0) {
pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n",
__func__,
t->wait_monitoring
);
- return -1;
+ return -ENXIO;
}
}
- GPMC_SET_ONE(GPMC_CS_CONFIG2, 0, 3, cs_on);
- GPMC_SET_ONE(GPMC_CS_CONFIG2, 8, 12, cs_rd_off);
- GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off);
+ ret = 0;
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 0, 3, 0, t->cs_on,
+ GPMC_CD_FCLK, "cs_on");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 8, 12, 0, t->cs_rd_off,
+ GPMC_CD_FCLK, "cs_rd_off");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 16, 20, 0, t->cs_wr_off,
+ GPMC_CD_FCLK, "cs_wr_off");
+ if (ret)
+ return -ENXIO;
+
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 0, 3, 0, t->adv_on,
+ GPMC_CD_FCLK, "adv_on");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 8, 12, 0, t->adv_rd_off,
+ GPMC_CD_FCLK, "adv_rd_off");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 16, 20, 0, t->adv_wr_off,
+ GPMC_CD_FCLK, "adv_wr_off");
+ if (ret)
+ return -ENXIO;
- GPMC_SET_ONE(GPMC_CS_CONFIG3, 0, 3, adv_on);
- GPMC_SET_ONE(GPMC_CS_CONFIG3, 8, 12, adv_rd_off);
- GPMC_SET_ONE(GPMC_CS_CONFIG3, 16, 20, adv_wr_off);
if (gpmc_capability & GPMC_HAS_MUX_AAD) {
- GPMC_SET_ONE(GPMC_CS_CONFIG3, 4, 6, adv_aad_mux_on);
- GPMC_SET_ONE(GPMC_CS_CONFIG3, 24, 26, adv_aad_mux_rd_off);
- GPMC_SET_ONE(GPMC_CS_CONFIG3, 28, 30, adv_aad_mux_wr_off);
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 4, 6, 0,
+ t->adv_aad_mux_on, GPMC_CD_FCLK,
+ "adv_aad_mux_on");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 24, 26, 0,
+ t->adv_aad_mux_rd_off, GPMC_CD_FCLK,
+ "adv_aad_mux_rd_off");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 28, 30, 0,
+ t->adv_aad_mux_wr_off, GPMC_CD_FCLK,
+ "adv_aad_mux_wr_off");
+ if (ret)
+ return -ENXIO;
}
- GPMC_SET_ONE(GPMC_CS_CONFIG4, 0, 3, oe_on);
- GPMC_SET_ONE(GPMC_CS_CONFIG4, 8, 12, oe_off);
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 0, 3, 0, t->oe_on,
+ GPMC_CD_FCLK, "oe_on");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 8, 12, 0, t->oe_off,
+ GPMC_CD_FCLK, "oe_off");
if (gpmc_capability & GPMC_HAS_MUX_AAD) {
- GPMC_SET_ONE(GPMC_CS_CONFIG4, 4, 6, oe_aad_mux_on);
- GPMC_SET_ONE(GPMC_CS_CONFIG4, 13, 15, oe_aad_mux_off);
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 4, 6, 0,
+ t->oe_aad_mux_on, GPMC_CD_FCLK,
+ "oe_aad_mux_on");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 13, 15, 0,
+ t->oe_aad_mux_off, GPMC_CD_FCLK,
+ "oe_aad_mux_off");
+ }
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 16, 19, 0, t->we_on,
+ GPMC_CD_FCLK, "we_on");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 24, 28, 0, t->we_off,
+ GPMC_CD_FCLK, "we_off");
+ if (ret)
+ return -ENXIO;
+
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 0, 4, 0, t->rd_cycle,
+ GPMC_CD_FCLK, "rd_cycle");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 8, 12, 0, t->wr_cycle,
+ GPMC_CD_FCLK, "wr_cycle");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 16, 20, 0, t->access,
+ GPMC_CD_FCLK, "access");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 24, 27, 0,
+ t->page_burst_access, GPMC_CD_FCLK,
+ "page_burst_access");
+ if (ret)
+ return -ENXIO;
+
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 0, 3, 0,
+ t->bus_turnaround, GPMC_CD_FCLK,
+ "bus_turnaround");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 8, 11, 0,
+ t->cycle2cycle_delay, GPMC_CD_FCLK,
+ "cycle2cycle_delay");
+ if (ret)
+ return -ENXIO;
+
+ if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) {
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 16, 19, 0,
+ t->wr_data_mux_bus, GPMC_CD_FCLK,
+ "wr_data_mux_bus");
+ if (ret)
+ return -ENXIO;
+ }
+ if (gpmc_capability & GPMC_HAS_WR_ACCESS) {
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 24, 28, 0,
+ t->wr_access, GPMC_CD_FCLK,
+ "wr_access");
+ if (ret)
+ return -ENXIO;
}
- GPMC_SET_ONE(GPMC_CS_CONFIG4, 16, 19, we_on);
- GPMC_SET_ONE(GPMC_CS_CONFIG4, 24, 28, we_off);
-
- GPMC_SET_ONE(GPMC_CS_CONFIG5, 0, 4, rd_cycle);
- GPMC_SET_ONE(GPMC_CS_CONFIG5, 8, 12, wr_cycle);
- GPMC_SET_ONE(GPMC_CS_CONFIG5, 16, 20, access);
-
- GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access);
-
- GPMC_SET_ONE(GPMC_CS_CONFIG6, 0, 3, bus_turnaround);
- GPMC_SET_ONE(GPMC_CS_CONFIG6, 8, 11, cycle2cycle_delay);
-
- if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
- GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus);
- if (gpmc_capability & GPMC_HAS_WR_ACCESS)
- GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access);
l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
l &= ~0x03;
l |= (div - 1);
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
- GPMC_SET_ONE_CD_MAX(GPMC_CS_CONFIG1, 18, 19,
- GPMC_CONFIG1_WAITMONITORINGTIME_MAX,
- wait_monitoring, GPMC_CD_CLK);
- GPMC_SET_ONE_CD_MAX(GPMC_CS_CONFIG1, 25, 26,
- GPMC_CONFIG1_CLKACTIVATIONTIME_MAX,
- clk_activation, GPMC_CD_FCLK);
+ ret = 0;
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 18, 19,
+ GPMC_CONFIG1_WAITMONITORINGTIME_MAX,
+ t->wait_monitoring, GPMC_CD_CLK,
+ "wait_monitoring");
+ ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 25, 26,
+ GPMC_CONFIG1_CLKACTIVATIONTIME_MAX,
+ t->clk_activation, GPMC_CD_FCLK,
+ "clk_activation");
+ if (ret)
+ return -ENXIO;
#ifdef CONFIG_OMAP_GPMC_DEBUG
pr_info("GPMC CS%d CLK period is %lu ns (div %d)\n",
@@ -878,20 +927,6 @@ static bool gpmc_cs_reserved(int cs)
return gpmc->flags & GPMC_CS_RESERVED;
}
-static void gpmc_cs_set_name(int cs, const char *name)
-{
- struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
-
- gpmc->name = name;
-}
-
-static const char *gpmc_cs_get_name(int cs)
-{
- struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
-
- return gpmc->name;
-}
-
static unsigned long gpmc_mem_align(unsigned long size)
{
int order;
@@ -937,56 +972,13 @@ static int gpmc_cs_delete_mem(int cs)
return r;
}
-/**
- * gpmc_cs_remap - remaps a chip-select physical base address
- * @cs: chip-select to remap
- * @base: physical base address to re-map chip-select to
- *
- * Re-maps a chip-select to a new physical base address specified by
- * "base". Returns 0 on success and appropriate negative error code
- * on failure.
- */
-static int gpmc_cs_remap(int cs, u32 base)
-{
- int ret;
- u32 old_base, size;
-
- if (cs > gpmc_cs_num) {
- pr_err("%s: requested chip-select is disabled\n", __func__);
- return -ENODEV;
- }
-
- /*
- * Make sure we ignore any device offsets from the GPMC partition
- * allocated for the chip select and that the new base confirms
- * to the GPMC 16MB minimum granularity.
- */
- base &= ~(SZ_16M - 1);
-
- gpmc_cs_get_memconf(cs, &old_base, &size);
- if (base == old_base)
- return 0;
-
- ret = gpmc_cs_delete_mem(cs);
- if (ret < 0)
- return ret;
-
- ret = gpmc_cs_insert_mem(cs, base, size);
- if (ret < 0)
- return ret;
-
- ret = gpmc_cs_set_memconf(cs, base, size);
-
- return ret;
-}
-
int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
{
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
struct resource *res = &gpmc->mem;
int r = -1;
- if (cs > gpmc_cs_num) {
+ if (cs >= gpmc_cs_num) {
pr_err("%s: requested chip-select is disabled\n", __func__);
return -ENODEV;
}
@@ -994,18 +986,18 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
if (size > (1 << GPMC_SECTION_SHIFT))
return -ENOMEM;
- spin_lock(&gpmc_mem_lock);
- if (gpmc_cs_reserved(cs)) {
- r = -EBUSY;
- goto out;
- }
+ guard(spinlock)(&gpmc_mem_lock);
+
+ if (gpmc_cs_reserved(cs))
+ return -EBUSY;
+
if (gpmc_cs_mem_enabled(cs))
r = adjust_resource(res, res->start & ~(size - 1), size);
if (r < 0)
r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0,
size, NULL, NULL);
if (r < 0)
- goto out;
+ return r;
/* Disable CS while changing base address and size mask */
gpmc_cs_disable_mem(cs);
@@ -1013,39 +1005,94 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
r = gpmc_cs_set_memconf(cs, res->start, resource_size(res));
if (r < 0) {
release_resource(res);
- goto out;
+ return r;
}
/* Enable CS */
gpmc_cs_enable_mem(cs);
*base = res->start;
gpmc_cs_set_reserved(cs, 1);
-out:
- spin_unlock(&gpmc_mem_lock);
- return r;
+
+ return 0;
}
EXPORT_SYMBOL(gpmc_cs_request);
void gpmc_cs_free(int cs)
{
- struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
- struct resource *res = &gpmc->mem;
+ struct gpmc_cs_data *gpmc;
+ struct resource *res;
- spin_lock(&gpmc_mem_lock);
+ guard(spinlock)(&gpmc_mem_lock);
if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {
- printk(KERN_ERR "Trying to free non-reserved GPMC CS%d\n", cs);
- BUG();
- spin_unlock(&gpmc_mem_lock);
+ WARN(1, "Trying to free non-reserved GPMC CS%d\n", cs);
return;
}
+ gpmc = &gpmc_cs[cs];
+ res = &gpmc->mem;
+
gpmc_cs_disable_mem(cs);
if (res->flags)
release_resource(res);
gpmc_cs_set_reserved(cs, 0);
- spin_unlock(&gpmc_mem_lock);
}
EXPORT_SYMBOL(gpmc_cs_free);
+static bool gpmc_is_valid_waitpin(u32 waitpin)
+{
+ return waitpin < gpmc_nr_waitpins;
+}
+
+static int gpmc_alloc_waitpin(struct gpmc_device *gpmc,
+ struct gpmc_settings *p)
+{
+ int ret;
+ struct gpmc_waitpin *waitpin;
+ struct gpio_desc *waitpin_desc;
+
+ if (!gpmc_is_valid_waitpin(p->wait_pin))
+ return -EINVAL;
+
+ waitpin = &gpmc->waitpins[p->wait_pin];
+
+ if (!waitpin->desc) {
+ /* Reserve the GPIO for wait pin usage.
+ * GPIO polarity doesn't matter here. Wait pin polarity
+ * is set in GPMC_CONFIG register.
+ */
+ waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
+ p->wait_pin, "WAITPIN",
+ GPIO_ACTIVE_HIGH,
+ GPIOD_IN);
+
+ ret = PTR_ERR(waitpin_desc);
+ if (IS_ERR(waitpin_desc) && ret != -EBUSY)
+ return ret;
+
+ /* New wait pin */
+ waitpin->desc = waitpin_desc;
+ waitpin->pin = p->wait_pin;
+ waitpin->polarity = p->wait_pin_polarity;
+ } else {
+ /* Shared wait pin */
+ if (p->wait_pin_polarity != waitpin->polarity ||
+ p->wait_pin != waitpin->pin) {
+ dev_err(gpmc->dev,
+ "shared-wait-pin: invalid configuration\n");
+ return -EINVAL;
+ }
+ dev_info(gpmc->dev, "shared wait-pin: %d\n", waitpin->pin);
+ }
+
+ return 0;
+}
+
+static void gpmc_free_waitpin(struct gpmc_device *gpmc,
+ int wait_pin)
+{
+ if (gpmc_is_valid_waitpin(wait_pin))
+ gpiochip_free_own_desc(gpmc->waitpins[wait_pin].desc);
+}
+
/**
* gpmc_configure - write request to configure gpmc
* @cmd: command type
@@ -1075,11 +1122,33 @@ int gpmc_configure(int cmd, int wval)
}
EXPORT_SYMBOL(gpmc_configure);
-void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
+static bool gpmc_nand_writebuffer_empty(void)
+{
+ if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
+ return true;
+
+ return false;
+}
+
+static struct gpmc_nand_ops nand_ops = {
+ .nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
+};
+
+/**
+ * gpmc_omap_get_nand_ops - Get the GPMC NAND interface
+ * @reg: the GPMC NAND register map exclusive for NAND use.
+ * @cs: GPMC chip select number on which the NAND sits. The
+ * register map returned will be specific to this chip select.
+ *
+ * Returns NULL on error e.g. invalid cs.
+ */
+struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
{
int i;
- reg->gpmc_status = NULL; /* deprecated */
+ if (cs >= gpmc_cs_num)
+ return NULL;
+
reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
@@ -1111,53 +1180,116 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 +
i * GPMC_BCH_SIZE;
}
-}
-
-static bool gpmc_nand_writebuffer_empty(void)
-{
- if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
- return true;
- return false;
+ return &nand_ops;
}
+EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops);
-static struct gpmc_nand_ops nand_ops = {
- .nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
-};
+static void gpmc_omap_onenand_calc_sync_timings(struct gpmc_timings *t,
+ struct gpmc_settings *s,
+ int freq, int latency)
+{
+ struct gpmc_device_timings dev_t;
+ const int t_cer = 15;
+ const int t_avdp = 12;
+ const int t_cez = 20; /* max of t_cez, t_oez */
+ const int t_wpl = 40;
+ const int t_wph = 30;
+ int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
+
+ switch (freq) {
+ case 104:
+ min_gpmc_clk_period = 9600; /* 104 MHz */
+ t_ces = 3;
+ t_avds = 4;
+ t_avdh = 2;
+ t_ach = 3;
+ t_aavdh = 6;
+ t_rdyo = 6;
+ break;
+ case 83:
+ min_gpmc_clk_period = 12000; /* 83 MHz */
+ t_ces = 5;
+ t_avds = 4;
+ t_avdh = 2;
+ t_ach = 6;
+ t_aavdh = 6;
+ t_rdyo = 9;
+ break;
+ case 66:
+ min_gpmc_clk_period = 15000; /* 66 MHz */
+ t_ces = 6;
+ t_avds = 5;
+ t_avdh = 2;
+ t_ach = 6;
+ t_aavdh = 6;
+ t_rdyo = 11;
+ break;
+ default:
+ min_gpmc_clk_period = 18500; /* 54 MHz */
+ t_ces = 7;
+ t_avds = 7;
+ t_avdh = 7;
+ t_ach = 9;
+ t_aavdh = 7;
+ t_rdyo = 15;
+ break;
+ }
-/**
- * gpmc_omap_get_nand_ops - Get the GPMC NAND interface
- * @regs: the GPMC NAND register map exclusive for NAND use.
- * @cs: GPMC chip select number on which the NAND sits. The
- * register map returned will be specific to this chip select.
- *
- * Returns NULL on error e.g. invalid cs.
- */
-struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
+ /* Set synchronous read timings */
+ memset(&dev_t, 0, sizeof(dev_t));
+
+ if (!s->sync_write) {
+ dev_t.t_avdp_w = max(t_avdp, t_cer) * 1000;
+ dev_t.t_wpl = t_wpl * 1000;
+ dev_t.t_wph = t_wph * 1000;
+ dev_t.t_aavdh = t_aavdh * 1000;
+ }
+ dev_t.ce_xdelay = true;
+ dev_t.avd_xdelay = true;
+ dev_t.oe_xdelay = true;
+ dev_t.we_xdelay = true;
+ dev_t.clk = min_gpmc_clk_period;
+ dev_t.t_bacc = dev_t.clk;
+ dev_t.t_ces = t_ces * 1000;
+ dev_t.t_avds = t_avds * 1000;
+ dev_t.t_avdh = t_avdh * 1000;
+ dev_t.t_ach = t_ach * 1000;
+ dev_t.cyc_iaa = (latency + 1);
+ dev_t.t_cez_r = t_cez * 1000;
+ dev_t.t_cez_w = dev_t.t_cez_r;
+ dev_t.cyc_aavdh_oe = 1;
+ dev_t.t_rdyo = t_rdyo * 1000 + min_gpmc_clk_period;
+
+ gpmc_calc_timings(t, s, &dev_t);
+}
+
+int gpmc_omap_onenand_set_timings(struct device *dev, int cs, int freq,
+ int latency,
+ struct gpmc_onenand_info *info)
{
- if (cs >= gpmc_cs_num)
- return NULL;
+ int ret;
+ struct gpmc_timings gpmc_t;
+ struct gpmc_settings gpmc_s;
- gpmc_update_nand_reg(reg, cs);
+ gpmc_read_settings_dt(dev->of_node, &gpmc_s);
- return &nand_ops;
-}
-EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops);
+ info->sync_read = gpmc_s.sync_read;
+ info->sync_write = gpmc_s.sync_write;
+ info->burst_len = gpmc_s.burst_len;
-int gpmc_get_client_irq(unsigned irq_config)
-{
- if (!gpmc_irq_domain) {
- pr_warn("%s called before GPMC IRQ domain available\n",
- __func__);
+ if (!gpmc_s.sync_read && !gpmc_s.sync_write)
return 0;
- }
- /* we restrict this to NAND IRQs only */
- if (irq_config >= GPMC_NR_NAND_IRQS)
- return 0;
+ gpmc_omap_onenand_calc_sync_timings(&gpmc_t, &gpmc_s, freq, latency);
- return irq_create_mapping(gpmc_irq_domain, irq_config);
+ ret = gpmc_cs_program_settings(cs, &gpmc_s);
+ if (ret < 0)
+ return ret;
+
+ return gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s);
}
+EXPORT_SYMBOL_GPL(gpmc_omap_onenand_set_timings);
static int gpmc_irq_endis(unsigned long hwirq, bool endis)
{
@@ -1323,10 +1455,8 @@ static int gpmc_setup_irq(struct gpmc_device *gpmc)
gpmc->irq_chip.irq_unmask = gpmc_irq_unmask;
gpmc->irq_chip.irq_set_type = gpmc_irq_set_type;
- gpmc_irq_domain = irq_domain_add_linear(gpmc->dev->of_node,
- gpmc->nirqs,
- &gpmc_irq_domain_ops,
- gpmc);
+ gpmc_irq_domain = irq_domain_create_linear(dev_fwnode(gpmc->dev), gpmc->nirqs,
+ &gpmc_irq_domain_ops, gpmc);
if (!gpmc_irq_domain) {
dev_err(gpmc->dev, "IRQ domain add failed\n");
return -ENODEV;
@@ -1367,15 +1497,20 @@ static void gpmc_mem_exit(void)
continue;
gpmc_cs_delete_mem(cs);
}
-
}
-static void gpmc_mem_init(void)
+static void gpmc_mem_init(struct gpmc_device *gpmc)
{
int cs;
- gpmc_mem_root.start = GPMC_MEM_START;
- gpmc_mem_root.end = GPMC_MEM_END;
+ if (!gpmc->data) {
+ /* All legacy devices have same data IO window */
+ gpmc_mem_root.start = GPMC_MEM_START;
+ gpmc_mem_root.end = GPMC_MEM_END;
+ } else {
+ gpmc_mem_root.start = gpmc->data->start;
+ gpmc_mem_root.end = gpmc->data->end;
+ }
/* Reserve all regions that has been set up by bootloader */
for (cs = 0; cs < gpmc_cs_num; cs++) {
@@ -1536,17 +1671,14 @@ static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t,
/* oe_on */
temp = dev_t->t_oeasu;
if (mux)
- temp = max_t(u32, temp,
- gpmc_t->adv_rd_off + dev_t->t_aavdh);
+ temp = max_t(u32, temp, gpmc_t->adv_rd_off + dev_t->t_aavdh);
gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp);
/* access */
temp = max_t(u32, dev_t->t_iaa, /* XXX: remove t_iaa in async ? */
- gpmc_t->oe_on + dev_t->t_oe);
- temp = max_t(u32, temp,
- gpmc_t->cs_on + dev_t->t_ce);
- temp = max_t(u32, temp,
- gpmc_t->adv_on + dev_t->t_aa);
+ gpmc_t->oe_on + dev_t->t_oe);
+ temp = max_t(u32, temp, gpmc_t->cs_on + dev_t->t_ce);
+ temp = max_t(u32, temp, gpmc_t->adv_on + dev_t->t_aa);
gpmc_t->access = gpmc_round_ps_to_ticks(temp);
gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1);
@@ -1655,10 +1787,11 @@ static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t,
return 0;
}
-/* TODO: remove this function once all peripherals are confirmed to
+/*
+ * TODO: remove this function once all peripherals are confirmed to
* work with generic timing. Simultaneously gpmc_cs_set_timings()
* has to be modified to handle timings in ps instead of ns
-*/
+ */
static void gpmc_convert_ps_to_ns(struct gpmc_timings *t)
{
t->cs_on /= 1000;
@@ -1762,7 +1895,8 @@ int gpmc_cs_program_settings(int cs, struct gpmc_settings *p)
}
}
- if (p->wait_pin > gpmc_nr_waitpins) {
+ if (p->wait_pin != GPMC_WAITPIN_INVALID &&
+ p->wait_pin > gpmc_nr_waitpins) {
pr_err("%s: invalid wait-pin (%d)\n", __func__, p->wait_pin);
return -EINVAL;
}
@@ -1794,18 +1928,77 @@ int gpmc_cs_program_settings(int cs, struct gpmc_settings *p)
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, config1);
+ if (p->wait_pin_polarity != GPMC_WAITPINPOLARITY_INVALID) {
+ config1 = gpmc_read_reg(GPMC_CONFIG);
+
+ if (p->wait_pin_polarity == GPMC_WAITPINPOLARITY_ACTIVE_LOW)
+ config1 &= ~GPMC_CONFIG_WAITPINPOLARITY(p->wait_pin);
+ else if (p->wait_pin_polarity == GPMC_WAITPINPOLARITY_ACTIVE_HIGH)
+ config1 |= GPMC_CONFIG_WAITPINPOLARITY(p->wait_pin);
+
+ gpmc_write_reg(GPMC_CONFIG, config1);
+ }
+
return 0;
}
#ifdef CONFIG_OF
-static const struct of_device_id gpmc_dt_ids[] = {
- { .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 void gpmc_cs_set_name(int cs, const char *name)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+
+ gpmc->name = name;
+}
+
+static const char *gpmc_cs_get_name(int cs)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+
+ return gpmc->name;
+}
+
+/**
+ * gpmc_cs_remap - remaps a chip-select physical base address
+ * @cs: chip-select to remap
+ * @base: physical base address to re-map chip-select to
+ *
+ * Re-maps a chip-select to a new physical base address specified by
+ * "base". Returns 0 on success and appropriate negative error code
+ * on failure.
+ */
+static int gpmc_cs_remap(int cs, u32 base)
+{
+ int ret;
+ u32 old_base, size;
+
+ if (cs >= gpmc_cs_num) {
+ pr_err("%s: requested chip-select is disabled\n", __func__);
+ return -ENODEV;
+ }
+
+ /*
+ * Make sure we ignore any device offsets from the GPMC partition
+ * allocated for the chip select and that the new base confirms
+ * to the GPMC 16MB minimum granularity.
+ */
+ base &= ~(SZ_16M - 1);
+
+ gpmc_cs_get_memconf(cs, &old_base, &size);
+ if (base == old_base)
+ return 0;
+
+ ret = gpmc_cs_delete_mem(cs);
+ if (ret < 0)
+ return ret;
+
+ ret = gpmc_cs_insert_mem(cs, base, size);
+ if (ret < 0)
+ return ret;
+
+ ret = gpmc_cs_set_memconf(cs, base, size);
+
+ return ret;
+}
/**
* gpmc_read_settings_dt - read gpmc settings from device-tree
@@ -1835,7 +2028,25 @@ void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)
__func__);
}
+ p->wait_pin = GPMC_WAITPIN_INVALID;
+ p->wait_pin_polarity = GPMC_WAITPINPOLARITY_INVALID;
+
if (!of_property_read_u32(np, "gpmc,wait-pin", &p->wait_pin)) {
+ if (!gpmc_is_valid_waitpin(p->wait_pin)) {
+ pr_err("%s: Invalid wait-pin (%d)\n", __func__, p->wait_pin);
+ p->wait_pin = GPMC_WAITPIN_INVALID;
+ }
+
+ if (!of_property_read_u32(np, "ti,wait-pin-polarity",
+ &p->wait_pin_polarity)) {
+ if (p->wait_pin_polarity != GPMC_WAITPINPOLARITY_ACTIVE_HIGH &&
+ p->wait_pin_polarity != GPMC_WAITPINPOLARITY_ACTIVE_LOW) {
+ pr_err("%s: Invalid wait-pin-polarity (%d)\n",
+ __func__, p->wait_pin_polarity);
+ p->wait_pin_polarity = GPMC_WAITPINPOLARITY_INVALID;
+ }
+ }
+
p->wait_on_read = of_property_read_bool(np,
"gpmc,wait-on-read");
p->wait_on_write = of_property_read_bool(np,
@@ -1922,41 +2133,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
of_property_read_bool(np, "gpmc,time-para-granularity");
}
-#if IS_ENABLED(CONFIG_MTD_ONENAND)
-static int gpmc_probe_onenand_child(struct platform_device *pdev,
- struct device_node *child)
-{
- u32 val;
- struct omap_onenand_platform_data *gpmc_onenand_data;
-
- if (of_property_read_u32(child, "reg", &val) < 0) {
- dev_err(&pdev->dev, "%s has no 'reg' property\n",
- child->full_name);
- return -ENODEV;
- }
-
- gpmc_onenand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_onenand_data),
- GFP_KERNEL);
- if (!gpmc_onenand_data)
- return -ENOMEM;
-
- gpmc_onenand_data->cs = val;
- gpmc_onenand_data->of_node = child;
- gpmc_onenand_data->dma_channel = -1;
-
- if (!of_property_read_u32(child, "dma-channel", &val))
- gpmc_onenand_data->dma_channel = val;
-
- return gpmc_onenand_init(gpmc_onenand_data);
-}
-#else
-static int gpmc_probe_onenand_child(struct platform_device *pdev,
- struct device_node *child)
-{
- return 0;
-}
-#endif
-
/**
* gpmc_probe_generic_child - configures the gpmc for a child device
* @pdev: pointer to gpmc platform device
@@ -1975,18 +2151,17 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
const char *name;
int ret, cs;
u32 val;
- struct gpio_desc *waitpin_desc = NULL;
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
if (of_property_read_u32(child, "reg", &cs) < 0) {
- dev_err(&pdev->dev, "%s has no 'reg' property\n",
- child->full_name);
+ dev_err(&pdev->dev, "%pOF has no 'reg' property\n",
+ child);
return -ENODEV;
}
if (of_address_to_resource(child, 0, &res) < 0) {
- dev_err(&pdev->dev, "%s has malformed 'reg' property\n",
- child->full_name);
+ dev_err(&pdev->dev, "%pOF has malformed 'reg' property\n",
+ child);
return -ENODEV;
}
@@ -1996,15 +2171,15 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
* timings.
*/
name = gpmc_cs_get_name(cs);
- if (name && child->name && of_node_cmp(child->name, name) == 0)
- goto no_timings;
+ if (name && of_node_name_eq(child, name))
+ goto no_timings;
ret = gpmc_cs_request(cs, resource_size(&res), &base);
if (ret < 0) {
dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs);
return ret;
}
- gpmc_cs_set_name(cs, child->name);
+ gpmc_cs_set_name(cs, child->full_name);
gpmc_read_settings_dt(child, &gpmc_s);
gpmc_read_timings_dt(child, &gpmc_t);
@@ -2026,7 +2201,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
gpmc_cs_disable_mem(cs);
/*
- * FIXME: gpmc_cs_request() will map the CS to an arbitary
+ * FIXME: gpmc_cs_request() will map the CS to an arbitrary
* location in the gpmc address space. When booting with
* device-tree we want the NOR flash to be mapped to the
* location specified in the device-tree blob. So remap the
@@ -2049,17 +2224,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
goto err;
}
- if (of_node_cmp(child->name, "nand") == 0) {
- /* Warn about older DT blobs with no compatible property */
- if (!of_property_read_bool(child, "compatible")) {
- dev_warn(&pdev->dev,
- "Incompatible NAND node: missing compatible");
- ret = -EINVAL;
- goto err;
- }
- }
-
- if (of_device_is_compatible(child, "ti,omap2-nand")) {
+ if (of_match_node(omap_nand_ids, child)) {
/* NAND specific setup */
val = 8;
of_property_read_u32(child, "nand-bus-width", &val);
@@ -2071,8 +2236,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
gpmc_s.device_width = GPMC_DEVWIDTH_16BIT;
break;
default:
- dev_err(&pdev->dev, "%s: invalid 'nand-bus-width'\n",
- child->name);
+ dev_err(&pdev->dev, "%pOFn: invalid 'nand-bus-width'\n",
+ child);
ret = -EINVAL;
goto err;
}
@@ -2083,24 +2248,19 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
} else {
ret = of_property_read_u32(child, "bank-width",
&gpmc_s.device_width);
- if (ret < 0) {
- dev_err(&pdev->dev, "%s has no 'bank-width' property\n",
- child->full_name);
+ if (ret < 0 && !gpmc_s.device_width) {
+ dev_err(&pdev->dev,
+ "%pOF has no 'gpmc,device-width' property\n",
+ child);
goto err;
}
}
/* Reserve wait pin if it is required and valid */
if (gpmc_s.wait_on_read || gpmc_s.wait_on_write) {
- unsigned int wait_pin = gpmc_s.wait_pin;
-
- waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
- wait_pin, "WAITPIN");
- if (IS_ERR(waitpin_desc)) {
- dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin);
- ret = PTR_ERR(waitpin_desc);
+ ret = gpmc_alloc_waitpin(gpmc, &gpmc_s);
+ if (ret < 0)
goto err;
- }
}
gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings");
@@ -2111,8 +2271,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s);
if (ret) {
- dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n",
- child->name);
+ dev_err(&pdev->dev, "failed to set gpmc timings for: %pOFn\n",
+ child);
goto err_cs;
}
@@ -2130,27 +2290,27 @@ no_timings:
if (!of_platform_device_create(child, NULL, &pdev->dev))
goto err_child_fail;
- /* is child a common bus? */
- if (of_match_node(of_default_bus_match_table, child))
- /* create children and other common bus children */
- if (of_platform_default_populate(child, NULL, &pdev->dev))
- goto err_child_fail;
+ /* create children and other common bus children */
+ if (of_platform_default_populate(child, NULL, &pdev->dev))
+ goto err_child_fail;
return 0;
err_child_fail:
- dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name);
+ dev_err(&pdev->dev, "failed to create gpmc child %pOFn\n", child);
ret = -ENODEV;
err_cs:
- gpiochip_free_own_desc(waitpin_desc);
+ gpmc_free_waitpin(gpmc, gpmc_s.wait_pin);
err:
gpmc_cs_free(cs);
return ret;
}
+static const struct of_device_id gpmc_dt_ids[];
+
static int gpmc_probe_dt(struct platform_device *pdev)
{
int ret;
@@ -2190,22 +2350,18 @@ static void gpmc_probe_dt_children(struct platform_device *pdev)
struct device_node *child;
for_each_available_child_of_node(pdev->dev.of_node, child) {
-
- if (!child->name)
- continue;
-
- if (of_node_cmp(child->name, "onenand") == 0)
- ret = gpmc_probe_onenand_child(pdev, child);
- else
- ret = gpmc_probe_generic_child(pdev, child);
-
+ ret = gpmc_probe_generic_child(pdev, child);
if (ret) {
- dev_err(&pdev->dev, "failed to probe DT child '%s': %d\n",
- child->name, ret);
+ dev_err(&pdev->dev, "failed to probe DT child '%pOFn': %d\n",
+ child, ret);
}
}
}
#else
+void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)
+{
+ memset(p, 0, sizeof(*p));
+}
static int gpmc_probe_dt(struct platform_device *pdev)
{
return 0;
@@ -2218,7 +2374,7 @@ static void gpmc_probe_dt_children(struct platform_device *pdev)
static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
- return 1; /* we're input only */
+ return GPIO_LINE_DIRECTION_IN; /* we're input only */
}
static int gpmc_gpio_direction_input(struct gpio_chip *chip,
@@ -2227,17 +2383,6 @@ static int gpmc_gpio_direction_input(struct gpio_chip *chip,
return 0; /* we're input only */
}
-static int gpmc_gpio_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
-{
- return -EINVAL; /* we're input only */
-}
-
-static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
-{
-}
-
static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
u32 reg;
@@ -2259,8 +2404,6 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
- gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
- gpmc->gpio_chip.set = gpmc_gpio_set;
gpmc->gpio_chip.get = gpmc_gpio_get;
gpmc->gpio_chip.base = -1;
@@ -2273,9 +2416,109 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
return 0;
}
+static void omap3_gpmc_save_context(struct gpmc_device *gpmc)
+{
+ struct omap3_gpmc_regs *gpmc_context;
+ int i;
+
+ if (!gpmc || !gpmc_base)
+ return;
+
+ gpmc_context = &gpmc->context;
+
+ gpmc_context->sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
+ gpmc_context->irqenable = gpmc_read_reg(GPMC_IRQENABLE);
+ gpmc_context->timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
+ gpmc_context->config = gpmc_read_reg(GPMC_CONFIG);
+ gpmc_context->prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
+ gpmc_context->prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
+ gpmc_context->prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
+ for (i = 0; i < gpmc_cs_num; i++) {
+ gpmc_context->cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
+ if (gpmc_context->cs_context[i].is_valid) {
+ gpmc_context->cs_context[i].config1 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
+ gpmc_context->cs_context[i].config2 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
+ gpmc_context->cs_context[i].config3 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
+ gpmc_context->cs_context[i].config4 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
+ gpmc_context->cs_context[i].config5 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
+ gpmc_context->cs_context[i].config6 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
+ gpmc_context->cs_context[i].config7 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
+ }
+ }
+}
+
+static void omap3_gpmc_restore_context(struct gpmc_device *gpmc)
+{
+ struct omap3_gpmc_regs *gpmc_context;
+ int i;
+
+ if (!gpmc || !gpmc_base)
+ return;
+
+ gpmc_context = &gpmc->context;
+
+ gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context->sysconfig);
+ gpmc_write_reg(GPMC_IRQENABLE, gpmc_context->irqenable);
+ gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context->timeout_ctrl);
+ gpmc_write_reg(GPMC_CONFIG, gpmc_context->config);
+ gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context->prefetch_config1);
+ gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context->prefetch_config2);
+ gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context->prefetch_control);
+ for (i = 0; i < gpmc_cs_num; i++) {
+ if (gpmc_context->cs_context[i].is_valid) {
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
+ gpmc_context->cs_context[i].config1);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
+ gpmc_context->cs_context[i].config2);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
+ gpmc_context->cs_context[i].config3);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
+ gpmc_context->cs_context[i].config4);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
+ gpmc_context->cs_context[i].config5);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
+ gpmc_context->cs_context[i].config6);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
+ gpmc_context->cs_context[i].config7);
+ } else {
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG7, 0);
+ }
+ }
+}
+
+static int omap_gpmc_context_notifier(struct notifier_block *nb,
+ unsigned long cmd, void *v)
+{
+ struct gpmc_device *gpmc;
+
+ gpmc = container_of(nb, struct gpmc_device, nb);
+ if (gpmc->is_suspended || pm_runtime_suspended(gpmc->dev))
+ return NOTIFY_OK;
+
+ switch (cmd) {
+ case CPU_CLUSTER_PM_ENTER:
+ omap3_gpmc_save_context(gpmc);
+ break;
+ case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */
+ break;
+ case CPU_CLUSTER_PM_EXIT:
+ omap3_gpmc_restore_context(gpmc);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
static int gpmc_probe(struct platform_device *pdev)
{
- int rc;
+ int rc, i;
u32 l;
struct resource *res;
struct gpmc_device *gpmc;
@@ -2287,24 +2530,29 @@ static int gpmc_probe(struct platform_device *pdev)
gpmc->dev = &pdev->dev;
platform_set_drvdata(pdev, gpmc);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL)
- return -ENOENT;
-
- phys_base = res->start;
- mem_size = resource_size(res);
-
- gpmc_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(gpmc_base))
- return PTR_ERR(gpmc_base);
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
if (!res) {
- dev_err(&pdev->dev, "Failed to get resource: irq\n");
- return -ENOENT;
+ /* legacy DT */
+ gpmc_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(gpmc_base))
+ return PTR_ERR(gpmc_base);
+ } else {
+ gpmc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpmc_base))
+ return PTR_ERR(gpmc_base);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "data");
+ if (!res) {
+ dev_err(&pdev->dev, "couldn't get data reg resource\n");
+ return -ENOENT;
+ }
+
+ gpmc->data = res;
}
- gpmc->irq = res->start;
+ gpmc->irq = platform_get_irq(pdev, 0);
+ if (gpmc->irq < 0)
+ return gpmc->irq;
gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(gpmc_l3_clk)) {
@@ -2326,6 +2574,15 @@ static int gpmc_probe(struct platform_device *pdev)
gpmc_nr_waitpins = GPMC_NR_WAITPINS;
}
+ gpmc->waitpins = devm_kzalloc(&pdev->dev,
+ gpmc_nr_waitpins * sizeof(struct gpmc_waitpin),
+ GFP_KERNEL);
+ if (!gpmc->waitpins)
+ return -ENOMEM;
+
+ for (i = 0; i < gpmc_nr_waitpins; i++)
+ gpmc->waitpins[i].pin = GPMC_WAITPIN_INVALID;
+
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
@@ -2350,7 +2607,7 @@ static int gpmc_probe(struct platform_device *pdev)
dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
GPMC_REVISION_MINOR(l));
- gpmc_mem_init();
+ gpmc_mem_init(gpmc);
rc = gpmc_gpio_init(gpmc);
if (rc)
goto gpio_init_failed;
@@ -2364,6 +2621,9 @@ static int gpmc_probe(struct platform_device *pdev)
gpmc_probe_dt_children(pdev);
+ gpmc->nb.notifier_call = omap_gpmc_context_notifier;
+ cpu_pm_register_notifier(&gpmc->nb);
+
return 0;
gpio_init_failed:
@@ -2374,36 +2634,59 @@ gpio_init_failed:
return rc;
}
-static int gpmc_remove(struct platform_device *pdev)
+static void gpmc_remove(struct platform_device *pdev)
{
+ int i;
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
+ cpu_pm_unregister_notifier(&gpmc->nb);
+ for (i = 0; i < gpmc_nr_waitpins; i++)
+ gpmc_free_waitpin(gpmc, i);
gpmc_free_irq(gpmc);
gpmc_mem_exit();
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
static int gpmc_suspend(struct device *dev)
{
- omap3_gpmc_save_context();
+ struct gpmc_device *gpmc = dev_get_drvdata(dev);
+
+ omap3_gpmc_save_context(gpmc);
pm_runtime_put_sync(dev);
+ gpmc->is_suspended = 1;
+
return 0;
}
static int gpmc_resume(struct device *dev)
{
+ struct gpmc_device *gpmc = dev_get_drvdata(dev);
+
pm_runtime_get_sync(dev);
- omap3_gpmc_restore_context();
+ omap3_gpmc_restore_context(gpmc);
+ gpmc->is_suspended = 0;
+
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume);
+#ifdef CONFIG_OF
+static const struct of_device_id gpmc_dt_ids[] = {
+ { .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 */
+ { .compatible = "ti,am64-gpmc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpmc_dt_ids);
+#endif
+
static struct platform_driver gpmc_driver = {
.probe = gpmc_probe,
.remove = gpmc_remove,
@@ -2414,79 +2697,7 @@ static struct platform_driver gpmc_driver = {
},
};
-static __init int gpmc_init(void)
-{
- return platform_driver_register(&gpmc_driver);
-}
-postcore_initcall(gpmc_init);
-
-static struct omap3_gpmc_regs gpmc_context;
+module_platform_driver(gpmc_driver);
-void omap3_gpmc_save_context(void)
-{
- int i;
-
- if (!gpmc_base)
- return;
-
- gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
- gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE);
- gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
- gpmc_context.config = gpmc_read_reg(GPMC_CONFIG);
- gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
- gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
- gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
- for (i = 0; i < gpmc_cs_num; i++) {
- gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
- if (gpmc_context.cs_context[i].is_valid) {
- gpmc_context.cs_context[i].config1 =
- gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
- gpmc_context.cs_context[i].config2 =
- gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
- gpmc_context.cs_context[i].config3 =
- gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
- gpmc_context.cs_context[i].config4 =
- gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
- gpmc_context.cs_context[i].config5 =
- gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
- gpmc_context.cs_context[i].config6 =
- gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
- gpmc_context.cs_context[i].config7 =
- gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
- }
- }
-}
-
-void omap3_gpmc_restore_context(void)
-{
- int i;
-
- if (!gpmc_base)
- return;
-
- gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig);
- gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable);
- gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl);
- gpmc_write_reg(GPMC_CONFIG, gpmc_context.config);
- gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
- gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
- gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
- for (i = 0; i < gpmc_cs_num; i++) {
- if (gpmc_context.cs_context[i].is_valid) {
- gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
- gpmc_context.cs_context[i].config1);
- gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
- gpmc_context.cs_context[i].config2);
- gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
- gpmc_context.cs_context[i].config3);
- gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
- gpmc_context.cs_context[i].config4);
- gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
- gpmc_context.cs_context[i].config5);
- gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
- gpmc_context.cs_context[i].config6);
- gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
- gpmc_context.cs_context[i].config7);
- }
- }
-}
+MODULE_DESCRIPTION("Texas Instruments GPMC driver");
+MODULE_LICENSE("GPL");