diff options
Diffstat (limited to 'drivers/mfd/asic3.c')
| -rw-r--r-- | drivers/mfd/asic3.c | 223 |
1 files changed, 163 insertions, 60 deletions
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index c45e6305b26..9f6294f2a07 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -20,6 +20,7 @@ #include <linux/delay.h> #include <linux/irq.h> #include <linux/gpio.h> +#include <linux/export.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -88,19 +89,19 @@ struct asic3 { static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset); -static inline void asic3_write_register(struct asic3 *asic, - unsigned int reg, u32 value) +void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value) { iowrite16(value, asic->mapping + (reg >> asic->bus_shift)); } +EXPORT_SYMBOL_GPL(asic3_write_register); -static inline u32 asic3_read_register(struct asic3 *asic, - unsigned int reg) +u32 asic3_read_register(struct asic3 *asic, unsigned int reg) { return ioread16(asic->mapping + (reg >> asic->bus_shift)); } +EXPORT_SYMBOL_GPL(asic3_read_register); static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set) { @@ -139,13 +140,12 @@ static void asic3_irq_flip_edge(struct asic3 *asic, static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) { + struct asic3 *asic = irq_desc_get_handler_data(desc); + struct irq_data *data = irq_desc_get_irq_data(desc); int iter, i; unsigned long flags; - struct asic3 *asic; - - desc->irq_data.chip->irq_ack(&desc->irq_data); - asic = get_irq_data(irq); + data->chip->irq_ack(data); for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) { u32 status; @@ -188,8 +188,7 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) irqnr = asic->irq_base + (ASIC3_GPIOS_PER_BANK * bank) + i; - desc = irq_to_desc(irqnr); - desc->handle_irq(irqnr, desc); + generic_handle_irq(irqnr); if (asic->irq_bothedge[bank] & bit) asic3_irq_flip_edge(asic, base, bit); @@ -200,11 +199,8 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) /* Handle remaining IRQs in the status register */ for (i = ASIC3_NUM_GPIOS; i < ASIC3_NR_IRQS; i++) { /* They start at bit 4 and go up */ - if (status & (1 << (i - ASIC3_NUM_GPIOS + 4))) { - desc = irq_to_desc(asic->irq_base + i); - desc->handle_irq(asic->irq_base + i, - desc); - } + if (status & (1 << (i - ASIC3_NUM_GPIOS + 4))) + generic_handle_irq(asic->irq_base + i); } } @@ -357,12 +353,28 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) return 0; } +static int asic3_gpio_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct asic3 *asic = irq_data_get_irq_chip_data(data); + u32 bank, index; + u16 bit; + + bank = asic3_irq_to_bank(asic, data->irq); + index = asic3_irq_to_index(asic, data->irq); + bit = 1<<index; + + asic3_set_register(asic, bank + ASIC3_GPIO_SLEEP_MASK, bit, !on); + + return 0; +} + static struct irq_chip asic3_gpio_irq_chip = { .name = "ASIC3-GPIO", .irq_ack = asic3_mask_gpio_irq, .irq_mask = asic3_mask_gpio_irq, .irq_unmask = asic3_unmask_gpio_irq, .irq_set_type = asic3_gpio_irq_type, + .irq_set_wake = asic3_gpio_irq_set_wake, }; static struct irq_chip asic3_irq_chip = { @@ -393,21 +405,21 @@ static int __init asic3_irq_probe(struct platform_device *pdev) for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { if (irq < asic->irq_base + ASIC3_NUM_GPIOS) - set_irq_chip(irq, &asic3_gpio_irq_chip); + irq_set_chip(irq, &asic3_gpio_irq_chip); else - set_irq_chip(irq, &asic3_irq_chip); + irq_set_chip(irq, &asic3_irq_chip); - set_irq_chip_data(irq, asic); - set_irq_handler(irq, handle_level_irq); + irq_set_chip_data(irq, asic); + irq_set_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK), ASIC3_INTMASK_GINTMASK); - set_irq_chained_handler(asic->irq_nr, asic3_irq_demux); - set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING); - set_irq_data(asic->irq_nr, asic); + irq_set_chained_handler(asic->irq_nr, asic3_irq_demux); + irq_set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(asic->irq_nr, asic); return 0; } @@ -421,11 +433,10 @@ static void asic3_irq_remove(struct platform_device *pdev) for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { set_irq_flags(irq, 0); - set_irq_handler(irq, NULL); - set_irq_chip(irq, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } - set_irq_chained_handler(asic->irq_nr, NULL); + irq_set_chained_handler(asic->irq_nr, NULL); } /* GPIOs */ @@ -530,6 +541,13 @@ static void asic3_gpio_set(struct gpio_chip *chip, return; } +static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct asic3 *asic = container_of(chip, struct asic3, gpio); + + return asic->irq_base + offset; +} + static __init int asic3_gpio_probe(struct platform_device *pdev, u16 *gpio_config, int num) { @@ -590,7 +608,7 @@ static int asic3_gpio_remove(struct platform_device *pdev) return gpiochip_remove(&asic->gpio); } -static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) +static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) { unsigned long flags; u32 cdex; @@ -602,8 +620,6 @@ static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); } spin_unlock_irqrestore(&asic->lock, flags); - - return 0; } static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) @@ -625,6 +641,7 @@ static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) /* MFD cells (SPI, PWM, LED, DS1WM, MMC) */ static struct ds1wm_driver_data ds1wm_pdata = { .active_high = 1, + .reset_recover_delay = 1, }; static struct resource ds1wm_resources[] = { @@ -678,11 +695,12 @@ static int ds1wm_disable(struct platform_device *pdev) return 0; } -static struct mfd_cell asic3_cell_ds1wm = { +static const struct mfd_cell asic3_cell_ds1wm = { .name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, - .driver_data = &ds1wm_pdata, + .platform_data = &ds1wm_pdata, + .pdata_size = sizeof(ds1wm_pdata), .num_resources = ARRAY_SIZE(ds1wm_resources), .resources = ds1wm_resources, }; @@ -779,16 +797,86 @@ static int asic3_mmc_disable(struct platform_device *pdev) return 0; } -static struct mfd_cell asic3_cell_mmc = { +static const struct mfd_cell asic3_cell_mmc = { .name = "tmio-mmc", .enable = asic3_mmc_enable, .disable = asic3_mmc_disable, - .driver_data = &asic3_mmc_data, + .suspend = asic3_mmc_disable, + .resume = asic3_mmc_enable, + .platform_data = &asic3_mmc_data, + .pdata_size = sizeof(asic3_mmc_data), .num_resources = ARRAY_SIZE(asic3_mmc_resources), .resources = asic3_mmc_resources, }; +static const int clock_ledn[ASIC3_NUM_LEDS] = { + [0] = ASIC3_CLOCK_LED0, + [1] = ASIC3_CLOCK_LED1, + [2] = ASIC3_CLOCK_LED2, +}; + +static int asic3_leds_enable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + asic3_clk_enable(asic, &asic->clocks[clock_ledn[cell->id]]); + + return 0; +} + +static int asic3_leds_disable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); + + return 0; +} + +static int asic3_leds_suspend(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0) + msleep(1); + + asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); + + return 0; +} + +static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = { + [0] = { + .name = "leds-asic3", + .id = 0, + .enable = asic3_leds_enable, + .disable = asic3_leds_disable, + .suspend = asic3_leds_suspend, + .resume = asic3_leds_enable, + }, + [1] = { + .name = "leds-asic3", + .id = 1, + .enable = asic3_leds_enable, + .disable = asic3_leds_disable, + .suspend = asic3_leds_suspend, + .resume = asic3_leds_enable, + }, + [2] = { + .name = "leds-asic3", + .id = 2, + .enable = asic3_leds_enable, + .disable = asic3_leds_disable, + .suspend = asic3_leds_suspend, + .resume = asic3_leds_enable, + }, +}; + static int __init asic3_mfd_probe(struct platform_device *pdev, + struct asic3_platform_data *pdata, struct resource *mem) { struct asic3 *asic = platform_get_drvdata(pdev); @@ -810,12 +898,10 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, ds1wm_resources[0].start >>= asic->bus_shift; ds1wm_resources[0].end >>= asic->bus_shift; - asic3_cell_ds1wm.platform_data = &asic3_cell_ds1wm; - asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm); - /* MMC */ asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) + - mem_sdio->start, 0x400 >> asic->bus_shift); + mem_sdio->start, + ASIC3_SD_CONFIG_SIZE >> asic->bus_shift); if (!asic->tmio_cnf) { ret = -ENOMEM; dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG\n"); @@ -824,17 +910,32 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, asic3_mmc_resources[0].start >>= asic->bus_shift; asic3_mmc_resources[0].end >>= asic->bus_shift; - asic3_cell_mmc.platform_data = &asic3_cell_mmc; - asic3_cell_mmc.data_size = sizeof(asic3_cell_mmc); - - ret = mfd_add_devices(&pdev->dev, pdev->id, - &asic3_cell_ds1wm, 1, mem, asic->irq_base); - if (ret < 0) - goto out; + if (pdata->clock_rate) { + ds1wm_pdata.clock_rate = pdata->clock_rate; + ret = mfd_add_devices(&pdev->dev, pdev->id, + &asic3_cell_ds1wm, 1, mem, asic->irq_base, NULL); + if (ret < 0) + goto out; + } - if (mem_sdio && (irq >= 0)) + if (mem_sdio && (irq >= 0)) { ret = mfd_add_devices(&pdev->dev, pdev->id, - &asic3_cell_mmc, 1, mem_sdio, irq); + &asic3_cell_mmc, 1, mem_sdio, irq, NULL); + if (ret < 0) + goto out; + } + + ret = 0; + if (pdata->leds) { + int i; + + for (i = 0; i < ASIC3_NUM_LEDS; ++i) { + asic3_cell_leds[i].platform_data = &pdata->leds[i]; + asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]); + } + ret = mfd_add_devices(&pdev->dev, 0, + asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0, NULL); + } out: return ret; @@ -851,13 +952,14 @@ static void asic3_mfd_remove(struct platform_device *pdev) /* Core */ static int __init asic3_probe(struct platform_device *pdev) { - struct asic3_platform_data *pdata = pdev->dev.platform_data; + struct asic3_platform_data *pdata = dev_get_platdata(&pdev->dev); struct asic3 *asic; struct resource *mem; unsigned long clksel; int ret = 0; - asic = kzalloc(sizeof(struct asic3), GFP_KERNEL); + asic = devm_kzalloc(&pdev->dev, + sizeof(struct asic3), GFP_KERNEL); if (asic == NULL) { printk(KERN_ERR "kzalloc failed\n"); return -ENOMEM; @@ -869,16 +971,14 @@ static int __init asic3_probe(struct platform_device *pdev) mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { - ret = -ENOMEM; dev_err(asic->dev, "no MEM resource\n"); - goto out_free; + return -ENOMEM; } asic->mapping = ioremap(mem->start, resource_size(mem)); if (!asic->mapping) { - ret = -ENOMEM; dev_err(asic->dev, "Couldn't ioremap\n"); - goto out_free; + return -ENOMEM; } asic->irq_base = pdata->irq_base; @@ -895,12 +995,14 @@ static int __init asic3_probe(struct platform_device *pdev) goto out_unmap; } + asic->gpio.label = "asic3"; asic->gpio.base = pdata->gpio_base; asic->gpio.ngpio = ASIC3_NUM_GPIOS; asic->gpio.get = asic3_gpio_get; asic->gpio.set = asic3_gpio_set; asic->gpio.direction_input = asic3_gpio_direction_input; asic->gpio.direction_output = asic3_gpio_direction_output; + asic->gpio.to_irq = asic3_gpio_to_irq; ret = asic3_gpio_probe(pdev, pdata->gpio_config, @@ -915,7 +1017,10 @@ static int __init asic3_probe(struct platform_device *pdev) */ memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init)); - asic3_mfd_probe(pdev, mem); + asic3_mfd_probe(pdev, pdata, mem); + + asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), + (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 1); dev_info(asic->dev, "ASIC3 Core driver\n"); @@ -927,17 +1032,17 @@ static int __init asic3_probe(struct platform_device *pdev) out_unmap: iounmap(asic->mapping); - out_free: - kfree(asic); - return ret; } -static int __devexit asic3_remove(struct platform_device *pdev) +static int asic3_remove(struct platform_device *pdev) { int ret; struct asic3 *asic = platform_get_drvdata(pdev); + asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), + (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 0); + asic3_mfd_remove(pdev); ret = asic3_gpio_remove(pdev); @@ -949,8 +1054,6 @@ static int __devexit asic3_remove(struct platform_device *pdev) iounmap(asic->mapping); - kfree(asic); - return 0; } @@ -962,7 +1065,7 @@ static struct platform_driver asic3_device_driver = { .driver = { .name = "asic3", }, - .remove = __devexit_p(asic3_remove), + .remove = asic3_remove, .shutdown = asic3_shutdown, }; |
