diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-24 11:00:20 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-24 11:00:20 -0700 |
commit | 8bc4d5f394a3facbad6af2f18940f1db3b1a0844 (patch) | |
tree | 14838a236e87126d4b84d22b9049b9a6f0be878f /drivers/memory | |
parent | 29567292c0b5b2fb484125c280a2175141fe2205 (diff) | |
parent | e5366a266a8cd4cd6b0fe66876462cca2e1c6a89 (diff) | |
download | linux-8bc4d5f394a3facbad6af2f18940f1db3b1a0844.tar.bz2 |
Merge tag 'for-linus-20160523' of git://git.infradead.org/linux-mtd
Pull MTD updates from Brian Norris:
"First cycle with Boris as NAND maintainer! Many (most) bullets stolen
from him.
Generic:
- Migrated NAND LED trigger to be a generic MTD trigger
NAND:
- Introduction of the "ECC algorithm" concept, to avoid overloading
the ECC mode field too much more
- Replaced the nand_ecclayout infrastructure with something a little
more flexible (finally!) and future proof
- Rework of the OMAP GPMC and NAND drivers; the TI folks pulled some
of this into their own tree as well
- Prepare the sunxi NAND driver to receive DMA support
- Handle bitflips in erased pages on GPMI revisions that do not
support this in hardware.
SPI NOR:
- Start using the spi_flash_read() API for SPI drivers that support
it (i.e., SPI drivers with special memory-mapped flash modes)
And other small scattered improvments"
* tag 'for-linus-20160523' of git://git.infradead.org/linux-mtd: (155 commits)
mtd: spi-nor: support GigaDevice gd25lq64c
mtd: nand_bch: fix spelling of "probably"
mtd: brcmnand: respect ECC algorithm set by NAND subsystem
gpmi-nand: Handle ECC Errors in erased pages
Documentation: devicetree: deprecate "soft_bch" nand-ecc-mode value
mtd: nand: add support for "nand-ecc-algo" DT property
mtd: mtd: drop NAND_ECC_SOFT_BCH enum value
mtd: drop support for NAND_ECC_SOFT_BCH as "soft_bch" mapping
mtd: nand: read ECC algorithm from the new field
mtd: nand: fsmc: validate ECC setup by checking algorithm directly
mtd: nand: set ECC algorithm to Hamming on fallback
staging: mt29f_spinand: set ECC algorithm explicitly
CRIS v32: nand: set ECC algorithm explicitly
mtd: nand: atmel: set ECC algorithm explicitly
mtd: nand: davinci: set ECC algorithm explicitly
mtd: nand: bf5xx: set ECC algorithm explicitly
mtd: nand: omap2: Fix high memory dma prefetch transfer
mtd: nand: omap2: Start dma request before enabling prefetch
mtd: nandsim: add __init attribute
mtd: nand: move of_get_nand_xxx() helpers into nand_base.c
...
Diffstat (limited to 'drivers/memory')
-rw-r--r-- | drivers/memory/Kconfig | 1 | ||||
-rw-r--r-- | drivers/memory/fsl_ifc.c | 36 | ||||
-rw-r--r-- | drivers/memory/omap-gpmc.c | 657 |
3 files changed, 437 insertions, 257 deletions
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index c61a284133e0..81ddb17575a9 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -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 diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c index 2a691da8c1c7..904b4af5f142 100644 --- a/drivers/memory/fsl_ifc.c +++ b/drivers/memory/fsl_ifc.c @@ -59,11 +59,11 @@ int fsl_ifc_find(phys_addr_t addr_base) { int i = 0; - if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs) return -ENODEV; for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { - u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); + u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr); if (cspr & CSPR_V && (cspr & CSPR_BA) == convert_ifc_address(addr_base)) return i; @@ -75,7 +75,7 @@ EXPORT_SYMBOL(fsl_ifc_find); static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) { - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc = ctrl->gregs; /* * Clear all the common status and event registers @@ -104,7 +104,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev) irq_dispose_mapping(ctrl->nand_irq); irq_dispose_mapping(ctrl->irq); - iounmap(ctrl->regs); + iounmap(ctrl->gregs); dev_set_drvdata(&dev->dev, NULL); kfree(ctrl); @@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(nand_irq_lock); static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) { - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; unsigned long flags; u32 stat; @@ -157,7 +157,7 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) { struct fsl_ifc_ctrl *ctrl = data; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc = ctrl->gregs; u32 err_axiid, err_srcid, status, cs_err, err_addr; irqreturn_t ret = IRQ_NONE; @@ -215,6 +215,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) { int ret = 0; int version, banks; + void __iomem *addr; dev_info(&dev->dev, "Freescale Integrated Flash Controller\n"); @@ -225,22 +226,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev); /* IOMAP the entire IFC region */ - fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); - if (!fsl_ifc_ctrl_dev->regs) { + fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0); + if (!fsl_ifc_ctrl_dev->gregs) { dev_err(&dev->dev, "failed to get memory region\n"); ret = -ENODEV; goto err; } - version = ifc_in32(&fsl_ifc_ctrl_dev->regs->ifc_rev) & - FSL_IFC_VERSION_MASK; - banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; - dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", - version >> 24, (version >> 16) & 0xf, banks); - - fsl_ifc_ctrl_dev->version = version; - fsl_ifc_ctrl_dev->banks = banks; - if (of_property_read_bool(dev->dev.of_node, "little-endian")) { fsl_ifc_ctrl_dev->little_endian = true; dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n"); @@ -249,8 +241,9 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n"); } - version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) & + version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) & FSL_IFC_VERSION_MASK; + banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", version >> 24, (version >> 16) & 0xf, banks); @@ -258,6 +251,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) fsl_ifc_ctrl_dev->version = version; fsl_ifc_ctrl_dev->banks = banks; + addr = fsl_ifc_ctrl_dev->gregs; + if (version >= FSL_IFC_VERSION_2_0_0) + addr += PGOFFSET_64K; + else + addr += PGOFFSET_4K; + fsl_ifc_ctrl_dev->rregs = addr; + /* get the Controller level irq */ fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); if (fsl_ifc_ctrl_dev->irq == 0) { diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 21825ddce4a3..af4884ba6b7c 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -21,15 +21,15 @@ #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> -#include <linux/of_mtd.h> #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 +81,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 +94,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 +135,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 +183,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 +204,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 +231,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 +247,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 +1058,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 +1080,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 +1114,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; +} + +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; + } - for (i = 0; i < GPMC_NR_IRQ; i++) - if (gpmc_client_irq[i].bitmask & irq_config) - return gpmc_client_irq[i].irq; + /* 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; - for (i = 0; i < GPMC_NR_IRQ; i++) - if (irq == gpmc_client_irq[i].irq) { - regval = gpmc_read_reg(GPMC_IRQENABLE); - if (endis) - regval |= gpmc_client_irq[i].bitmask; - else - regval &= ~gpmc_client_irq[i].bitmask; - gpmc_write_reg(GPMC_IRQENABLE, regval); - break; - } + /* 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 |= BIT(hwirq); + else + regval &= ~BIT(hwirq); + gpmc_write_reg(GPMC_IRQENABLE, regval); 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 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; - 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; - - gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE; - gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT; - - 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); + return 0; +} + +static const struct irq_domain_ops gpmc_irq_domain_ops = { + .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; + + 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 +1316,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; + + 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, NULL); + free_irq(gpmc->irq, gpmc); - 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); - } + for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) + irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq)); - irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ); + irq_domain_remove(gpmc_irq_domain); + gpmc_irq_domain = NULL; return 0; } @@ -1242,12 +1375,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 +1924,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 +1979,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 +2041,80 @@ 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 (ret < 0) - goto err; + if (of_node_cmp(child->name, "nand") == 0) { + /* Warn about older DT blobs with no compatible property */ + if (!of_property_read_bool(child, "compatible")) { + dev_warn(&pdev->dev, + "Incompatible NAND node: missing compatible"); + ret = -EINVAL; + goto err; + } + } + + if (of_device_is_compatible(child, "ti,omap2-nand")) { + /* NAND specific setup */ + val = 8; + of_property_read_u32(child, "nand-bus-width", &val); + 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(); - - 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; + rc = gpmc_gpio_init(gpmc); + if (rc) + goto gpio_init_failed; + + 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) |