Commit 4217ff35 authored by Boris Brezillon's avatar Boris Brezillon

Merge branch 'for-v4.7/gpmc-mtd-common' of github.com:rogerq/linux into nand/next

Pull NAND/GPMC updates from Roger Quadros:
 "We do a couple of things in this series which result in cleaner device
  tree implementation, faster perfomance and multi-platform support. As
  an added bonus we get to use the GPMC_WAIT pins as GPI/Interrupts.

  - Establish a custom interface between NAND and GPMC driver. This is
    needed because all of the NAND registers sit in the GPMC register
    space.

  - Clean up device tree support so that omap-gpmc IP and the omap2 NAND
    driver can be used on non-OMAP platforms. e.g. Keystone.

  - Implement GPIOCHIP for the GPMC WAITPINS. SoCs can contain 2 to 4 of
    these and most of them would be unused otherwise. It also allows a
    cleaner implementation of NAND Ready pin status for the NAND driver.

  - Implement GPMC IRQ domain to proivde the 2 NAND events and GPMC
    WAITPIN edge interrupts.

  - Implement GPIOlib based NAND ready pin checking for OMAP NAND driver.
    On dra7-evm, Read speed increases from 13768 KiB/ to 17246 KiB/s.
    Write speed was unchanged at 7123 KiB/s."

* 'for-v4.7/gpmc-mtd-common' of github.com:rogerq/linux:
  mtd: nand: omap2: Implement NAND ready using gpiolib
  memory: omap-gpmc: Prevent GPMC_STATUS from being accessed via gpmc_regs
  memory: omap-gpmc: Support WAIT pin edge interrupts
  memory: omap-gpmc: Reserve WAITPIN if needed for WAIT monitoring
  memory: omap-gpmc: Support general purpose input for WAITPINs
  memory: omap-gpmc: Move device tree binding to correct location
  memory: omap-gpmc: Prevent mapping into 1st 16MB
  mtd: nand: omap: Update DT binding documentation
  mtd: nand: omap: Clean up device tree support
  mtd: nand: omap: Copy platform data parameters to omap_nand_info data
  mtd: nand: omap: Switch to using GPMC-NAND ops for writebuffer empty check
  mtd: nand: omap: Use gpmc_omap_get_nand_ops() to get NAND registers
  memory: omap-gpmc: Implement IRQ domain for NAND IRQs
  memory: omap-gpmc: Add GPMC-NAND ops to get writebufferempty status
  memory: omap-gpmc: Introduce GPMC to NAND interface
  ARM: OMAP2+: gpmc: Add gpmc timings and settings to platform data
  ARM: OMAP2+: gpmc: Add platform data
