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
This commit is contained in:
commit
4217ff3509
|
@ -32,6 +32,19 @@ Required properties:
|
|||
bootloader) are used for the physical address decoding.
|
||||
As this will change in the future, filling correct
|
||||
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.
|
||||
|
||||
|
@ -130,6 +143,10 @@ Example for an AM33xx board:
|
|||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
/* child nodes go here */
|
||||
};
|
|
@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt
|
|||
|
||||
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:
|
||||
|
||||
|
@ -44,6 +48,7 @@ Optional properties:
|
|||
locating ECC errors for BCHx algorithms. SoC devices which have
|
||||
ELM hardware engines should specify this device node in .dtsi
|
||||
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):
|
||||
|
||||
|
@ -55,20 +60,26 @@ Example for an AM33xx board:
|
|||
gpmc: gpmc@50000000 {
|
||||
compatible = "ti,am3352-gpmc";
|
||||
ti,hwmods = "gpmc";
|
||||
reg = <0x50000000 0x1000000>;
|
||||
reg = <0x50000000 0x36c>;
|
||||
interrupts = <100>;
|
||||
gpmc,num-cs = <8>;
|
||||
gpmc,num-waitpins = <2>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0x08000000 0x2000>; /* CS0: NAND */
|
||||
ranges = <0 0 0x08000000 0x1000000>; /* CS0 space, 16MB */
|
||||
elm_id = <&elm>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
|
||||
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>;
|
||||
ti,nand-ecc-opt = "bch8";
|
||||
ti,nand-xfer-type = "polled";
|
||||
rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */
|
||||
|
||||
gpmc,sync-clk-ps = <0>;
|
||||
gpmc,cs-on-ns = <0>;
|
||||
|
|
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
s.device_nand = true;
|
||||
|
@ -121,8 +118,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
|
|||
if (err < 0)
|
||||
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)) {
|
||||
pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n");
|
||||
err = -EINVAL;
|
||||
|
|
|
@ -51,6 +51,7 @@ config TI_EMIF
|
|||
|
||||
config OMAP_GPMC
|
||||
bool
|
||||
select GPIOLIB
|
||||
help
|
||||
This driver is for the General Purpose Memory Controller (GPMC)
|
||||
present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
@ -29,7 +31,6 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/omap-gpmc.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/platform_data/mtd-nand-omap2.h>
|
||||
|
@ -81,6 +82,8 @@
|
|||
|
||||
#define GPMC_CONFIG_LIMITEDADDRESS BIT(1)
|
||||
|
||||
#define GPMC_STATUS_EMPTYWRITEBUFFERSTATUS BIT(0)
|
||||
|
||||
#define GPMC_CONFIG2_CSEXTRADELAY BIT(7)
|
||||
#define GPMC_CONFIG3_ADVEXTRADELAY BIT(7)
|
||||
#define GPMC_CONFIG4_OEEXTRADELAY BIT(7)
|
||||
|
@ -92,6 +95,14 @@
|
|||
#define GPMC_CS_SIZE 0x30
|
||||
#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_CHUNK_SHIFT 24 /* 16 MB */
|
||||
|
@ -125,7 +136,6 @@
|
|||
#define GPMC_CONFIG_RDY_BSY 0x00000001
|
||||
#define GPMC_CONFIG_DEV_SIZE 0x00000002
|
||||
#define GPMC_CONFIG_DEV_TYPE 0x00000003
|
||||
#define GPMC_SET_IRQ_STATUS 0x00000004
|
||||
|
||||
#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31)
|
||||
#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30)
|
||||
|
@ -174,16 +184,12 @@
|
|||
#define GPMC_CONFIG_WRITEPROTECT 0x00000010
|
||||
#define WR_RD_PIN_MONITORING 0x00600000
|
||||
|
||||
#define GPMC_ENABLE_IRQ 0x0000000d
|
||||
|
||||
/* ECC commands */
|
||||
#define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */
|
||||
#define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */
|
||||
#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_IRQ 2
|
||||
#define GPMC_NR_NAND_IRQS 2 /* number of NAND specific IRQs */
|
||||
|
||||
enum gpmc_clk_domain {
|
||||
GPMC_CD_FCLK,
|
||||
|
@ -199,11 +205,6 @@ struct gpmc_cs_data {
|
|||
struct resource mem;
|
||||
};
|
||||
|
||||
struct gpmc_client_irq {
|
||||
unsigned irq;
|
||||
u32 bitmask;
|
||||
};
|
||||
|
||||
/* Structure to save gpmc cs context */
|
||||
struct gpmc_cs_config {
|
||||
u32 config1;
|
||||
|
@ -231,9 +232,15 @@ struct omap3_gpmc_regs {
|
|||
struct gpmc_cs_config cs_context[GPMC_CS_NUM];
|
||||
};
|
||||
|
||||
static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
|
||||
static struct irq_chip gpmc_irq_chip;
|
||||
static int gpmc_irq_start;
|
||||
struct gpmc_device {
|
||||
struct device *dev;
|
||||
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 gpmc_cs_data gpmc_cs[GPMC_CS_NUM];
|
||||
|
@ -241,8 +248,6 @@ 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 struct device *gpmc_dev;
|
||||
static int gpmc_irq;
|
||||
static resource_size_t phys_base, mem_size;
|
||||
static unsigned gpmc_capability;
|
||||
static void __iomem *gpmc_base;
|
||||
|
@ -1054,14 +1059,6 @@ int gpmc_configure(int cmd, int wval)
|
|||
u32 regval;
|
||||
|
||||
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:
|
||||
regval = gpmc_read_reg(GPMC_CONFIG);
|
||||
if (wval)
|
||||
|
@ -1084,7 +1081,7 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
|
|||
{
|
||||
int i;
|
||||
|
||||
reg->gpmc_status = gpmc_base + GPMC_STATUS;
|
||||
reg->gpmc_status = NULL; /* deprecated */
|
||||
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 +
|
||||
|
@ -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 0;
|
||||
|
||||
for (i = 0; i < GPMC_NR_IRQ; i++)
|
||||
if (gpmc_client_irq[i].bitmask & irq_config)
|
||||
return gpmc_client_irq[i].irq;
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int gpmc_irq_endis(unsigned irq, bool endis)
|
||||
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);
|
||||
|
||||
int gpmc_get_client_irq(unsigned irq_config)
|
||||
{
|
||||
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 irq_create_mapping(gpmc_irq_domain, irq_config);
|
||||
}
|
||||
|
||||
static int gpmc_irq_endis(unsigned long hwirq, bool endis)
|
||||
{
|
||||
int i;
|
||||
u32 regval;
|
||||
|
||||
for (i = 0; i < GPMC_NR_IRQ; i++)
|
||||
if (irq == gpmc_client_irq[i].irq) {
|
||||
/* bits GPMC_NR_NAND_IRQS to 8 are reserved */
|
||||
if (hwirq >= GPMC_NR_NAND_IRQS)
|
||||
hwirq += 8 - GPMC_NR_NAND_IRQS;
|
||||
|
||||
regval = gpmc_read_reg(GPMC_IRQENABLE);
|
||||
if (endis)
|
||||
regval |= gpmc_client_irq[i].bitmask;
|
||||
regval |= BIT(hwirq);
|
||||
else
|
||||
regval &= ~gpmc_client_irq[i].bitmask;
|
||||
regval &= ~BIT(hwirq);
|
||||
gpmc_write_reg(GPMC_IRQENABLE, regval);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gpmc_irq_endis(p->irq, true);
|
||||
gpmc_irq_endis(p->hwirq, true);
|
||||
}
|
||||
|
||||
static void gpmc_irq_noop(struct irq_data *data) { }
|
||||
|
||||
static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; }
|
||||
|
||||
static int gpmc_setup_irq(void)
|
||||
static void gpmc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
gpmc_irq_endis(d->hwirq, false);
|
||||
}
|
||||
|
||||
static void gpmc_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
gpmc_irq_endis(d->hwirq, true);
|
||||
}
|
||||
|
||||
static void gpmc_irq_edge_config(unsigned long hwirq, bool rising_edge)
|
||||
{
|
||||
int i;
|
||||
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;
|
||||
|
||||
gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0);
|
||||
if (gpmc_irq_start < 0) {
|
||||
pr_err("irq_alloc_descs failed\n");
|
||||
return gpmc_irq_start;
|
||||
/* 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 0;
|
||||
}
|
||||
|
||||
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";
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE;
|
||||
gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT;
|
||||
static const struct irq_domain_ops gpmc_irq_domain_ops = {
|
||||
.map = gpmc_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
for (i = 0; i < GPMC_NR_IRQ; i++) {
|
||||
gpmc_client_irq[i].irq = gpmc_irq_start + i;
|
||||
irq_set_chip_and_handler(gpmc_client_irq[i].irq,
|
||||
&gpmc_irq_chip, handle_simple_irq);
|
||||
irq_modify_status(gpmc_client_irq[i].irq, IRQ_NOREQUEST,
|
||||
IRQ_NOAUTOEN);
|
||||
static irqreturn_t gpmc_handle_irq(int irq, void *data)
|
||||
{
|
||||
int hwirq, virq;
|
||||
u32 regval, regvalx;
|
||||
struct gpmc_device *gpmc = data;
|
||||
|
||||
regval = gpmc_read_reg(GPMC_IRQSTATUS);
|
||||
regvalx = regval;
|
||||
|
||||
if (!regval)
|
||||
return IRQ_NONE;
|
||||
|
||||
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 */
|
||||
gpmc_write_reg(GPMC_IRQENABLE, 0);
|
||||
|
||||
|
@ -1206,22 +1317,45 @@ static int gpmc_setup_irq(void)
|
|||
regval = gpmc_read_reg(GPMC_IRQSTATUS);
|
||||
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;
|
||||
|
||||
static int gpmc_free_irq(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (gpmc_irq)
|
||||
free_irq(gpmc_irq, NULL);
|
||||
|
||||
for (i = 0; i < GPMC_NR_IRQ; i++) {
|
||||
irq_set_handler(gpmc_client_irq[i].irq, NULL);
|
||||
irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip);
|
||||
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;
|
||||
}
|
||||
|
||||
irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ);
|
||||
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(struct gpmc_device *gpmc)
|
||||
{
|
||||
int hwirq;
|
||||
|
||||
free_irq(gpmc->irq, gpmc);
|
||||
|
||||
for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++)
|
||||
irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq));
|
||||
|
||||
irq_domain_remove(gpmc_irq_domain);
|
||||
gpmc_irq_domain = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1242,12 +1376,7 @@ static void gpmc_mem_init(void)
|
|||
{
|
||||
int cs;
|
||||
|
||||
/*
|
||||
* 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.start = GPMC_MEM_START;
|
||||
gpmc_mem_root.end = GPMC_MEM_END;
|
||||
|
||||
/* 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,
|
|||
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)
|
||||
static int gpmc_probe_onenand_child(struct platform_device *pdev,
|
||||
struct device_node *child)
|
||||
|
@ -1950,6 +1980,8 @@ 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",
|
||||
|
@ -2010,23 +2042,79 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
|
|||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
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");
|
||||
|
||||
ret = gpmc_cs_program_settings(cs, &gpmc_s);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
goto err_cs;
|
||||
|
||||
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);
|
||||
goto err;
|
||||
goto err_cs;
|
||||
}
|
||||
|
||||
/* Clear limited address i.e. enable A26-A11 */
|
||||
|
@ -2057,16 +2145,81 @@ err_child_fail:
|
|||
dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name);
|
||||
ret = -ENODEV;
|
||||
|
||||
err_cs:
|
||||
if (waitpin_desc)
|
||||
gpiochip_free_own_desc(waitpin_desc);
|
||||
|
||||
err:
|
||||
gpmc_cs_free(cs);
|
||||
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *child;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(gpmc_dt_ids, &pdev->dev);
|
||||
|
||||
|
@ -2094,17 +2247,26 @@ static int gpmc_probe_dt(struct platform_device *pdev)
|
|||
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) {
|
||||
|
||||
if (!child->name)
|
||||
continue;
|
||||
|
||||
if (of_node_cmp(child->name, "nand") == 0)
|
||||
ret = gpmc_probe_nand_child(pdev, child);
|
||||
else if (of_node_cmp(child->name, "onenand") == 0)
|
||||
if (of_node_cmp(child->name, "onenand") == 0)
|
||||
ret = gpmc_probe_onenand_child(pdev, child);
|
||||
else
|
||||
ret = gpmc_probe_generic_child(pdev, child);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2114,6 +2276,11 @@ static int gpmc_probe_dt(struct platform_device *pdev)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpmc_probe_dt_children(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int gpmc_probe(struct platform_device *pdev)
|
||||
|
@ -2121,6 +2288,14 @@ static int gpmc_probe(struct platform_device *pdev)
|
|||
int rc;
|
||||
u32 l;
|
||||
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);
|
||||
if (res == NULL)
|
||||
|
@ -2134,15 +2309,16 @@ static int gpmc_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(gpmc_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (res == NULL)
|
||||
dev_warn(&pdev->dev, "Failed to get resource: irq\n");
|
||||
else
|
||||
gpmc_irq = res->start;
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get resource: irq\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
gpmc->irq = res->start;
|
||||
|
||||
gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
|
||||
if (IS_ERR(gpmc_l3_clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get GPMC fck\n");
|
||||
gpmc_irq = 0;
|
||||
return PTR_ERR(gpmc_l3_clk);
|
||||
}
|
||||
|
||||
|
@ -2151,11 +2327,18 @@ static int gpmc_probe(struct platform_device *pdev)
|
|||
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_get_sync(&pdev->dev);
|
||||
|
||||
gpmc_dev = &pdev->dev;
|
||||
|
||||
l = gpmc_read_reg(GPMC_REVISION);
|
||||
|
||||
/*
|
||||
|
@ -2174,36 +2357,51 @@ static int gpmc_probe(struct platform_device *pdev)
|
|||
gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS;
|
||||
if (GPMC_REVISION_MAJOR(l) > 0x5)
|
||||
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_mem_init();
|
||||
rc = gpmc_gpio_init(gpmc);
|
||||
if (rc)
|
||||
goto gpio_init_failed;
|
||||
|
||||
if (gpmc_setup_irq() < 0)
|
||||
dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
gpmc_cs_num = GPMC_CS_NUM;
|
||||
gpmc_nr_waitpins = GPMC_NR_WAITPINS;
|
||||
gpmc->nirqs = GPMC_NR_NAND_IRQS + gpmc_nr_waitpins;
|
||||
rc = gpmc_setup_irq(gpmc);
|
||||
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) {
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
dev_err(gpmc_dev, "failed to probe DT parameters\n");
|
||||
return rc;
|
||||
dev_err(gpmc->dev, "failed to probe DT children\n");
|
||||
goto dt_children_failed;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gpmc_free_irq();
|
||||
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
|
||||
|
||||
gpmc_free_irq(gpmc);
|
||||
gpmc_gpio_exit(gpmc);
|
||||
gpmc_mem_exit();
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
gpmc_dev = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2249,25 +2447,6 @@ static __exit void gpmc_exit(void)
|
|||
postcore_initcall(gpmc_init);
|
||||
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;
|
||||
|
||||
void omap3_gpmc_save_context(void)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
@ -24,10 +25,12 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_mtd.h>
|
||||
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/platform_data/elm.h>
|
||||
|
||||
#include <linux/omap-gpmc.h>
|
||||
#include <linux/platform_data/mtd-nand-omap2.h>
|
||||
|
||||
#define DRIVER_NAME "omap2-nand"
|
||||
|
@ -151,13 +154,17 @@ static struct nand_hw_control omap_gpmc_controller = {
|
|||
};
|
||||
|
||||
struct omap_nand_info {
|
||||
struct omap_nand_platform_data *pdata;
|
||||
struct nand_chip nand;
|
||||
struct platform_device *pdev;
|
||||
|
||||
int gpmc_cs;
|
||||
unsigned long phys_base;
|
||||
bool dev_ready;
|
||||
enum nand_io xfer_type;
|
||||
int devsize;
|
||||
enum omap_ecc ecc_opt;
|
||||
struct device_node *elm_of_node;
|
||||
|
||||
unsigned long phys_base;
|
||||
struct completion comp;
|
||||
struct dma_chan *dma;
|
||||
int gpmc_irq_fifo;
|
||||
|
@ -168,12 +175,16 @@ struct omap_nand_info {
|
|||
} iomode;
|
||||
u_char *buf;
|
||||
int buf_len;
|
||||
/* Interface to GPMC */
|
||||
struct gpmc_nand_regs reg;
|
||||
struct gpmc_nand_ops *ops;
|
||||
bool flash_bbt;
|
||||
/* generated at runtime depending on ECC algorithm and layout selected */
|
||||
struct nand_ecclayout oobinfo;
|
||||
/* fields specific for BCHx_HW ECC scheme */
|
||||
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)
|
||||
|
@ -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);
|
||||
u_char *p = (u_char *)buf;
|
||||
u32 status = 0;
|
||||
bool status;
|
||||
|
||||
while (len--) {
|
||||
iowrite8(*p++, info->nand.IO_ADDR_W);
|
||||
/* wait until buffer is available for write */
|
||||
do {
|
||||
status = readl(info->reg.gpmc_status) &
|
||||
STATUS_BUFF_EMPTY;
|
||||
status = info->ops->nand_writebuffer_empty();
|
||||
} while (!status);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
u16 *p = (u16 *) buf;
|
||||
u32 status = 0;
|
||||
bool status;
|
||||
/* FIXME try bursts of writesw() or DMA ... */
|
||||
len >>= 1;
|
||||
|
||||
|
@ -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);
|
||||
/* wait until buffer is available for write */
|
||||
do {
|
||||
status = readl(info->reg.gpmc_status) &
|
||||
STATUS_BUFF_EMPTY;
|
||||
status = info->ops->nand_writebuffer_empty();
|
||||
} while (!status);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
* Returns true if ready and false if busy.
|
||||
*/
|
||||
static int omap_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
|
||||
val = readl(info->reg.gpmc_status);
|
||||
|
||||
if ((val & 0x100) == 0x100) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return gpiod_get_value(info->ready_gpiod);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1630,7 +1634,7 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
|
|||
"CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
|
@ -1638,10 +1642,86 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
|
|||
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)
|
||||
{
|
||||
struct omap_nand_info *info;
|
||||
struct omap_nand_platform_data *pdata;
|
||||
struct omap_nand_platform_data *pdata = NULL;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *nand_chip;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
|
@ -1651,30 +1731,49 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
unsigned sig;
|
||||
unsigned oob_index;
|
||||
struct resource *res;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "platform data missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
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->reg = pdata->reg;
|
||||
info->of_node = pdata->of_node;
|
||||
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;
|
||||
mtd = nand_to_mtd(nand_chip);
|
||||
mtd->dev.parent = &pdev->dev;
|
||||
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);
|
||||
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -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->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
|
||||
* function and the generic nand_wait function which reads the status
|
||||
|
@ -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
|
||||
* 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->chip_delay = 0;
|
||||
} else {
|
||||
|
@ -1703,21 +1809,22 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
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;
|
||||
else
|
||||
nand_chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
/* 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)) {
|
||||
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;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
/* re-populate low-level callbacks based on xfer modes */
|
||||
switch (pdata->xfer_type) {
|
||||
switch (info->xfer_type) {
|
||||
case NAND_OMAP_PREFETCH_POLLED:
|
||||
nand_chip->read_buf = omap_read_buf_pref;
|
||||
nand_chip->write_buf = omap_write_buf_pref;
|
||||
|
@ -1797,7 +1904,7 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
|
||||
default:
|
||||
dev_err(&pdev->dev,
|
||||
"xfer_type(%d) not supported!\n", pdata->xfer_type);
|
||||
"xfer_type(%d) not supported!\n", info->xfer_type);
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
}
|
||||
|
@ -2020,6 +2127,9 @@ scan_tail:
|
|||
goto return_error;
|
||||
}
|
||||
|
||||
if (dev->of_node)
|
||||
mtd_device_register(mtd, NULL, 0);
|
||||
else
|
||||
mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, mtd);
|
||||
|
@ -2051,11 +2161,17 @@ static int omap_nand_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id omap_nand_ids[] = {
|
||||
{ .compatible = "ti,omap2-nand", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver omap_nand_driver = {
|
||||
.probe = omap_nand_probe,
|
||||
.remove = omap_nand_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(omap_nand_ids),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -7,161 +7,53 @@
|
|||
* option) any later version.
|
||||
*/
|
||||
|
||||
/* Maximum Number of Chip Selects */
|
||||
#define GPMC_CS_NUM 8
|
||||
#include <linux/platform_data/gpmc-omap.h>
|
||||
|
||||
#define GPMC_CONFIG_WP 0x00000005
|
||||
|
||||
#define GPMC_IRQ_FIFOEVENTENABLE 0x01
|
||||
#define GPMC_IRQ_COUNT_EVENT 0x02
|
||||
/* IRQ numbers in GPMC IRQ domain for legacy boot use */
|
||||
#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
|
||||
* (which is in picoseconds), while the register values are in gpmc_fck cycles.
|
||||
/**
|
||||
* gpmc_nand_ops - Interface between NAND and GPMC
|
||||
* @nand_write_buffer_empty: get the NAND write buffer empty status.
|
||||
*/
|
||||
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;
|
||||
struct gpmc_nand_ops {
|
||||
bool (*nand_writebuffer_empty)(void);
|
||||
};
|
||||
|
||||
/* 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 */
|
||||
struct gpmc_nand_regs;
|
||||
|
||||
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 */
|
||||
#if IS_ENABLED(CONFIG_OMAP_GPMC)
|
||||
struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
|
||||
int cs);
|
||||
#else
|
||||
static inline gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
|
||||
int cs)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_OMAP_GPMC */
|
||||
|
||||
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 {
|
||||
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 */
|
||||
};
|
||||
/* 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,
|
||||
struct gpmc_settings *gpmc_s,
|
||||
struct gpmc_device_timings *dev_t);
|
||||
|
||||
struct gpmc_nand_regs;
|
||||
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 unsigned int gpmc_ticks_to_ns(unsigned int ticks);
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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 {
|
|||
};
|
||||
|
||||
struct gpmc_nand_regs {
|
||||
void __iomem *gpmc_status;
|
||||
void __iomem *gpmc_nand_command;
|
||||
void __iomem *gpmc_nand_address;
|
||||
void __iomem *gpmc_nand_data;
|
||||
|
@ -64,21 +63,24 @@ struct gpmc_nand_regs {
|
|||
void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER];
|
||||
void __iomem *gpmc_bch_result5[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 {
|
||||
int cs;
|
||||
struct mtd_partition *parts;
|
||||
int nr_parts;
|
||||
bool dev_ready;
|
||||
bool flash_bbt;
|
||||
enum nand_io xfer_type;
|
||||
int devsize;
|
||||
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;
|
||||
|
||||
/* deprecated */
|
||||
struct gpmc_nand_regs reg;
|
||||
struct device_node *of_node;
|
||||
bool dev_ready;
|
||||
};
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue