diff options
Diffstat (limited to 'drivers/memory')
-rw-r--r-- | drivers/memory/omap-gpmc.c | 106 |
1 files changed, 88 insertions, 18 deletions
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 784a64f31a8b..ea9c89747950 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -189,9 +189,7 @@ #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, @@ -239,6 +237,7 @@ struct gpmc_device { int irq; struct irq_chip irq_chip; struct gpio_chip gpio_chip; + int nirqs; }; static struct irq_domain *gpmc_irq_domain; @@ -1155,7 +1154,8 @@ int gpmc_get_client_irq(unsigned irq_config) return 0; } - if (irq_config >= GPMC_NR_IRQ) + /* 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); @@ -1165,6 +1165,10 @@ static int gpmc_irq_endis(unsigned long hwirq, bool endis) { u32 regval; + /* 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); @@ -1185,9 +1189,64 @@ static void gpmc_irq_enable(struct irq_data *p) 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 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) +{ + u32 regval; + + /* NAND IRQs polarity is not configurable */ + if (hwirq < GPMC_NR_NAND_IRQS) + return; + + /* WAITPIN starts at BIT 8 */ + hwirq += 8 - GPMC_NR_NAND_IRQS; + + regval = gpmc_read_reg(GPMC_CONFIG); + if (rising_edge) + regval &= ~BIT(hwirq); + else + regval |= BIT(hwirq); + + gpmc_write_reg(GPMC_CONFIG, regval); +} + +static void gpmc_irq_ack(struct irq_data *d) +{ + unsigned int hwirq = d->hwirq; + + /* skip reserved bits */ + if (hwirq >= GPMC_NR_NAND_IRQS) + hwirq += 8 - GPMC_NR_NAND_IRQS; + + /* Setting bit to 1 clears (or Acks) the interrupt */ + gpmc_write_reg(GPMC_IRQSTATUS, BIT(hwirq)); +} + +static int gpmc_irq_set_type(struct irq_data *d, unsigned int trigger) +{ + /* can't set type for NAND IRQs */ + if (d->hwirq < GPMC_NR_NAND_IRQS) + return -EINVAL; + + /* We can support either rising or falling edge at a time */ + if (trigger == IRQ_TYPE_EDGE_FALLING) + gpmc_irq_edge_config(d->hwirq, false); + else if (trigger == IRQ_TYPE_EDGE_RISING) + gpmc_irq_edge_config(d->hwirq, true); + else + return -EINVAL; -static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; } + return 0; +} static int gpmc_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) @@ -1195,8 +1254,14 @@ static int gpmc_irq_map(struct irq_domain *d, unsigned int virq, struct gpmc_device *gpmc = d->host_data; irq_set_chip_data(virq, gpmc); - irq_set_chip_and_handler(virq, &gpmc->irq_chip, handle_simple_irq); - irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN); + 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); + } return 0; } @@ -1209,16 +1274,21 @@ static const struct irq_domain_ops gpmc_irq_domain_ops = { static irqreturn_t gpmc_handle_irq(int irq, void *data) { int hwirq, virq; - u32 regval; + 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_NR_IRQ; hwirq++) { - if (regval & BIT(hwirq)) { + 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, @@ -1248,16 +1318,15 @@ static int gpmc_setup_irq(struct gpmc_device *gpmc) gpmc_write_reg(GPMC_IRQSTATUS, regval); 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->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_NR_IRQ, + gpmc->nirqs, &gpmc_irq_domain_ops, gpmc); if (!gpmc_irq_domain) { @@ -1282,7 +1351,7 @@ static int gpmc_free_irq(struct gpmc_device *gpmc) free_irq(gpmc->irq, gpmc); - for (hwirq = 0; hwirq < GPMC_NR_IRQ; hwirq++) + for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq)); irq_domain_remove(gpmc_irq_domain); @@ -2296,6 +2365,7 @@ static int gpmc_probe(struct platform_device *pdev) 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"); |