parents f55532a0 10f22ee3
...@@ -32,6 +32,19 @@ Required properties: ...@@ -32,6 +32,19 @@ Required properties:
bootloader) are used for the physical address decoding. bootloader) are used for the physical address decoding.
As this will change in the future, filling correct As this will change in the future, filling correct
values here is a requirement. values here is a requirement.
- interrupt-controller: The GPMC driver implements and interrupt controller for
the NAND events "fifoevent" and "termcount" plus the
rising/falling edges on the GPMC_WAIT pins.
The interrupt number mapping is as follows
0 - NAND_fifoevent
1 - NAND_termcount
2 - GPMC_WAIT0 pin edge
3 - GPMC_WAIT1 pin edge, and so on.
- interrupt-cells: Must be set to 2
- gpio-controller: The GPMC driver implements a GPIO controller for the
GPMC WAIT pins that can be used as general purpose inputs.
0 maps to GPMC_WAIT0 pin.
- gpio-cells: Must be set to 2
Timing properties for child nodes. All are optional and default to 0. Timing properties for child nodes. All are optional and default to 0.
...@@ -130,6 +143,10 @@ Example for an AM33xx board: ...@@ -130,6 +143,10 @@ Example for an AM33xx board:
#address-cells = <2>; #address-cells = <2>;
#size-cells = <1>; #size-cells = <1>;
ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */
interrupt-controller;
#interrupt-cells = <2>;
gpio-controller;
#gpio-cells = <2>;
/* child nodes go here */ /* child nodes go here */
}; };
...@@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt ...@@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt
Required properties: Required properties:
- reg: The CS line the peripheral is connected to - compatible: "ti,omap2-nand"
- reg: range id (CS number), base offset and length of the
NAND I/O space
- interrupt-parent: must point to gpmc node
- interrupts: Two interrupt specifiers, one for fifoevent, one for termcount.
Optional properties: Optional properties:
...@@ -44,6 +48,7 @@ Optional properties: ...@@ -44,6 +48,7 @@ Optional properties:
locating ECC errors for BCHx algorithms. SoC devices which have locating ECC errors for BCHx algorithms. SoC devices which have
ELM hardware engines should specify this device node in .dtsi ELM hardware engines should specify this device node in .dtsi
Using ELM for ECC error correction frees some CPU cycles. Using ELM for ECC error correction frees some CPU cycles.
- rb-gpios: GPIO specifier for the ready/busy# pin.
For inline partition table parsing (optional): For inline partition table parsing (optional):
...@@ -55,20 +60,26 @@ Example for an AM33xx board: ...@@ -55,20 +60,26 @@ Example for an AM33xx board:
gpmc: gpmc@50000000 { gpmc: gpmc@50000000 {
compatible = "ti,am3352-gpmc"; compatible = "ti,am3352-gpmc";
ti,hwmods = "gpmc"; ti,hwmods = "gpmc";
reg = <0x50000000 0x1000000>; reg = <0x50000000 0x36c>;
interrupts = <100>; interrupts = <100>;
gpmc,num-cs = <8>; gpmc,num-cs = <8>;
gpmc,num-waitpins = <2>; gpmc,num-waitpins = <2>;
#address-cells = <2>; #address-cells = <2>;
#size-cells = <1>; #size-cells = <1>;
ranges = <0 0 0x08000000 0x2000>; /* CS0: NAND */ ranges = <0 0 0x08000000 0x1000000>; /* CS0 space, 16MB */
elm_id = <&elm>; elm_id = <&elm>;
interrupt-controller;
#interrupt-cells = <2>;
nand@0,0 { nand@0,0 {
reg = <0 0 0>; /* CS0, offset 0 */ compatible = "ti,omap2-nand";
reg = <0 0 4>; /* CS0, offset 0, NAND I/O window 4 */
interrupt-parent = <&gpmc>;
interrupts = <0 IRQ_TYPE_NONE>, <1 IRQ_TYPE NONE>;
nand-bus-width = <16>; nand-bus-width = <16>;
ti,nand-ecc-opt = "bch8"; ti,nand-ecc-opt = "bch8";
ti,nand-xfer-type = "polled"; ti,nand-xfer-type = "polled";
rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */
gpmc,sync-clk-ps = <0>; gpmc,sync-clk-ps = <0>;
gpmc,cs-on-ns = <0>; gpmc,cs-on-ns = <0>;
......
...@@ -97,9 +97,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, ...@@ -97,9 +97,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT); gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
memset(&s, 0, sizeof(struct gpmc_settings)); memset(&s, 0, sizeof(struct gpmc_settings));
if (gpmc_nand_data->of_node)
gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);
else
gpmc_set_legacy(gpmc_nand_data, &s); gpmc_set_legacy(gpmc_nand_data, &s);
s.device_nand = true; s.device_nand = true;
...@@ -121,8 +118,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, ...@@ -121,8 +118,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
if (err < 0) if (err < 0)
goto out_free_cs; goto out_free_cs;
gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs);
if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) { if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) {
pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n"); pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n");
err = -EINVAL; err = -EINVAL;
......
...@@ -51,6 +51,7 @@ config TI_EMIF ...@@ -51,6 +51,7 @@ config TI_EMIF
config OMAP_GPMC config OMAP_GPMC
bool bool
select GPIOLIB
help help
This driver is for the General Purpose Memory Controller (GPMC) This driver is for the General Purpose Memory Controller (GPMC)
present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -29,7 +31,6 @@ ...@@ -29,7 +31,6 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/omap-gpmc.h> #include <linux/omap-gpmc.h>
#include <linux/mtd/nand.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/platform_data/mtd-nand-omap2.h> #include <linux/platform_data/mtd-nand-omap2.h>
...@@ -81,6 +82,8 @@ ...@@ -81,6 +82,8 @@
#define GPMC_CONFIG_LIMITEDADDRESS BIT(1) #define GPMC_CONFIG_LIMITEDADDRESS BIT(1)
#define GPMC_STATUS_EMPTYWRITEBUFFERSTATUS BIT(0)
#define GPMC_CONFIG2_CSEXTRADELAY BIT(7) #define GPMC_CONFIG2_CSEXTRADELAY BIT(7)
#define GPMC_CONFIG3_ADVEXTRADELAY BIT(7) #define GPMC_CONFIG3_ADVEXTRADELAY BIT(7)
#define GPMC_CONFIG4_OEEXTRADELAY BIT(7) #define GPMC_CONFIG4_OEEXTRADELAY BIT(7)
...@@ -92,6 +95,14 @@ ...@@ -92,6 +95,14 @@
#define GPMC_CS_SIZE 0x30 #define GPMC_CS_SIZE 0x30
#define GPMC_BCH_SIZE 0x10 #define GPMC_BCH_SIZE 0x10
/*
* The first 1MB of GPMC address space is typically mapped to
* the internal ROM. Never allocate the first page, to
* facilitate bug detection; even if we didn't boot from ROM.
* As GPMC minimum partition size is 16MB we can only start from
* there.
*/
#define GPMC_MEM_START 0x1000000
#define GPMC_MEM_END 0x3FFFFFFF #define GPMC_MEM_END 0x3FFFFFFF
#define GPMC_CHUNK_SHIFT 24 /* 16 MB */ #define GPMC_CHUNK_SHIFT 24 /* 16 MB */
...@@ -125,7 +136,6 @@ ...@@ -125,7 +136,6 @@
#define GPMC_CONFIG_RDY_BSY 0x00000001 #define GPMC_CONFIG_RDY_BSY 0x00000001
#define GPMC_CONFIG_DEV_SIZE 0x00000002 #define GPMC_CONFIG_DEV_SIZE 0x00000002
#define GPMC_CONFIG_DEV_TYPE 0x00000003 #define GPMC_CONFIG_DEV_TYPE 0x00000003
#define GPMC_SET_IRQ_STATUS 0x00000004
#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31) #define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31)
#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30) #define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30)
...@@ -174,16 +184,12 @@ ...@@ -174,16 +184,12 @@
#define GPMC_CONFIG_WRITEPROTECT 0x00000010 #define GPMC_CONFIG_WRITEPROTECT 0x00000010
#define WR_RD_PIN_MONITORING 0x00600000 #define WR_RD_PIN_MONITORING 0x00600000
#define GPMC_ENABLE_IRQ 0x0000000d
/* ECC commands */ /* ECC commands */
#define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */ #define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */
#define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */ #define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */
#define GPMC_ECC_READSYN 2 /* Reset before syndrom is read back */ #define GPMC_ECC_READSYN 2 /* Reset before syndrom is read back */
/* XXX: Only NAND irq has been considered,currently these are the only ones used #define GPMC_NR_NAND_IRQS 2 /* number of NAND specific IRQs */
*/
#define GPMC_NR_IRQ 2
enum gpmc_clk_domain { enum gpmc_clk_domain {
GPMC_CD_FCLK, GPMC_CD_FCLK,
...@@ -199,11 +205,6 @@ struct gpmc_cs_data { ...@@ -199,11 +205,6 @@ struct gpmc_cs_data {
struct resource mem; struct resource mem;
}; };
struct gpmc_client_irq {
unsigned irq;
u32 bitmask;
};
/* Structure to save gpmc cs context */ /* Structure to save gpmc cs context */
struct gpmc_cs_config { struct gpmc_cs_config {
u32 config1; u32 config1;
...@@ -231,9 +232,15 @@ struct omap3_gpmc_regs { ...@@ -231,9 +232,15 @@ struct omap3_gpmc_regs {
struct gpmc_cs_config cs_context[GPMC_CS_NUM]; struct gpmc_cs_config cs_context[GPMC_CS_NUM];
}; };
static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ]; struct gpmc_device {
static struct irq_chip gpmc_irq_chip; struct device *dev;
static int gpmc_irq_start; int irq;
struct irq_chip irq_chip;
struct gpio_chip gpio_chip;
int nirqs;
};
static struct irq_domain *gpmc_irq_domain;
static struct resource gpmc_mem_root; static struct resource gpmc_mem_root;
static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM]; static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM];
...@@ -241,8 +248,6 @@ static DEFINE_SPINLOCK(gpmc_mem_lock); ...@@ -241,8 +248,6 @@ static DEFINE_SPINLOCK(gpmc_mem_lock);
/* Define chip-selects as reserved by default until probe completes */ /* Define chip-selects as reserved by default until probe completes */
static unsigned int gpmc_cs_num = GPMC_CS_NUM; static unsigned int gpmc_cs_num = GPMC_CS_NUM;
static unsigned int gpmc_nr_waitpins; static unsigned int gpmc_nr_waitpins;
static struct device *gpmc_dev;
static int gpmc_irq;
static resource_size_t phys_base, mem_size; static resource_size_t phys_base, mem_size;
static unsigned gpmc_capability; static unsigned gpmc_capability;
static void __iomem *gpmc_base; static void __iomem *gpmc_base;
...@@ -1054,14 +1059,6 @@ int gpmc_configure(int cmd, int wval) ...@@ -1054,14 +1059,6 @@ int gpmc_configure(int cmd, int wval)
u32 regval; u32 regval;
switch (cmd) { switch (cmd) {
case GPMC_ENABLE_IRQ:
gpmc_write_reg(GPMC_IRQENABLE, wval);
break;
case GPMC_SET_IRQ_STATUS:
gpmc_write_reg(GPMC_IRQSTATUS, wval);
break;
case GPMC_CONFIG_WP: case GPMC_CONFIG_WP:
regval = gpmc_read_reg(GPMC_CONFIG); regval = gpmc_read_reg(GPMC_CONFIG);
if (wval) if (wval)
...@@ -1084,7 +1081,7 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) ...@@ -1084,7 +1081,7 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
{ {
int i; int i;
reg->gpmc_status = gpmc_base + GPMC_STATUS; reg->gpmc_status = NULL; /* deprecated */
reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET + reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
...@@ -1118,87 +1115,201 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) ...@@ -1118,87 +1115,201 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
} }
} }
int gpmc_get_client_irq(unsigned irq_config) static bool gpmc_nand_writebuffer_empty(void)
{ {
int i; if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
return true;
if (hweight32(irq_config) > 1) return false;
return 0; }
static struct gpmc_nand_ops nand_ops = {
.nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
};
/**
* 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)
{
if (cs >= gpmc_cs_num)
return NULL;
gpmc_update_nand_reg(reg, cs);
return &nand_ops;
}
EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops);
for (i = 0; i < GPMC_NR_IRQ; i++) int gpmc_get_client_irq(unsigned irq_config)
if (gpmc_client_irq[i].bitmask & irq_config) {
return gpmc_client_irq[i].irq; if (!gpmc_irq_domain) {
pr_warn("%s called before GPMC IRQ domain available\n",
__func__);
return 0;
}
/* we restrict this to NAND IRQs only */
if (irq_config >= GPMC_NR_NAND_IRQS)
return 0; return 0;
return irq_create_mapping(gpmc_irq_domain, irq_config);
} }
static int gpmc_irq_endis(unsigned irq, bool endis) static int gpmc_irq_endis(unsigned long hwirq, bool endis)
{ {
int i;
u32 regval; u32 regval;
for (i = 0; i < GPMC_NR_IRQ; i++) /* bits GPMC_NR_NAND_IRQS to 8 are reserved */
if (irq == gpmc_client_irq[i].irq) { if (hwirq >= GPMC_NR_NAND_IRQS)
hwirq += 8 - GPMC_NR_NAND_IRQS;
regval = gpmc_read_reg(GPMC_IRQENABLE); regval = gpmc_read_reg(GPMC_IRQENABLE);
if (endis) if (endis)
regval |= gpmc_client_irq[i].bitmask; regval |= BIT(hwirq);
else else
regval &= ~gpmc_client_irq[i].bitmask; regval &= ~BIT(hwirq);
gpmc_write_reg(GPMC_IRQENABLE, regval); gpmc_write_reg(GPMC_IRQENABLE, regval);
break;
}
return 0; return 0;
} }
static void gpmc_irq_disable(struct irq_data *p) static void gpmc_irq_disable(struct irq_data *p)
{ {
gpmc_irq_endis(p->irq, false); gpmc_irq_endis(p->hwirq, false);
} }
static void gpmc_irq_enable(struct irq_data *p) static void gpmc_irq_enable(struct irq_data *p)
{ {
gpmc_irq_endis(p->irq, true); gpmc_irq_endis(p->hwirq, true);
} }
static void gpmc_irq_noop(struct irq_data *data) { } static void gpmc_irq_mask(struct irq_data *d)
{
gpmc_irq_endis(d->hwirq, false);
}
static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; } static void gpmc_irq_unmask(struct irq_data *d)
{
gpmc_irq_endis(d->hwirq, true);
}
static int gpmc_setup_irq(void) static void gpmc_irq_edge_config(unsigned long hwirq, bool rising_edge)
{ {
int i;
u32 regval; u32 regval;
if (!gpmc_irq) /* NAND IRQs polarity is not configurable */
if (hwirq < GPMC_NR_NAND_IRQS)
return;
/* WAITPIN starts at BIT 8 */
hwirq += 8 - GPMC_NR_NAND_IRQS;
regval = gpmc_read_reg(GPMC_CONFIG);
if (rising_edge)
regval &= ~BIT(hwirq);
else
regval |= BIT(hwirq);
gpmc_write_reg(GPMC_CONFIG, regval);
}
static void gpmc_irq_ack(struct irq_data *d)
{
unsigned int hwirq = d->hwirq;
/* skip reserved bits */
if (hwirq >= GPMC_NR_NAND_IRQS)
hwirq += 8 - GPMC_NR_NAND_IRQS;
/* Setting bit to 1 clears (or Acks) the interrupt */
gpmc_write_reg(GPMC_IRQSTATUS, BIT(hwirq));
}
static int gpmc_irq_set_type(struct irq_data *d, unsigned int trigger)
{
/* can't set type for NAND IRQs */
if (d->hwirq < GPMC_NR_NAND_IRQS)
return -EINVAL;
/* We can support either rising or falling edge at a time */
if (trigger == IRQ_TYPE_EDGE_FALLING)
gpmc_irq_edge_config(d->hwirq, false);
else if (trigger == IRQ_TYPE_EDGE_RISING)
gpmc_irq_edge_config(d->hwirq, true);
else
return -EINVAL; return -EINVAL;
gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0); return 0;
if (gpmc_irq_start < 0) { }
pr_err("irq_alloc_descs failed\n");
return gpmc_irq_start; static int gpmc_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
struct gpmc_device *gpmc = d->host_data;
irq_set_chip_data(virq, gpmc);
if (hw < GPMC_NR_NAND_IRQS) {
irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN);
irq_set_chip_and_handler(virq, &gpmc->irq_chip,
handle_simple_irq);
} else {
irq_set_chip_and_handler(virq, &gpmc->irq_chip,
handle_edge_irq);
} }
gpmc_irq_chip.name = "gpmc"; return 0;
gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret; }
gpmc_irq_chip.irq_enable = gpmc_irq_enable;
gpmc_irq_chip.irq_disable = gpmc_irq_disable;
gpmc_irq_chip.irq_shutdown = gpmc_irq_noop;
gpmc_irq_chip.irq_ack = gpmc_irq_noop;
gpmc_irq_chip.irq_mask = gpmc_irq_noop;
gpmc_irq_chip.irq_unmask = gpmc_irq_noop;
gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE; static const struct irq_domain_ops gpmc_irq_domain_ops = {
gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT; .map = gpmc_irq_map,
.xlate = irq_domain_xlate_twocell,
};
static irqreturn_t gpmc_handle_irq(int irq, void *data)
{
int hwirq, virq;
u32 regval, regvalx;
struct gpmc_device *gpmc = data;
for (i = 0; i < GPMC_NR_IRQ; i++) { regval = gpmc_read_reg(GPMC_IRQSTATUS);
gpmc_client_irq[i].irq = gpmc_irq_start + i; regvalx = regval;
irq_set_chip_and_handler(gpmc_client_irq[i].irq,
&gpmc_irq_chip, handle_simple_irq); if (!regval)
irq_modify_status(gpmc_client_irq[i].irq, IRQ_NOREQUEST, return IRQ_NONE;
IRQ_NOAUTOEN);
for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) {
/* skip reserved status bits */
if (hwirq == GPMC_NR_NAND_IRQS)
regvalx >>= 8 - GPMC_NR_NAND_IRQS;
if (regvalx & BIT(hwirq)) {
virq = irq_find_mapping(gpmc_irq_domain, hwirq);
if (!virq) {
dev_warn(gpmc->dev,
"spurious irq detected hwirq %d, virq %d\n",
hwirq, virq);
} }
generic_handle_irq(virq);
}
}
gpmc_write_reg(GPMC_IRQSTATUS, regval);
return IRQ_HANDLED;
}
static int gpmc_setup_irq(struct gpmc_device *gpmc)
{
u32 regval;
int rc;
/* Disable interrupts */ /* Disable interrupts */
gpmc_write_reg(GPMC_IRQENABLE, 0); gpmc_write_reg(GPMC_IRQENABLE, 0);
...@@ -1206,22 +1317,45 @@ static int gpmc_setup_irq(void) ...@@ -1206,22 +1317,45 @@ static int gpmc_setup_irq(void)
regval = gpmc_read_reg(GPMC_IRQSTATUS); regval = gpmc_read_reg(GPMC_IRQSTATUS);
gpmc_write_reg(GPMC_IRQSTATUS, regval); gpmc_write_reg(GPMC_IRQSTATUS, regval);
return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL); gpmc->irq_chip.name = "gpmc";
gpmc->irq_chip.irq_enable = gpmc_irq_enable;
gpmc->irq_chip.irq_disable = gpmc_irq_disable;
gpmc->irq_chip.irq_ack = gpmc_irq_ack;
gpmc->irq_chip.irq_mask = gpmc_irq_mask;
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);
if (!gpmc_irq_domain) {
dev_err(gpmc->dev, "IRQ domain add failed\n");
return -ENODEV;
}
rc = request_irq(gpmc->irq, gpmc_handle_irq, 0, "gpmc", gpmc);
if (rc) {
dev_err(gpmc->dev, "failed to request irq %d: %d\n",
gpmc->irq, rc);
irq_domain_remove(gpmc_irq_domain);
gpmc_irq_domain = NULL;
}
return rc;
} }
static int gpmc_free_irq(void) static int gpmc_free_irq(struct gpmc_device *gpmc)
{ {
int i; int hwirq;
if (gpmc_irq) free_irq(gpmc->irq, gpmc);
free_irq(gpmc_irq, NULL);
for (i = 0; i < GPMC_NR_IRQ; i++) { for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++)
irq_set_handler(gpmc_client_irq[i].irq, NULL); irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq));
irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip);
}
irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ); irq_domain_remove(gpmc_irq_domain);
gpmc_irq_domain = NULL;
return 0; return 0;
} }
...@@ -1242,12 +1376,7 @@ static void gpmc_mem_init(void) ...@@ -1242,12 +1376,7 @@ static void gpmc_mem_init(void)
{ {
int cs; int cs;
/* gpmc_mem_root.start = GPMC_MEM_START;
* The first 1MB of GPMC address space is typically mapped to
* the internal ROM. Never allocate the first page, to
* facilitate bug detection; even if we didn't boot from ROM.
*/
gpmc_mem_root.start = SZ_1M;
gpmc_mem_root.end = GPMC_MEM_END; gpmc_mem_root.end = GPMC_MEM_END;
/* Reserve all regions that has been set up by bootloader */ /* Reserve all regions that has been set up by bootloader */
...@@ -1796,105 +1925,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, ...@@ -1796,105 +1925,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
of_property_read_bool(np, "gpmc,time-para-granularity"); of_property_read_bool(np, "gpmc,time-para-granularity");
} }
#if IS_ENABLED(CONFIG_MTD_NAND)
static const char * const nand_xfer_types[] = {
[NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
[NAND_OMAP_POLLED] = "polled",
[NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
[NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
};
static int gpmc_probe_nand_child(struct platform_device *pdev,
struct device_node *child)
{
u32 val;
const char *s;
struct gpmc_timings gpmc_t;
struct omap_nand_platform_data *gpmc_nand_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_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data),
GFP_KERNEL);
if (!gpmc_nand_data)
return -ENOMEM;
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);
/* 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;
}
if (!strcmp(s, "sw"))
gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
else if (!strcmp(s, "ham1") ||
!strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
gpmc_nand_data->ecc_opt =
OMAP_ECC_HAM1_CODE_HW;
else if (!strcmp(s, "bch4"))
if (gpmc_nand_data->elm_of_node)
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH4_CODE_HW;
else
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
else if (!strcmp(s, "bch8"))
if (gpmc_nand_data->elm_of_node)
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH8_CODE_HW;
else
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
else if (!strcmp(s, "bch16"))
if (gpmc_nand_data->elm_of_node)
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH16_CODE_HW;
else
pr_err("%s: BCH16 requires ELM support\n", __func__);
else
pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
/* select data transfer mode for NAND controller */
if (!of_property_read_string(child, "ti,nand-xfer-type", &s))
for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++)
if (!strcasecmp(s, nand_xfer_types[val])) {
gpmc_nand_data->xfer_type = val;
break;
}
gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child);
val = of_get_nand_bus_width(child);
if (val == 16)
gpmc_nand_data->devsize = NAND_BUSWIDTH_16;
gpmc_read_timings_dt(child, &gpmc_t);
gpmc_nand_init(gpmc_nand_data, &gpmc_t);
return 0;
}
#else
static int gpmc_probe_nand_child(struct platform_device *pdev,
struct device_node *child)
{
return 0;
}
#endif
#if IS_ENABLED(CONFIG_MTD_ONENAND) #if IS_ENABLED(CONFIG_MTD_ONENAND)
static int gpmc_probe_onenand_child(struct platform_device *pdev, static int gpmc_probe_onenand_child(struct platform_device *pdev,
struct device_node *child) struct device_node *child)
...@@ -1950,6 +1980,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, ...@@ -1950,6 +1980,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
const char *name; const char *name;
int ret, cs; int ret, cs;
u32 val; 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) { if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(&pdev->dev, "%s has no 'reg' property\n", dev_err(&pdev->dev, "%s has no 'reg' property\n",
...@@ -2010,23 +2042,79 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, ...@@ -2010,23 +2042,79 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n", dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
cs, &res.start); cs, &res.start);
if (res.start < GPMC_MEM_START) {
dev_info(&pdev->dev,
"GPMC CS %d start cannot be lesser than 0x%x\n",
cs, GPMC_MEM_START);
} else if (res.end > GPMC_MEM_END) {
dev_info(&pdev->dev,
"GPMC CS %d end cannot be greater than 0x%x\n",
cs, GPMC_MEM_END);
}
goto err; goto err;
} }
ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); 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")) {
/* NAND specific setup */
val = of_get_nand_bus_width(child);
switch (val) {
case 8:
gpmc_s.device_width = GPMC_DEVWIDTH_8BIT;
break;
case 16:
gpmc_s.device_width = GPMC_DEVWIDTH_16BIT;
break;
default:
dev_err(&pdev->dev, "%s: invalid 'nand-bus-width'\n",
child->name);
ret = -EINVAL;
goto err;
}
/* disable write protect */
gpmc_configure(GPMC_CONFIG_WP, 0);
gpmc_s.device_nand = true;
} else {
ret = of_property_read_u32(child, "bank-width",
&gpmc_s.device_width);
if (ret < 0) if (ret < 0)
goto err; 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);
goto err;
}
}
gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings"); gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings");
ret = gpmc_cs_program_settings(cs, &gpmc_s); ret = gpmc_cs_program_settings(cs, &gpmc_s);
if (ret < 0) if (ret < 0)
goto err; goto err_cs;
ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s); ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n", dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n",
child->name); child->name);
goto err; goto err_cs;
} }
/* Clear limited address i.e. enable A26-A11 */ /* Clear limited address i.e. enable A26-A11 */
...@@ -2057,16 +2145,81 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, ...@@ -2057,16 +2145,81 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name); dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name);
ret = -ENODEV; ret = -ENODEV;
err_cs:
if (waitpin_desc)
gpiochip_free_own_desc(waitpin_desc);
err: err:
gpmc_cs_free(cs); gpmc_cs_free(cs);
return ret; return ret;
} }
static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
return 1; /* we're input only */
}
static int gpmc_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
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;
offset += 8;
reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
return !!reg;
}
static int gpmc_gpio_init(struct gpmc_device *gpmc)
{
int ret;
gpmc->gpio_chip.parent = gpmc->dev;
gpmc->gpio_chip.owner = THIS_MODULE;
gpmc->gpio_chip.label = DEVICE_NAME;
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;
ret = gpiochip_add(&gpmc->gpio_chip);
if (ret < 0) {
dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
return ret;
}
return 0;
}
static void gpmc_gpio_exit(struct gpmc_device *gpmc)
{
gpiochip_remove(&gpmc->gpio_chip);
}
static int gpmc_probe_dt(struct platform_device *pdev) static int gpmc_probe_dt(struct platform_device *pdev)
{ {
int ret; int ret;
struct device_node *child;
const struct of_device_id *of_id = const struct of_device_id *of_id =
of_match_device(gpmc_dt_ids, &pdev->dev); of_match_device(gpmc_dt_ids, &pdev->dev);
...@@ -2094,17 +2247,26 @@ static int gpmc_probe_dt(struct platform_device *pdev) ...@@ -2094,17 +2247,26 @@ static int gpmc_probe_dt(struct platform_device *pdev)
return ret; return ret;
} }
return 0;
}
static int gpmc_probe_dt_children(struct platform_device *pdev)
{
int ret;
struct device_node *child;
for_each_available_child_of_node(pdev->dev.of_node, child) { for_each_available_child_of_node(pdev->dev.of_node, child) {
if (!child->name) if (!child->name)
continue; continue;
if (of_node_cmp(child->name, "nand") == 0) if (of_node_cmp(child->name, "onenand") == 0)
ret = gpmc_probe_nand_child(pdev, child);
else if (of_node_cmp(child->name, "onenand") == 0)
ret = gpmc_probe_onenand_child(pdev, child); ret = gpmc_probe_onenand_child(pdev, child);
else else
ret = gpmc_probe_generic_child(pdev, child); ret = gpmc_probe_generic_child(pdev, child);
if (ret)
return ret;
} }
return 0; return 0;
...@@ -2114,6 +2276,11 @@ static int gpmc_probe_dt(struct platform_device *pdev) ...@@ -2114,6 +2276,11 @@ static int gpmc_probe_dt(struct platform_device *pdev)
{ {
return 0; return 0;
} }
static int gpmc_probe_dt_children(struct platform_device *pdev)
{
return 0;
}
#endif #endif
static int gpmc_probe(struct platform_device *pdev) static int gpmc_probe(struct platform_device *pdev)
...@@ -2121,6 +2288,14 @@ static int gpmc_probe(struct platform_device *pdev) ...@@ -2121,6 +2288,14 @@ static int gpmc_probe(struct platform_device *pdev)
int rc; int rc;
u32 l; u32 l;
struct resource *res; struct resource *res;
struct gpmc_device *gpmc;
gpmc = devm_kzalloc(&pdev->dev, sizeof(*gpmc), GFP_KERNEL);
if (!gpmc)
return -ENOMEM;
gpmc->dev = &pdev->dev;
platform_set_drvdata(pdev, gpmc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) if (res == NULL)
...@@ -2134,15 +2309,16 @@ static int gpmc_probe(struct platform_device *pdev) ...@@ -2134,15 +2309,16 @@ static int gpmc_probe(struct platform_device *pdev)
return PTR_ERR(gpmc_base); return PTR_ERR(gpmc_base);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) if (!res) {
dev_warn(&pdev->dev, "Failed to get resource: irq\n"); dev_err(&pdev->dev, "Failed to get resource: irq\n");
else return -ENOENT;
gpmc_irq = res->start; }
gpmc->irq = res->start;
gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck"); gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(gpmc_l3_clk)) { if (IS_ERR(gpmc_l3_clk)) {
dev_err(&pdev->dev, "Failed to get GPMC fck\n"); dev_err(&pdev->dev, "Failed to get GPMC fck\n");
gpmc_irq = 0;
return PTR_ERR(gpmc_l3_clk); return PTR_ERR(gpmc_l3_clk);
} }
...@@ -2151,11 +2327,18 @@ static int gpmc_probe(struct platform_device *pdev) ...@@ -2151,11 +2327,18 @@ static int gpmc_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
if (pdev->dev.of_node) {
rc = gpmc_probe_dt(pdev);
if (rc)
return rc;
} else {
gpmc_cs_num = GPMC_CS_NUM;
gpmc_nr_waitpins = GPMC_NR_WAITPINS;
}
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev); pm_runtime_get_sync(&pdev->dev);
gpmc_dev = &pdev->dev;
l = gpmc_read_reg(GPMC_REVISION); l = gpmc_read_reg(GPMC_REVISION);
/* /*
...@@ -2174,36 +2357,51 @@ static int gpmc_probe(struct platform_device *pdev) ...@@ -2174,36 +2357,51 @@ static int gpmc_probe(struct platform_device *pdev)
gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS; gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS;
if (GPMC_REVISION_MAJOR(l) > 0x5) if (GPMC_REVISION_MAJOR(l) > 0x5)
gpmc_capability |= GPMC_HAS_MUX_AAD; gpmc_capability |= GPMC_HAS_MUX_AAD;
dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
GPMC_REVISION_MINOR(l)); GPMC_REVISION_MINOR(l));
gpmc_mem_init(); gpmc_mem_init();
rc = gpmc_gpio_init(gpmc);
if (gpmc_setup_irq() < 0) if (rc)
dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); goto gpio_init_failed;
if (!pdev->dev.of_node) { gpmc->nirqs = GPMC_NR_NAND_IRQS + gpmc_nr_waitpins;
gpmc_cs_num = GPMC_CS_NUM; rc = gpmc_setup_irq(gpmc);
gpmc_nr_waitpins = GPMC_NR_WAITPINS; if (rc) {
dev_err(gpmc->dev, "gpmc_setup_irq failed\n");
goto setup_irq_failed;
} }
rc = gpmc_probe_dt(pdev); rc = gpmc_probe_dt_children(pdev);
if (rc < 0) { if (rc < 0) {
pm_runtime_put_sync(&pdev->dev); dev_err(gpmc->dev, "failed to probe DT children\n");
dev_err(gpmc_dev, "failed to probe DT parameters\n"); goto dt_children_failed;
return rc;
} }
return 0; return 0;
dt_children_failed:
gpmc_free_irq(gpmc);
setup_irq_failed:
gpmc_gpio_exit(gpmc);
gpio_init_failed:
gpmc_mem_exit();
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return rc;
} }
static int gpmc_remove(struct platform_device *pdev) static int gpmc_remove(struct platform_device *pdev)
{ {
gpmc_free_irq(); struct gpmc_device *gpmc = platform_get_drvdata(pdev);
gpmc_free_irq(gpmc);
gpmc_gpio_exit(gpmc);
gpmc_mem_exit(); gpmc_mem_exit();
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
gpmc_dev = NULL;
return 0; return 0;
} }
...@@ -2249,25 +2447,6 @@ static __exit void gpmc_exit(void) ...@@ -2249,25 +2447,6 @@ static __exit void gpmc_exit(void)
postcore_initcall(gpmc_init); postcore_initcall(gpmc_init);
module_exit(gpmc_exit); module_exit(gpmc_exit);
static irqreturn_t gpmc_handle_irq(int irq, void *dev)
{
int i;
u32 regval;
regval = gpmc_read_reg(GPMC_IRQSTATUS);
if (!regval)
return IRQ_NONE;
for (i = 0; i < GPMC_NR_IRQ; i++)
if (regval & gpmc_client_irq[i].bitmask)
generic_handle_irq(gpmc_client_irq[i].irq);
gpmc_write_reg(GPMC_IRQSTATUS, regval);
return IRQ_HANDLED;
}
static struct omap3_gpmc_regs gpmc_context; static struct omap3_gpmc_regs gpmc_context;
void omap3_gpmc_save_context(void) void omap3_gpmc_save_context(void)
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
...@@ -24,10 +25,12 @@ ...@@ -24,10 +25,12 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_mtd.h>
#include <linux/mtd/nand_bch.h> #include <linux/mtd/nand_bch.h>
#include <linux/platform_data/elm.h> #include <linux/platform_data/elm.h>
#include <linux/omap-gpmc.h>
#include <linux/platform_data/mtd-nand-omap2.h> #include <linux/platform_data/mtd-nand-omap2.h>
#define DRIVER_NAME "omap2-nand" #define DRIVER_NAME "omap2-nand"
...@@ -151,13 +154,17 @@ static struct nand_hw_control omap_gpmc_controller = { ...@@ -151,13 +154,17 @@ static struct nand_hw_control omap_gpmc_controller = {
}; };
struct omap_nand_info { struct omap_nand_info {
struct omap_nand_platform_data *pdata;
struct nand_chip nand; struct nand_chip nand;
struct platform_device *pdev; struct platform_device *pdev;
int gpmc_cs; int gpmc_cs;
unsigned long phys_base; bool dev_ready;
enum nand_io xfer_type;
int devsize;
enum omap_ecc ecc_opt; enum omap_ecc ecc_opt;
struct device_node *elm_of_node;
unsigned long phys_base;
struct completion comp; struct completion comp;
struct dma_chan *dma; struct dma_chan *dma;
int gpmc_irq_fifo; int gpmc_irq_fifo;
...@@ -168,12 +175,16 @@ struct omap_nand_info { ...@@ -168,12 +175,16 @@ struct omap_nand_info {
} iomode; } iomode;
u_char *buf; u_char *buf;
int buf_len; int buf_len;
/* Interface to GPMC */
struct gpmc_nand_regs reg; struct gpmc_nand_regs reg;
struct gpmc_nand_ops *ops;
bool flash_bbt;
/* generated at runtime depending on ECC algorithm and layout selected */ /* generated at runtime depending on ECC algorithm and layout selected */
struct nand_ecclayout oobinfo; struct nand_ecclayout oobinfo;
/* fields specific for BCHx_HW ECC scheme */ /* fields specific for BCHx_HW ECC scheme */
struct device *elm_dev; struct device *elm_dev;
struct device_node *of_node; /* NAND ready gpio */
struct gpio_desc *ready_gpiod;
}; };
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
...@@ -288,14 +299,13 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) ...@@ -288,14 +299,13 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
{ {
struct omap_nand_info *info = mtd_to_omap(mtd); struct omap_nand_info *info = mtd_to_omap(mtd);
u_char *p = (u_char *)buf; u_char *p = (u_char *)buf;
u32 status = 0; bool status;
while (len--) { while (len--) {
iowrite8(*p++, info->nand.IO_ADDR_W); iowrite8(*p++, info->nand.IO_ADDR_W);
/* wait until buffer is available for write */ /* wait until buffer is available for write */
do { do {
status = readl(info->reg.gpmc_status) & status = info->ops->nand_writebuffer_empty();
STATUS_BUFF_EMPTY;
} while (!status); } while (!status);
} }
} }
...@@ -323,7 +333,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) ...@@ -323,7 +333,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
{ {
struct omap_nand_info *info = mtd_to_omap(mtd); struct omap_nand_info *info = mtd_to_omap(mtd);
u16 *p = (u16 *) buf; u16 *p = (u16 *) buf;
u32 status = 0; bool status;
/* FIXME try bursts of writesw() or DMA ... */ /* FIXME try bursts of writesw() or DMA ... */
len >>= 1; len >>= 1;
...@@ -331,8 +341,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) ...@@ -331,8 +341,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
iowrite16(*p++, info->nand.IO_ADDR_W); iowrite16(*p++, info->nand.IO_ADDR_W);
/* wait until buffer is available for write */ /* wait until buffer is available for write */
do { do {
status = readl(info->reg.gpmc_status) & status = info->ops->nand_writebuffer_empty();
STATUS_BUFF_EMPTY;
} while (!status); } while (!status);
} }
} }
...@@ -1017,21 +1026,16 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) ...@@ -1017,21 +1026,16 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
} }
/** /**
* omap_dev_ready - calls the platform specific dev_ready function * omap_dev_ready - checks the NAND Ready GPIO line
* @mtd: MTD device structure * @mtd: MTD device structure
*
* Returns true if ready and false if busy.
*/ */
static int omap_dev_ready(struct mtd_info *mtd) static int omap_dev_ready(struct mtd_info *mtd)
{ {
unsigned int val = 0;
struct omap_nand_info *info = mtd_to_omap(mtd); struct omap_nand_info *info = mtd_to_omap(mtd);
val = readl(info->reg.gpmc_status); return gpiod_get_value(info->ready_gpiod);
if ((val & 0x100) == 0x100) {
return 1;
} else {
return 0;
}
} }
/** /**
...@@ -1630,7 +1634,7 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info, ...@@ -1630,7 +1634,7 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
"CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); "CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
return false; return false;
} }
if (ecc_needs_elm && !is_elm_present(info, pdata->elm_of_node)) { if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) {
dev_err(&info->pdev->dev, "ELM not available\n"); dev_err(&info->pdev->dev, "ELM not available\n");
return false; return false;
} }
...@@ -1638,10 +1642,86 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info, ...@@ -1638,10 +1642,86 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
return true; return true;
} }
static const char * const nand_xfer_types[] = {
[NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
[NAND_OMAP_POLLED] = "polled",
[NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
[NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
};
static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
{
struct device_node *child = dev->of_node;
int i;
const char *s;
u32 cs;
if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(dev, "reg not found in DT\n");
return -EINVAL;
}
info->gpmc_cs = cs;
/* detect availability of ELM module. Won't be present pre-OMAP4 */
info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
if (!info->elm_of_node)
dev_dbg(dev, "ti,elm-id not in DT\n");
/* select ecc-scheme for NAND */
if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
dev_err(dev, "ti,nand-ecc-opt not found\n");
return -EINVAL;
}
if (!strcmp(s, "sw")) {
info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
} else if (!strcmp(s, "ham1") ||
!strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
info->ecc_opt = OMAP_ECC_HAM1_CODE_HW;
} else if (!strcmp(s, "bch4")) {
if (info->elm_of_node)
info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
else
info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
} else if (!strcmp(s, "bch8")) {
if (info->elm_of_node)
info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
else
info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
} else if (!strcmp(s, "bch16")) {
info->ecc_opt = OMAP_ECC_BCH16_CODE_HW;
} else {
dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
return -EINVAL;
}
/* select data transfer mode */
if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
if (!strcasecmp(s, nand_xfer_types[i])) {
info->xfer_type = i;
goto next;
}
}
dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
return -EINVAL;
}
next:
of_get_nand_on_flash_bbt(child);
if (of_get_nand_bus_width(child) == 16)
info->devsize = NAND_BUSWIDTH_16;
return 0;
}
static int omap_nand_probe(struct platform_device *pdev) static int omap_nand_probe(struct platform_device *pdev)
{ {
struct omap_nand_info *info; struct omap_nand_info *info;
struct omap_nand_platform_data *pdata; struct omap_nand_platform_data *pdata = NULL;
struct mtd_info *mtd; struct mtd_info *mtd;
struct nand_chip *nand_chip; struct nand_chip *nand_chip;
struct nand_ecclayout *ecclayout; struct nand_ecclayout *ecclayout;
...@@ -1651,30 +1731,49 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1651,30 +1731,49 @@ static int omap_nand_probe(struct platform_device *pdev)
unsigned sig; unsigned sig;
unsigned oob_index; unsigned oob_index;
struct resource *res; struct resource *res;
struct device *dev = &pdev->dev;
pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
}
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
GFP_KERNEL); GFP_KERNEL);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, info);
info->pdev = pdev; info->pdev = pdev;
if (dev->of_node) {
if (omap_get_dt_info(dev, info))
return -EINVAL;
} else {
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "platform data missing\n");
return -EINVAL;
}
info->gpmc_cs = pdata->cs; info->gpmc_cs = pdata->cs;
info->reg = pdata->reg; info->reg = pdata->reg;
info->of_node = pdata->of_node;
info->ecc_opt = pdata->ecc_opt; info->ecc_opt = pdata->ecc_opt;
if (pdata->dev_ready)
dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n");
info->xfer_type = pdata->xfer_type;
info->devsize = pdata->devsize;
info->elm_of_node = pdata->elm_of_node;
info->flash_bbt = pdata->flash_bbt;
}
platform_set_drvdata(pdev, info);
info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
if (!info->ops) {
dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
return -ENODEV;
}
nand_chip = &info->nand; nand_chip = &info->nand;
mtd = nand_to_mtd(nand_chip); mtd = nand_to_mtd(nand_chip);
mtd->dev.parent = &pdev->dev; mtd->dev.parent = &pdev->dev;
nand_chip->ecc.priv = NULL; nand_chip->ecc.priv = NULL;
nand_set_flash_node(nand_chip, pdata->of_node); nand_set_flash_node(nand_chip, dev->of_node);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
...@@ -1688,6 +1787,13 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1688,6 +1787,13 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
nand_chip->cmd_ctrl = omap_hwcontrol; nand_chip->cmd_ctrl = omap_hwcontrol;
info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
GPIOD_IN);
if (IS_ERR(info->ready_gpiod)) {
dev_err(dev, "failed to get ready gpio\n");
return PTR_ERR(info->ready_gpiod);
}
/* /*
* If RDY/BSY line is connected to OMAP then use the omap ready * If RDY/BSY line is connected to OMAP then use the omap ready
* function and the generic nand_wait function which reads the status * function and the generic nand_wait function which reads the status
...@@ -1695,7 +1801,7 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1695,7 +1801,7 @@ static int omap_nand_probe(struct platform_device *pdev)
* chip delay which is slightly more than tR (AC Timing) of the NAND * chip delay which is slightly more than tR (AC Timing) of the NAND
* device and read status register until you get a failure or success * device and read status register until you get a failure or success
*/ */
if (pdata->dev_ready) { if (info->ready_gpiod) {
nand_chip->dev_ready = omap_dev_ready; nand_chip->dev_ready = omap_dev_ready;
nand_chip->chip_delay = 0; nand_chip->chip_delay = 0;
} else { } else {
...@@ -1703,21 +1809,22 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1703,21 +1809,22 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 50; nand_chip->chip_delay = 50;
} }
if (pdata->flash_bbt) if (info->flash_bbt)
nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
else else
nand_chip->options |= NAND_SKIP_BBTSCAN; nand_chip->options |= NAND_SKIP_BBTSCAN;
/* scan NAND device connected to chip controller */ /* scan NAND device connected to chip controller */
nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16; nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
if (nand_scan_ident(mtd, 1, NULL)) { if (nand_scan_ident(mtd, 1, NULL)) {
dev_err(&info->pdev->dev, "scan failed, may be bus-width mismatch\n"); dev_err(&info->pdev->dev,
"scan failed, may be bus-width mismatch\n");
err = -ENXIO; err = -ENXIO;
goto return_error; goto return_error;
} }
/* re-populate low-level callbacks based on xfer modes */ /* re-populate low-level callbacks based on xfer modes */
switch (pdata->xfer_type) { switch (info->xfer_type) {
case NAND_OMAP_PREFETCH_POLLED: case NAND_OMAP_PREFETCH_POLLED:
nand_chip->read_buf = omap_read_buf_pref; nand_chip->read_buf = omap_read_buf_pref;
nand_chip->write_buf = omap_write_buf_pref; nand_chip->write_buf = omap_write_buf_pref;
...@@ -1797,7 +1904,7 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1797,7 +1904,7 @@ static int omap_nand_probe(struct platform_device *pdev)
default: default:
dev_err(&pdev->dev, dev_err(&pdev->dev,
"xfer_type(%d) not supported!\n", pdata->xfer_type); "xfer_type(%d) not supported!\n", info->xfer_type);
err = -EINVAL; err = -EINVAL;
goto return_error; goto return_error;
} }
...@@ -2020,6 +2127,9 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2020,6 +2127,9 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error; goto return_error;
} }
if (dev->of_node)
mtd_device_register(mtd, NULL, 0);
else
mtd_device_register(mtd, pdata->parts, pdata->nr_parts); mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, mtd); platform_set_drvdata(pdev, mtd);
...@@ -2051,11 +2161,17 @@ static int omap_nand_remove(struct platform_device *pdev) ...@@ -2051,11 +2161,17 @@ static int omap_nand_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct of_device_id omap_nand_ids[] = {
{ .compatible = "ti,omap2-nand", },
{},
};
static struct platform_driver omap_nand_driver = { static struct platform_driver omap_nand_driver = {
.probe = omap_nand_probe, .probe = omap_nand_probe,
.remove = omap_nand_remove, .remove = omap_nand_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.of_match_table = of_match_ptr(omap_nand_ids),
}, },
}; };
......
...@@ -7,161 +7,53 @@ ...@@ -7,161 +7,53 @@
* option) any later version. * option) any later version.
*/ */
/* Maximum Number of Chip Selects */ #include <linux/platform_data/gpmc-omap.h>
#define GPMC_CS_NUM 8
#define GPMC_CONFIG_WP 0x00000005 #define GPMC_CONFIG_WP 0x00000005
#define GPMC_IRQ_FIFOEVENTENABLE 0x01 /* IRQ numbers in GPMC IRQ domain for legacy boot use */
#define GPMC_IRQ_COUNT_EVENT 0x02 #define GPMC_IRQ_FIFOEVENTENABLE 0
#define GPMC_IRQ_COUNT_EVENT 1
#define GPMC_BURST_4 4 /* 4 word burst */
#define GPMC_BURST_8 8 /* 8 word burst */
#define GPMC_BURST_16 16 /* 16 word burst */
#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */
#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */
#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */
#define GPMC_MUX_AD 2 /* Addr-Data multiplex */
/* 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 * gpmc_nand_ops - Interface between NAND and GPMC
* (which is in picoseconds), while the register values are in gpmc_fck cycles. * @nand_write_buffer_empty: get the NAND write buffer empty status.
*/ */
struct gpmc_timings { struct gpmc_nand_ops {
/* Minimum clock period for synchronous mode (in picoseconds) */ bool (*nand_writebuffer_empty)(void);
u32 sync_clk;
/* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */
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 */
u32 adv_aad_mux_on; /* ADV assertion time for AAD */
u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */
u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */
/* 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 */
u32 oe_aad_mux_on; /* OE assertion time for AAD */
u32 oe_aad_mux_off; /* OE deassertion time for AAD */
/* 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;
}; };
/* Device timings in picoseconds */ struct gpmc_nand_regs;
struct gpmc_device_timings {
u32 t_ceasu; /* address setup to CS valid */
u32 t_avdasu; /* address setup to ADV valid */
/* XXX: try to combine t_avdp_r & t_avdp_w. Issue is
* of tusb using these timings even for sync whilst
* ideally for adv_rd/(wr)_off it should have considered
* t_avdh instead. This indirectly necessitates r/w
* variations of t_avdp as it is possible to have one
* sync & other async
*/
u32 t_avdp_r; /* ADV low time (what about t_cer ?) */
u32 t_avdp_w;
u32 t_aavdh; /* address hold time */
u32 t_oeasu; /* address setup to OE valid */
u32 t_aa; /* access time from ADV assertion */
u32 t_iaa; /* initial access time */
u32 t_oe; /* access time from OE assertion */
u32 t_ce; /* access time from CS asertion */
u32 t_rd_cycle; /* read cycle time */
u32 t_cez_r; /* read CS deassertion to high Z */
u32 t_cez_w; /* write CS deassertion to high Z */
u32 t_oez; /* OE deassertion to high Z */
u32 t_weasu; /* address setup to WE valid */
u32 t_wpl; /* write assertion time */
u32 t_wph; /* write deassertion time */
u32 t_wr_cycle; /* write cycle time */
u32 clk;
u32 t_bacc; /* burst access valid clock to output delay */
u32 t_ces; /* CS setup time to clk */
u32 t_avds; /* ADV setup time to clk */
u32 t_avdh; /* ADV hold time from clk */
u32 t_ach; /* address hold time from clk */
u32 t_rdyo; /* clk to ready valid */
u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */
u32 t_ce_avd; /* CS on to ADV on delay */
/* XXX: check the possibility of combining
* cyc_aavhd_oe & cyc_aavdh_we
*/
u8 cyc_aavdh_oe;/* read address hold time in cycles */
u8 cyc_aavdh_we;/* write address hold time in cycles */
u8 cyc_oe; /* access time from OE assertion in cycles */
u8 cyc_wpl; /* write deassertion time in cycles */
u32 cyc_iaa; /* initial access time in cycles */
/* extra delays */
bool ce_xdelay;
bool avd_xdelay;
bool oe_xdelay;
bool we_xdelay;
};
struct gpmc_settings { #if IS_ENABLED(CONFIG_OMAP_GPMC)
bool burst_wrap; /* enables wrap bursting */ struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
bool burst_read; /* enables read page/burst mode */ int cs);
bool burst_write; /* enables write page/burst mode */ #else
bool device_nand; /* device is NAND */ static inline gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
bool sync_read; /* enables synchronous reads */ int cs)
bool sync_write; /* enables synchronous writes */ {
bool wait_on_read; /* monitor wait on reads */ return NULL;
bool wait_on_write; /* monitor wait on writes */ }
u32 burst_len; /* page/burst length */ #endif /* CONFIG_OMAP_GPMC */
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 */
}; /* deprecated APIs */
#if IS_ENABLED(CONFIG_OMAP_GPMC)
void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
#else
static inline void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
{
}
#endif /* CONFIG_OMAP_GPMC */
/*--------------------------------*/
extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t,
struct gpmc_settings *gpmc_s, struct gpmc_settings *gpmc_s,
struct gpmc_device_timings *dev_t); struct gpmc_device_timings *dev_t);
struct gpmc_nand_regs;
struct device_node; struct device_node;
extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
extern int gpmc_get_client_irq(unsigned irq_config); extern int gpmc_get_client_irq(unsigned irq_config);
extern unsigned int gpmc_ticks_to_ns(unsigned int ticks); extern unsigned int gpmc_ticks_to_ns(unsigned int ticks);
......
/*
* OMAP GPMC Platform data
*
* Copyright (C) 2014 Texas Instruments, Inc. - http://www.ti.com
* Roger Quadros <rogerq@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#ifndef _GPMC_OMAP_H_
#define _GPMC_OMAP_H_
/* Maximum Number of Chip Selects */
#define GPMC_CS_NUM 8
/* 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 GPMC_CS_CONFIG2 */
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 */
u32 adv_aad_mux_on; /* ADV assertion time for AAD */
u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */
u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */
/* 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 */
u32 oe_aad_mux_on; /* OE assertion time for AAD */
u32 oe_aad_mux_off; /* OE deassertion time for AAD */
/* 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;
};
/* Device timings in picoseconds */
struct gpmc_device_timings {
u32 t_ceasu; /* address setup to CS valid */
u32 t_avdasu; /* address setup to ADV valid */
/* XXX: try to combine t_avdp_r & t_avdp_w. Issue is
* of tusb using these timings even for sync whilst
* ideally for adv_rd/(wr)_off it should have considered
* t_avdh instead. This indirectly necessitates r/w
* variations of t_avdp as it is possible to have one
* sync & other async
*/
u32 t_avdp_r; /* ADV low time (what about t_cer ?) */
u32 t_avdp_w;
u32 t_aavdh; /* address hold time */
u32 t_oeasu; /* address setup to OE valid */
u32 t_aa; /* access time from ADV assertion */
u32 t_iaa; /* initial access time */
u32 t_oe; /* access time from OE assertion */
u32 t_ce; /* access time from CS asertion */
u32 t_rd_cycle; /* read cycle time */
u32 t_cez_r; /* read CS deassertion to high Z */
u32 t_cez_w; /* write CS deassertion to high Z */
u32 t_oez; /* OE deassertion to high Z */
u32 t_weasu; /* address setup to WE valid */
u32 t_wpl; /* write assertion time */
u32 t_wph; /* write deassertion time */
u32 t_wr_cycle; /* write cycle time */
u32 clk;
u32 t_bacc; /* burst access valid clock to output delay */
u32 t_ces; /* CS setup time to clk */
u32 t_avds; /* ADV setup time to clk */
u32 t_avdh; /* ADV hold time from clk */
u32 t_ach; /* address hold time from clk */
u32 t_rdyo; /* clk to ready valid */
u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */
u32 t_ce_avd; /* CS on to ADV on delay */
/* XXX: check the possibility of combining
* cyc_aavhd_oe & cyc_aavdh_we
*/
u8 cyc_aavdh_oe;/* read address hold time in cycles */
u8 cyc_aavdh_we;/* write address hold time in cycles */
u8 cyc_oe; /* access time from OE assertion in cycles */
u8 cyc_wpl; /* write deassertion time in cycles */
u32 cyc_iaa; /* initial access time in cycles */
/* extra delays */
bool ce_xdelay;
bool avd_xdelay;
bool oe_xdelay;
bool we_xdelay;
};
#define GPMC_BURST_4 4 /* 4 word burst */
#define GPMC_BURST_8 8 /* 8 word burst */
#define GPMC_BURST_16 16 /* 16 word burst */
#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */
#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */
#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */
#define GPMC_MUX_AD 2 /* Addr-Data multiplex */
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 */
};
/* Data for each chip select */
struct gpmc_omap_cs_data {
bool valid; /* data is valid */
bool is_nand; /* device within this CS is NAND */
struct gpmc_settings *settings;
struct gpmc_device_timings *device_timings;
struct gpmc_timings *gpmc_timings;
struct platform_device *pdev; /* device within this CS region */
unsigned int pdata_size;
};
struct gpmc_omap_platform_data {
struct gpmc_omap_cs_data cs[GPMC_CS_NUM];
};
#endif /* _GPMC_OMAP_H */
...@@ -45,7 +45,6 @@ enum omap_ecc { ...@@ -45,7 +45,6 @@ enum omap_ecc {
}; };
struct gpmc_nand_regs { struct gpmc_nand_regs {
void __iomem *gpmc_status;
void __iomem *gpmc_nand_command; void __iomem *gpmc_nand_command;
void __iomem *gpmc_nand_address; void __iomem *gpmc_nand_address;
void __iomem *gpmc_nand_data; void __iomem *gpmc_nand_data;
...@@ -64,21 +63,24 @@ struct gpmc_nand_regs { ...@@ -64,21 +63,24 @@ struct gpmc_nand_regs {
void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER];
void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER];
void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
/* Deprecated. Do not use */
void __iomem *gpmc_status;
}; };
struct omap_nand_platform_data { struct omap_nand_platform_data {
int cs; int cs;
struct mtd_partition *parts; struct mtd_partition *parts;
int nr_parts; int nr_parts;
bool dev_ready;
bool flash_bbt; bool flash_bbt;
enum nand_io xfer_type; enum nand_io xfer_type;
int devsize; int devsize;
enum omap_ecc ecc_opt; enum omap_ecc ecc_opt;
struct gpmc_nand_regs reg;
/* for passing the partitions */
struct device_node *of_node;
struct device_node *elm_of_node; struct device_node *elm_of_node;
/* deprecated */
struct gpmc_nand_regs reg;
struct device_node *of_node;
bool dev_ready;
}; };
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment