diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/card/queue.c | 10 | ||||
-rw-r--r-- | drivers/mmc/card/sdio_uart.c | 93 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 41 | ||||
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 400 |
4 files changed, 445 insertions, 99 deletions
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index c5a7a855f4b1..381fe032caa1 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -154,9 +154,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock if (mq->bounce_buf) { blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY); - blk_queue_max_sectors(mq->queue, bouncesz / 512); - blk_queue_max_phys_segments(mq->queue, bouncesz / 512); - blk_queue_max_hw_segments(mq->queue, bouncesz / 512); + blk_queue_max_hw_sectors(mq->queue, bouncesz / 512); + blk_queue_max_segments(mq->queue, bouncesz / 512); blk_queue_max_segment_size(mq->queue, bouncesz); mq->sg = kmalloc(sizeof(struct scatterlist), @@ -180,10 +179,9 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock if (!mq->bounce_buf) { blk_queue_bounce_limit(mq->queue, limit); - blk_queue_max_sectors(mq->queue, + blk_queue_max_hw_sectors(mq->queue, min(host->max_blk_count, host->max_req_size / 512)); - blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); - blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); + blk_queue_max_segments(mq->queue, host->max_hw_segs); blk_queue_max_segment_size(mq->queue, host->max_seg_size); mq->sg = kmalloc(sizeof(struct scatterlist) * diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index f53755533e7e..3fab78ba8952 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -37,6 +37,7 @@ #include <linux/gfp.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/kfifo.h> #include <linux/mmc/core.h> #include <linux/mmc/card.h> @@ -47,19 +48,9 @@ #define UART_NR 8 /* Number of UARTs this driver can handle */ -#define UART_XMIT_SIZE PAGE_SIZE +#define FIFO_SIZE PAGE_SIZE #define WAKEUP_CHARS 256 -#define circ_empty(circ) ((circ)->head == (circ)->tail) -#define circ_clear(circ) ((circ)->head = (circ)->tail = 0) - -#define circ_chars_pending(circ) \ - (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE)) - -#define circ_chars_free(circ) \ - (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) - - struct uart_icount { __u32 cts; __u32 dsr; @@ -82,7 +73,7 @@ struct sdio_uart_port { struct mutex func_lock; struct task_struct *in_sdio_uart_irq; unsigned int regs_offset; - struct circ_buf xmit; + struct kfifo xmit_fifo; spinlock_t write_lock; struct uart_icount icount; unsigned int uartclk; @@ -105,6 +96,8 @@ static int sdio_uart_add_port(struct sdio_uart_port *port) kref_init(&port->kref); mutex_init(&port->func_lock); spin_lock_init(&port->write_lock); + if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL)) + return -ENOMEM; spin_lock(&sdio_uart_table_lock); for (index = 0; index < UART_NR; index++) { @@ -140,6 +133,7 @@ static void sdio_uart_port_destroy(struct kref *kref) { struct sdio_uart_port *port = container_of(kref, struct sdio_uart_port, kref); + kfifo_free(&port->xmit_fifo); kfree(port); } @@ -456,9 +450,11 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, static void sdio_uart_transmit_chars(struct sdio_uart_port *port) { - struct circ_buf *xmit = &port->xmit; + struct kfifo *xmit = &port->xmit_fifo; int count; struct tty_struct *tty; + u8 iobuf[16]; + int len; if (port->x_char) { sdio_out(port, UART_TX, port->x_char); @@ -469,27 +465,25 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) tty = tty_port_tty_get(&port->port); - if (tty == NULL || circ_empty(xmit) || + if (tty == NULL || !kfifo_len(xmit) || tty->stopped || tty->hw_stopped) { sdio_uart_stop_tx(port); tty_kref_put(tty); return; } - count = 16; - do { - sdio_out(port, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + len = kfifo_out_locked(xmit, iobuf, 16, &port->write_lock); + for (count = 0; count < len; count++) { + sdio_out(port, UART_TX, iobuf[count]); port->icount.tx++; - if (circ_empty(xmit)) - break; - } while (--count > 0); + } - if (circ_chars_pending(xmit) < WAKEUP_CHARS) + len = kfifo_len(xmit); + if (len < WAKEUP_CHARS) { tty_wakeup(tty); - - if (circ_empty(xmit)) - sdio_uart_stop_tx(port); + if (len == 0) + sdio_uart_stop_tx(port); + } tty_kref_put(tty); } @@ -632,7 +626,6 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty) { struct sdio_uart_port *port = container_of(tport, struct sdio_uart_port, port); - unsigned long page; int ret; /* @@ -641,22 +634,17 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty) */ set_bit(TTY_IO_ERROR, &tty->flags); - /* Initialise and allocate the transmit buffer. */ - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - port->xmit.buf = (unsigned char *)page; - circ_clear(&port->xmit); + kfifo_reset(&port->xmit_fifo); ret = sdio_uart_claim_func(port); if (ret) - goto err1; + return ret; ret = sdio_enable_func(port->func); if (ret) - goto err2; + goto err1; ret = sdio_claim_irq(port->func, sdio_uart_irq); if (ret) - goto err3; + goto err2; /* * Clear the FIFO buffers and disable them. @@ -700,12 +688,10 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty) sdio_uart_release_func(port); return 0; -err3: - sdio_disable_func(port->func); err2: - sdio_uart_release_func(port); + sdio_disable_func(port->func); err1: - free_page((unsigned long)port->xmit.buf); + sdio_uart_release_func(port); return ret; } @@ -727,7 +713,7 @@ static void sdio_uart_shutdown(struct tty_port *tport) ret = sdio_uart_claim_func(port); if (ret) - goto skip; + return; sdio_uart_stop_rx(port); @@ -749,10 +735,6 @@ static void sdio_uart_shutdown(struct tty_port *tport) sdio_disable_func(port->func); sdio_uart_release_func(port); - -skip: - /* Free the transmit buffer page. */ - free_page((unsigned long)port->xmit.buf); } /** @@ -822,27 +804,12 @@ static int sdio_uart_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct sdio_uart_port *port = tty->driver_data; - struct circ_buf *circ = &port->xmit; - int c, ret = 0; + int ret; if (!port->func) return -ENODEV; - spin_lock(&port->write_lock); - while (1) { - c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(circ->buf + circ->head, buf, c); - circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); - buf += c; - count -= c; - ret += c; - } - spin_unlock(&port->write_lock); - + ret = kfifo_in_locked(&port->xmit_fifo, buf, count, &port->write_lock); if (!(port->ier & UART_IER_THRI)) { int err = sdio_uart_claim_func(port); if (!err) { @@ -859,13 +826,13 @@ static int sdio_uart_write(struct tty_struct *tty, const unsigned char *buf, static int sdio_uart_write_room(struct tty_struct *tty) { struct sdio_uart_port *port = tty->driver_data; - return port ? circ_chars_free(&port->xmit) : 0; + return FIFO_SIZE - kfifo_len(&port->xmit_fifo); } static int sdio_uart_chars_in_buffer(struct tty_struct *tty) { struct sdio_uart_port *port = tty->driver_data; - return port ? circ_chars_pending(&port->xmit) : 0; + return kfifo_len(&port->xmit_fifo); } static void sdio_uart_send_xchar(struct tty_struct *tty, char ch) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 90d168ad03b6..84c103a7ee13 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2,6 +2,7 @@ * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver * * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * Copyright (C) 2010 ST-Ericsson AB. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -34,9 +35,6 @@ #define DRIVER_NAME "mmci-pl18x" -#define DBG(host,fmt,args...) \ - pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args) - static unsigned int fmax = 515633; /* @@ -105,8 +103,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) void __iomem *base; int blksz_bits; - DBG(host, "blksz %04x blks %04x flags %08x\n", - data->blksz, data->blocks, data->flags); + dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n", + data->blksz, data->blocks, data->flags); host->data = data; host->size = data->blksz; @@ -155,7 +153,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) { void __iomem *base = host->base; - DBG(host, "op %02x arg %08x flags %08x\n", + dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n", cmd->opcode, cmd->arg, cmd->flags); if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { @@ -184,8 +182,20 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, { if (status & MCI_DATABLOCKEND) { host->data_xfered += data->blksz; +#ifdef CONFIG_ARCH_U300 + /* + * On the U300 some signal or other is + * badly routed so that a data write does + * not properly terminate with a MCI_DATAEND + * status flag. This quirk will make writes + * work again. + */ + if (data->flags & MMC_DATA_WRITE) + status |= MCI_DATAEND; +#endif } if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { + dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status); if (status & MCI_DATACRCFAIL) data->error = -EILSEQ; else if (status & MCI_DATATIMEOUT) @@ -307,7 +317,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) status = readl(base + MMCISTATUS); - DBG(host, "irq1 %08x\n", status); + dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); do { unsigned long flags; @@ -401,7 +411,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) status &= readl(host->base + MMCIMASK0); writel(status, host->base + MMCICLEAR); - DBG(host, "irq0 %08x\n", status); + dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); data = host->data; if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| @@ -428,8 +438,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq != NULL); if (mrq->data && !is_power_of_2(mrq->data->blksz)) { - printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n", - mmc_hostname(mmc), mrq->data->blksz); + dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n", + mrq->data->blksz); mrq->cmd->error = -EINVAL; mmc_request_done(mmc, mrq); return; @@ -582,8 +592,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) host->hw_designer = amba_manf(dev); host->hw_revision = amba_rev(dev); - DBG(host, "designer ID = 0x%02x\n", host->hw_designer); - DBG(host, "revision = 0x%01x\n", host->hw_revision); + dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer); + dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision); host->clk = clk_get(&dev->dev, NULL); if (IS_ERR(host->clk)) { @@ -608,7 +618,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) if (ret < 0) goto clk_disable; host->mclk = clk_get_rate(host->clk); - DBG(host, "eventual mclk rate: %u Hz\n", host->mclk); + dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n", + host->mclk); } host->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!host->base) { @@ -619,6 +630,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc->ops = &mmci_ops; mmc->f_min = (host->mclk + 511) / 512; mmc->f_max = min(host->mclk, fmax); + dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); + #ifdef CONFIG_REGULATOR /* If we're using the regulator framework, try to fetch a regulator */ host->vcc = regulator_get(&dev->dev, "vmmc"); @@ -712,7 +725,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc_add_host(mmc); - printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n", + dev_info(&dev->dev, "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n", mmc_hostname(mmc), amba_rev(dev), amba_config(dev), (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4b2322518909..83f0affadcae 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -30,6 +30,8 @@ #include <linux/mmc/core.h> #include <linux/io.h> #include <linux/semaphore.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> #include <plat/dma.h> #include <mach/hardware.h> #include <plat/board.h> @@ -146,6 +148,15 @@ struct omap_hsmmc_host { struct clk *fclk; struct clk *iclk; struct clk *dbclk; + /* + * vcc == configured supply + * vcc_aux == optional + * - MMC1, supply for DAT4..DAT7 + * - MMC2/MMC2, external level shifter voltage supply, for + * chip (SDIO, eMMC, etc) or transceiver (MMC2 only) + */ + struct regulator *vcc; + struct regulator *vcc_aux; struct semaphore sem; struct work_struct mmc_carddetect_work; void __iomem *base; @@ -171,10 +182,337 @@ struct omap_hsmmc_host { int vdd; int protect_card; int reqs_blocked; + int use_reg; struct omap_mmc_platform_data *pdata; }; +static int omap_hsmmc_card_detect(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + /* NOTE: assumes card detect signal is active-low */ + return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); +} + +static int omap_hsmmc_get_wp(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + /* NOTE: assumes write protect signal is active-high */ + return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); +} + +static int omap_hsmmc_get_cover_state(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + /* NOTE: assumes card detect signal is active-low */ + return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); +} + +#ifdef CONFIG_PM + +static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + disable_irq(mmc->slots[0].card_detect_irq); + return 0; +} + +static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + enable_irq(mmc->slots[0].card_detect_irq); + return 0; +} + +#else + +#define omap_hsmmc_suspend_cdirq NULL +#define omap_hsmmc_resume_cdirq NULL + +#endif + +#ifdef CONFIG_REGULATOR + +static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + struct omap_hsmmc_host *host = + platform_get_drvdata(to_platform_device(dev)); + int ret; + + if (mmc_slot(host).before_set_reg) + mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); + + if (power_on) + ret = mmc_regulator_set_ocr(host->vcc, vdd); + else + ret = mmc_regulator_set_ocr(host->vcc, 0); + + if (mmc_slot(host).after_set_reg) + mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); + + return ret; +} + +static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + struct omap_hsmmc_host *host = + platform_get_drvdata(to_platform_device(dev)); + int ret = 0; + + /* + * If we don't see a Vcc regulator, assume it's a fixed + * voltage always-on regulator. + */ + if (!host->vcc) + return 0; + + if (mmc_slot(host).before_set_reg) + mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); + + /* + * Assume Vcc regulator is used only to power the card ... OMAP + * VDDS is used to power the pins, optionally with a transceiver to + * support cards using voltages other than VDDS (1.8V nominal). When a + * transceiver is used, DAT3..7 are muxed as transceiver control pins. + * + * In some cases this regulator won't support enable/disable; + * e.g. it's a fixed rail for a WLAN chip. + * + * In other cases vcc_aux switches interface power. Example, for + * eMMC cards it represents VccQ. Sometimes transceivers or SDIO + * chips/cards need an interface voltage rail too. + */ + if (power_on) { + ret = mmc_regulator_set_ocr(host->vcc, vdd); + /* Enable interface voltage rail, if needed */ + if (ret == 0 && host->vcc_aux) { + ret = regulator_enable(host->vcc_aux); + if (ret < 0) + ret = mmc_regulator_set_ocr(host->vcc, 0); + } + } else { + if (host->vcc_aux) + ret = regulator_disable(host->vcc_aux); + if (ret == 0) + ret = mmc_regulator_set_ocr(host->vcc, 0); + } + + if (mmc_slot(host).after_set_reg) + mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); + + return ret; +} + +static int omap_hsmmc_1_set_sleep(struct device *dev, int slot, int sleep, + int vdd, int cardsleep) +{ + struct omap_hsmmc_host *host = + platform_get_drvdata(to_platform_device(dev)); + int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; + + return regulator_set_mode(host->vcc, mode); +} + +static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep, + int vdd, int cardsleep) +{ + struct omap_hsmmc_host *host = + platform_get_drvdata(to_platform_device(dev)); + int err, mode; + + /* + * If we don't see a Vcc regulator, assume it's a fixed + * voltage always-on regulator. + */ + if (!host->vcc) + return 0; + + mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; + + if (!host->vcc_aux) + return regulator_set_mode(host->vcc, mode); + + if (cardsleep) { + /* VCC can be turned off if card is asleep */ + if (sleep) + err = mmc_regulator_set_ocr(host->vcc, 0); + else + err = mmc_regulator_set_ocr(host->vcc, vdd); + } else + err = regulator_set_mode(host->vcc, mode); + if (err) + return err; + + if (!mmc_slot(host).vcc_aux_disable_is_sleep) + return regulator_set_mode(host->vcc_aux, mode); + + if (sleep) + return regulator_disable(host->vcc_aux); + else + return regulator_enable(host->vcc_aux); +} + +static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) +{ + struct regulator *reg; + int ret = 0; + + switch (host->id) { + case OMAP_MMC1_DEVID: + /* On-chip level shifting via PBIAS0/PBIAS1 */ + mmc_slot(host).set_power = omap_hsmmc_1_set_power; + mmc_slot(host).set_sleep = omap_hsmmc_1_set_sleep; + break; + case OMAP_MMC2_DEVID: + case OMAP_MMC3_DEVID: + /* Off-chip level shifting, or none */ + mmc_slot(host).set_power = omap_hsmmc_23_set_power; + mmc_slot(host).set_sleep = omap_hsmmc_23_set_sleep; + break; + default: + pr_err("MMC%d configuration not supported!\n", host->id); + return -EINVAL; + } + + reg = regulator_get(host->dev, "vmmc"); + if (IS_ERR(reg)) { + dev_dbg(host->dev, "vmmc regulator missing\n"); + /* + * HACK: until fixed.c regulator is usable, + * we don't require a main regulator + * for MMC2 or MMC3 + */ + if (host->id == OMAP_MMC1_DEVID) { + ret = PTR_ERR(reg); + goto err; + } + } else { + host->vcc = reg; + mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg); + + /* Allow an aux regulator */ + reg = regulator_get(host->dev, "vmmc_aux"); + host->vcc_aux = IS_ERR(reg) ? NULL : reg; + + /* + * UGLY HACK: workaround regulator framework bugs. + * When the bootloader leaves a supply active, it's + * initialized with zero usecount ... and we can't + * disable it without first enabling it. Until the + * framework is fixed, we need a workaround like this + * (which is safe for MMC, but not in general). + */ + if (regulator_is_enabled(host->vcc) > 0) { + regulator_enable(host->vcc); + regulator_disable(host->vcc); + } + if (host->vcc_aux) { + if (regulator_is_enabled(reg) > 0) { + regulator_enable(reg); + regulator_disable(reg); + } + } + } + + return 0; + +err: + mmc_slot(host).set_power = NULL; + mmc_slot(host).set_sleep = NULL; + return ret; +} + +static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) +{ + regulator_put(host->vcc); + regulator_put(host->vcc_aux); + mmc_slot(host).set_power = NULL; + mmc_slot(host).set_sleep = NULL; +} + +static inline int omap_hsmmc_have_reg(void) +{ + return 1; +} + +#else + +static inline int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) +{ + return -EINVAL; +} + +static inline void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) +{ +} + +static inline int omap_hsmmc_have_reg(void) +{ + return 0; +} + +#endif + +static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata) +{ + int ret; + + if (gpio_is_valid(pdata->slots[0].switch_pin)) { + pdata->suspend = omap_hsmmc_suspend_cdirq; + pdata->resume = omap_hsmmc_resume_cdirq; + if (pdata->slots[0].cover) + pdata->slots[0].get_cover_state = + omap_hsmmc_get_cover_state; + else + pdata->slots[0].card_detect = omap_hsmmc_card_detect; + pdata->slots[0].card_detect_irq = + gpio_to_irq(pdata->slots[0].switch_pin); + ret = gpio_request(pdata->slots[0].switch_pin, "mmc_cd"); + if (ret) + return ret; + ret = gpio_direction_input(pdata->slots[0].switch_pin); + if (ret) + goto err_free_sp; + } else + pdata->slots[0].switch_pin = -EINVAL; + + if (gpio_is_valid(pdata->slots[0].gpio_wp)) { + pdata->slots[0].get_ro = omap_hsmmc_get_wp; + ret = gpio_request(pdata->slots[0].gpio_wp, "mmc_wp"); + if (ret) + goto err_free_cd; + ret = gpio_direction_input(pdata->slots[0].gpio_wp); + if (ret) + goto err_free_wp; + } else + pdata->slots[0].gpio_wp = -EINVAL; + + return 0; + +err_free_wp: + gpio_free(pdata->slots[0].gpio_wp); +err_free_cd: + if (gpio_is_valid(pdata->slots[0].switch_pin)) +err_free_sp: + gpio_free(pdata->slots[0].switch_pin); + return ret; +} + +static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata) +{ + if (gpio_is_valid(pdata->slots[0].gpio_wp)) + gpio_free(pdata->slots[0].gpio_wp); + if (gpio_is_valid(pdata->slots[0].switch_pin)) + gpio_free(pdata->slots[0].switch_pin); +} + /* * Stop clock to the card */ @@ -835,7 +1173,7 @@ static void omap_hsmmc_detect(struct work_struct *work) sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); if (slot->card_detect) - carddetect = slot->card_detect(slot->card_detect_irq); + carddetect = slot->card_detect(host->dev, host->slot_id); else { omap_hsmmc_protect_card(host); carddetect = -ENOSYS; @@ -1242,7 +1580,7 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc) if (!mmc_slot(host).card_detect) return -ENOSYS; - return mmc_slot(host).card_detect(mmc_slot(host).card_detect_irq); + return mmc_slot(host).card_detect(host->dev, host->slot_id); } static int omap_hsmmc_get_ro(struct mmc_host *mmc) @@ -1311,7 +1649,7 @@ static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host) if (host->power_mode == MMC_POWER_OFF) return 0; - return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT); + return OMAP_MMC_SLEEP_TIMEOUT; } /* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */ @@ -1347,11 +1685,14 @@ static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host) dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n", host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); + if (mmc_slot(host).no_off) + return 0; + if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || mmc_slot(host).card_detect || (mmc_slot(host).get_cover_state && mmc_slot(host).get_cover_state(host->dev, host->slot_id))) - return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT); + return OMAP_MMC_OFF_TIMEOUT; return 0; } @@ -1362,6 +1703,9 @@ static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host) if (!mmc_try_claim_host(host->mmc)) return 0; + if (mmc_slot(host).no_off) + return 0; + if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) || mmc_slot(host).card_detect || (mmc_slot(host).get_cover_state && @@ -1616,7 +1960,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) struct mmc_host *mmc; struct omap_hsmmc_host *host = NULL; struct resource *res; - int ret = 0, irq; + int ret, irq; if (pdata == NULL) { dev_err(&pdev->dev, "Platform Data is missing\n"); @@ -1638,10 +1982,14 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) if (res == NULL) return -EBUSY; + ret = omap_hsmmc_gpio_init(pdata); + if (ret) + goto err; + mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; - goto err; + goto err_alloc; } host = mmc_priv(mmc); @@ -1656,7 +2004,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) host->slot_id = 0; host->mapbase = res->start; host->base = ioremap(host->mapbase, SZ_4K); - host->power_mode = -1; + host->power_mode = MMC_POWER_OFF; platform_set_drvdata(pdev, host); INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect); @@ -1666,6 +2014,13 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) else mmc->ops = &omap_hsmmc_ops; + /* + * If regulator_disable can only put vcc_aux to sleep then there is + * no off state. + */ + if (mmc_slot(host).vcc_aux_disable_is_sleep) + mmc_slot(host).no_off = 1; + mmc->f_min = 400000; mmc->f_max = 52000000; @@ -1781,7 +2136,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) goto err_irq; } - /* initialize power supplies, gpios, etc */ if (pdata->init != NULL) { if (pdata->init(&pdev->dev) != 0) { dev_dbg(mmc_dev(host->mmc), @@ -1789,6 +2143,14 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) goto err_irq_cd_init; } } + + if (omap_hsmmc_have_reg() && !mmc_slot(host).set_power) { + ret = omap_hsmmc_reg_get(host); + if (ret) + goto err_reg; + host->use_reg = 1; + } + mmc->ocr_avail = mmc_slot(host).ocr_mask; /* Request IRQ for card detect */ @@ -1823,19 +2185,22 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) ret = device_create_file(&mmc->class_dev, &dev_attr_cover_switch); if (ret < 0) - goto err_cover_switch; + goto err_slot_name; } omap_hsmmc_debugfs(mmc); return 0; -err_cover_switch: - device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); err_slot_name: mmc_remove_host(mmc); -err_irq_cd: free_irq(mmc_slot(host).card_detect_irq, host); +err_irq_cd: + if (host->use_reg) + omap_hsmmc_reg_put(host); +err_reg: + if (host->pdata->cleanup) + host->pdata->cleanup(&pdev->dev); err_irq_cd_init: free_irq(host->irq, host); err_irq: @@ -1847,14 +2212,14 @@ err_irq: clk_disable(host->dbclk); clk_put(host->dbclk); } - err1: iounmap(host->base); + platform_set_drvdata(pdev, NULL); + mmc_free_host(mmc); +err_alloc: + omap_hsmmc_gpio_free(pdata); err: - dev_dbg(mmc_dev(host->mmc), "Probe Failed\n"); release_mem_region(res->start, res->end - res->start + 1); - if (host) - mmc_free_host(mmc); return ret; } @@ -1866,6 +2231,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev) if (host) { mmc_host_enable(host->mmc); mmc_remove_host(host->mmc); + if (host->use_reg) + omap_hsmmc_reg_put(host); if (host->pdata->cleanup) host->pdata->cleanup(&pdev->dev); free_irq(host->irq, host); @@ -1884,6 +2251,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) mmc_free_host(host->mmc); iounmap(host->base); + omap_hsmmc_gpio_free(pdev->dev.platform_data); } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |