From ff3112f5adcc9dc33c107c826f82017db8f889f5 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 8 Mar 2008 23:43:19 +0100 Subject: mmc: set controller name early Reorganise code so that mmc_hostname() works directly after allocation. That way host drivers can use that name for resource allocations and messages during probing. Signed-off-by: Pierre Ossman --- drivers/mmc/core/host.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index c65d203a846d..1d795c5379b5 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -2,7 +2,7 @@ * linux/drivers/mmc/core/host.c * * Copyright (C) 2003 Russell King, All Rights Reserved. - * Copyright (C) 2007 Pierre Ossman + * Copyright (C) 2007-2008 Pierre Ossman * * 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 @@ -57,12 +57,25 @@ static DEFINE_SPINLOCK(mmc_host_lock); */ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) { + int err; struct mmc_host *host; + if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) + return NULL; + host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); if (!host) return NULL; + spin_lock(&mmc_host_lock); + err = idr_get_new(&mmc_host_idr, host, &host->index); + spin_unlock(&mmc_host_lock); + if (err) + goto free; + + snprintf(host->class_dev.bus_id, BUS_ID_SIZE, + "mmc%d", host->index); + host->parent = dev; host->class_dev.parent = dev; host->class_dev.class = &mmc_host_class; @@ -85,6 +98,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->max_blk_count = PAGE_CACHE_SIZE / 512; return host; + +free: + kfree(host); + return NULL; } EXPORT_SYMBOL(mmc_alloc_host); @@ -104,18 +121,6 @@ int mmc_add_host(struct mmc_host *host) WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && !host->ops->enable_sdio_irq); - if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) - return -ENOMEM; - - spin_lock(&mmc_host_lock); - err = idr_get_new(&mmc_host_idr, host, &host->index); - spin_unlock(&mmc_host_lock); - if (err) - return err; - - snprintf(host->class_dev.bus_id, BUS_ID_SIZE, - "mmc%d", host->index); - led_trigger_register_simple(host->class_dev.bus_id, &host->led); err = device_add(&host->class_dev); @@ -144,10 +149,6 @@ void mmc_remove_host(struct mmc_host *host) device_del(&host->class_dev); led_trigger_unregister_simple(host->led); - - spin_lock(&mmc_host_lock); - idr_remove(&mmc_host_idr, host->index); - spin_unlock(&mmc_host_lock); } EXPORT_SYMBOL(mmc_remove_host); @@ -160,6 +161,10 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { + spin_lock(&mmc_host_lock); + idr_remove(&mmc_host_idr, host->index); + spin_unlock(&mmc_host_lock); + put_device(&host->class_dev); } -- cgit v1.2.2 From b69c9058907642f8e1b32076906755c6623ea060 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 8 Mar 2008 23:44:25 +0100 Subject: sdhci: remove custom controller name Remove the use of the sdhci specific device name and use the mmc controller name instead. Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 44 ++++++++++++++++---------------------------- drivers/mmc/host/sdhci.h | 5 +---- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4b673aa2dc3c..71e020d6718d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver * - * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,10 +30,6 @@ static unsigned int debug_quirks = 0; -/* For multi controllers in one platform case */ -static u16 chip_index = 0; -static spinlock_t index_lock; - /* * Different quirks to handle when the hardware deviates from a strict * interpretation of the SDHCI specification. @@ -1105,7 +1101,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) goto out; } - DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); + DBG("*** %s got interrupt: 0x%08x\n", + mmc_hostname(host->mmc), intmask); if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), @@ -1235,7 +1232,7 @@ static int sdhci_resume (struct pci_dev *pdev) if (chip->hosts[i]->flags & SDHCI_USE_DMA) pci_set_master(pdev); ret = request_irq(chip->hosts[i]->irq, sdhci_irq, - IRQF_SHARED, chip->hosts[i]->slot_descr, + IRQF_SHARED, mmc_hostname(chip->hosts[i]->mmc), chip->hosts[i]); if (ret) return ret; @@ -1324,9 +1321,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq); - snprintf(host->slot_descr, 20, "sdhc%d:slot%d", chip->index, slot); - - ret = pci_request_region(pdev, host->bar, host->slot_descr); + ret = pci_request_region(pdev, host->bar, mmc_hostname(mmc)); if (ret) goto free; @@ -1343,7 +1338,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; if (version > 1) { printk(KERN_ERR "%s: Unknown controller version (%d). " - "You may experience problems.\n", host->slot_descr, + "You may experience problems.\n", mmc_hostname(mmc), version); } @@ -1366,13 +1361,13 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) (host->flags & SDHCI_USE_DMA)) { printk(KERN_WARNING "%s: Will use DMA " "mode even though HW doesn't fully " - "claim to support it.\n", host->slot_descr); + "claim to support it.\n", mmc_hostname(mmc)); } if (host->flags & SDHCI_USE_DMA) { if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { printk(KERN_WARNING "%s: No suitable DMA available. " - "Falling back to PIO.\n", host->slot_descr); + "Falling back to PIO.\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_DMA; } } @@ -1386,7 +1381,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; if (host->max_clk == 0) { printk(KERN_ERR "%s: Hardware doesn't specify base clock " - "frequency.\n", host->slot_descr); + "frequency.\n", mmc_hostname(mmc)); ret = -ENODEV; goto unmap; } @@ -1396,7 +1391,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; if (host->timeout_clk == 0) { printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " - "frequency.\n", host->slot_descr); + "frequency.\n", mmc_hostname(mmc)); ret = -ENODEV; goto unmap; } @@ -1424,7 +1419,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) if (mmc->ocr_avail == 0) { printk(KERN_ERR "%s: Hardware doesn't report any " - "support voltages.\n", host->slot_descr); + "support voltages.\n", mmc_hostname(mmc)); ret = -ENODEV; goto unmap; } @@ -1458,8 +1453,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) */ mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; if (mmc->max_blk_size >= 3) { - printk(KERN_WARNING "%s: Invalid maximum block size, assuming 512\n", - host->slot_descr); + printk(KERN_WARNING "%s: Invalid maximum block size, " + "assuming 512 bytes\n", mmc_hostname(mmc)); mmc->max_blk_size = 512; } else mmc->max_blk_size = 512 << mmc->max_blk_size; @@ -1480,7 +1475,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - host->slot_descr, host); + mmc_hostname(mmc), host); if (ret) goto untasklet; @@ -1494,8 +1489,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc_add_host(mmc); - printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc), - host->addr, host->irq, + printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", + mmc_hostname(mmc), host->addr, host->irq, (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); return 0; @@ -1589,11 +1584,6 @@ static int __devinit sdhci_probe(struct pci_dev *pdev, chip->num_slots = slots; pci_set_drvdata(pdev, chip); - /* Add for multi controller case */ - spin_lock(&index_lock); - chip->index = chip_index++; - spin_unlock(&index_lock); - for (i = 0;i < slots;i++) { ret = sdhci_probe_slot(pdev, i); if (ret) { @@ -1654,8 +1644,6 @@ static int __init sdhci_drv_init(void) ": Secure Digital Host Controller Interface driver\n"); printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - spin_lock_init(&index_lock); - return pci_register_driver(&sdhci_driver); } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index d5a38f1b755a..3288e209ba44 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver * - * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -190,8 +190,6 @@ struct sdhci_host { int offset; /* Offset into current sg */ int remain; /* Bytes left in current */ - char slot_descr[20]; /* Name for reservations */ - int irq; /* Device IRQ */ int bar; /* PCI BAR index */ unsigned long addr; /* Bus address */ @@ -208,7 +206,6 @@ struct sdhci_chip { unsigned long quirks; - int index; /* Index for chip0, chip1 ...*/ int num_slots; /* Slots on controller */ struct sdhci_host *hosts[0]; /* Pointers to hosts */ }; -- cgit v1.2.2 From 2f730fec83be76f1b3b8f0066b3447f55c50d7a0 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 17 Mar 2008 10:29:38 +0100 Subject: sdhci: allow led to be controlled freely Hook up the controller LED to the LED subsystem, allowing more flexible control than simply indicating an ongoing request. Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci.h | 4 ++++ 2 files changed, 48 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 71e020d6718d..6250eb5f98a8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -19,6 +19,8 @@ #include #include +#include + #include #include "sdhci.h" @@ -252,6 +254,24 @@ static void sdhci_deactivate_led(struct sdhci_host *host) writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); } +#ifdef CONFIG_LEDS_CLASS +static void sdhci_led_control(struct led_classdev *led, + enum led_brightness brightness) +{ + struct sdhci_host *host = container_of(led, struct sdhci_host, led); + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (brightness == LED_OFF) + sdhci_deactivate_led(host); + else + sdhci_activate_led(host); + + spin_unlock_irqrestore(&host->lock, flags); +} +#endif + /*****************************************************************************\ * * * Core functions * @@ -769,7 +789,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq != NULL); +#ifndef CONFIG_LEDS_CLASS sdhci_activate_led(host); +#endif host->mrq = mrq; @@ -961,7 +983,9 @@ static void sdhci_tasklet_finish(unsigned long param) host->cmd = NULL; host->data = NULL; +#ifndef CONFIG_LEDS_CLASS sdhci_deactivate_led(host); +#endif mmiowb(); spin_unlock_irqrestore(&host->lock, flags); @@ -1485,6 +1509,17 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) sdhci_dumpregs(host); #endif +#ifdef CONFIG_LEDS_CLASS + host->led.name = mmc_hostname(mmc); + host->led.brightness = LED_OFF; + host->led.default_trigger = mmc_hostname(mmc); + host->led.brightness_set = sdhci_led_control; + + ret = led_classdev_register(&pdev->dev, &host->led); + if (ret) + goto reset; +#endif + mmiowb(); mmc_add_host(mmc); @@ -1495,6 +1530,11 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) return 0; +#ifdef CONFIG_LEDS_CLASS +reset: + sdhci_reset(host, SDHCI_RESET_ALL); + free_irq(host->irq, host); +#endif untasklet: tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); @@ -1522,6 +1562,10 @@ static void sdhci_remove_slot(struct pci_dev *pdev, int slot) mmc_remove_host(mmc); +#ifdef CONFIG_LEDS_CLASS + led_classdev_unregister(&host->led); +#endif + sdhci_reset(host, SDHCI_RESET_ALL); free_irq(host->irq, host); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 3288e209ba44..7fb02e177a3d 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -168,6 +168,10 @@ struct sdhci_host { struct sdhci_chip *chip; struct mmc_host *mmc; /* MMC structure */ +#ifdef CONFIG_LEDS_CLASS + struct led_classdev led; /* LED control */ +#endif + spinlock_t lock; /* Mutex */ int flags; /* Host attributes */ -- cgit v1.2.2 From 6fee65cfde519ae811c3cde47c622271168ca449 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 28 Mar 2008 14:34:47 -0700 Subject: mmc: use shorter, equivalent set_current_state() Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Pierre Ossman --- drivers/mmc/core/sdio_irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 3bd3021f5e80..c292e124107a 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -128,12 +128,12 @@ static int sdio_irq_thread(void *_host) } } - set_task_state(current, TASK_INTERRUPTIBLE); + set_current_state(TASK_INTERRUPTIBLE); if (host->caps & MMC_CAP_SDIO_IRQ) host->ops->enable_sdio_irq(host, 1); if (!kthread_should_stop()) schedule_timeout(period); - set_task_state(current, TASK_RUNNING); + set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); if (host->caps & MMC_CAP_SDIO_IRQ) -- cgit v1.2.2 From 5ec21b1ccf554593d25fba0f411c03e1d6fd88c5 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Aguiar Date: Wed, 26 Mar 2008 16:08:41 -0400 Subject: MMC: OMAP: Remove some opcodes from host driver This patch removes some opcodes from host driver so there's no need on putting '#include ', that should not be needed in host drivers. Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 90c358b57d1c..da70467915d6 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -495,15 +495,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (status & OMAP_MMC_STAT_CMD_TOUT) { /* Timeouts are routine with some commands */ if (host->cmd) { - if (host->cmd->opcode != MMC_ALL_SEND_CID && - host->cmd->opcode != - MMC_SEND_OP_COND && - host->cmd->opcode != - MMC_APP_CMD && - !mmc_omap_cover_is_open(host)) + if (!mmc_omap_cover_is_open(host)) dev_err(mmc_dev(host->mmc), - "command timeout, CMD %d\n", - host->cmd->opcode); + "command timeout, CMD %d\n", + host->cmd->opcode); host->cmd->error = -ETIMEDOUT; end_command = 1; } -- cgit v1.2.2 From ec2f362a9126237e79a21d5c3cd225f5a07f8e89 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 26 Mar 2008 16:08:45 -0400 Subject: MMC: OMAP: Remove extra divisor increase As noted by Kyungmin Park, the divisor calculation has an unnecessary increase. Signed-off-by: Tony Lindgren Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index da70467915d6..472e977b7404 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -931,7 +931,6 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) if (dsor > 250) dsor = 250; - dsor++; if (ios->bus_width == MMC_BUS_WIDTH_4) dsor |= 1 << 15; -- cgit v1.2.2 From d365abe27db12df25660fe254e09876cea8a9ad8 Mon Sep 17 00:00:00 2001 From: Francisco Alecrim Date: Wed, 26 Mar 2008 16:08:48 -0400 Subject: MMC: OMAP: Fix the BYTEBLOCK capability removal According with commit 255d01af9a990fd5166f04ed0cc0b30b7b67e81e from Linux-OMAP tree, the BYTEBLOCK capability was removed by Pierre Ossman. MMC_CAP_BYTEBLOCK is not defined causing the compile error: drivers/mmc/host/omap.c: In function `mmc_omap_probe': drivers/mmc/host/omap.c:1077: error: `MMC_CAP_BYTEBLOCK' undeclared (first use in this function) drivers/mmc/host/omap.c:1077: error: (Each undeclared identifier is reported only once drivers/mmc/host/omap.c:1077: error: for each function it appears in.) Signed-off-by: Francisco Alecrim Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 472e977b7404..adf679a157eb 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1070,7 +1070,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev) mmc->f_min = 400000; mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + mmc->caps = MMC_CAP_MULTIWRITE; if (minfo->wire4) mmc->caps |= MMC_CAP_4_BIT_DATA; -- cgit v1.2.2 From 4bc9e35556bf4444014ba65b80abb2fb9f70899a Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 26 Mar 2008 16:08:53 -0400 Subject: MMC: OMAP: Remove cover switch handling to allow adding multislot support This patch removes the MMC cover switch handling temporarily to make following multislot patches cleaner. MMC cover switch handling will be added back in later patches after adding basic multislot support. Signed-off-by: Tony Lindgren Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 152 +----------------------------------------------- 1 file changed, 1 insertion(+), 151 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index adf679a157eb..7d17c899c394 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -95,8 +95,6 @@ * when the cover switch is open */ #define OMAP_MMC_SWITCH_POLL_DELAY 500 -static int mmc_omap_enable_poll = 1; - struct mmc_omap_host { int initialized; int suspended; @@ -133,62 +131,11 @@ struct mmc_omap_host { short power_pin; short wp_pin; - int switch_pin; struct work_struct switch_work; struct timer_list switch_timer; int switch_last_state; }; -static inline int -mmc_omap_cover_is_open(struct mmc_omap_host *host) -{ - if (host->switch_pin < 0) - return 0; - return omap_get_gpio_datain(host->switch_pin); -} - -static ssize_t -mmc_omap_show_cover_switch(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mmc_omap_host *host = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : - "closed"); -} - -static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); - -static ssize_t -mmc_omap_show_enable_poll(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); -} - -static ssize_t -mmc_omap_store_enable_poll(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t size) -{ - int enable_poll; - - if (sscanf(buf, "%10d", &enable_poll) != 1) - return -EINVAL; - - if (enable_poll != mmc_omap_enable_poll) { - struct mmc_omap_host *host = dev_get_drvdata(dev); - - mmc_omap_enable_poll = enable_poll; - if (enable_poll && host->switch_pin >= 0) - schedule_work(&host->switch_work); - } - return size; -} - -static DEVICE_ATTR(enable_poll, 0664, - mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); - static void mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) { @@ -495,8 +442,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (status & OMAP_MMC_STAT_CMD_TOUT) { /* Timeouts are routine with some commands */ if (host->cmd) { - if (!mmc_omap_cover_is_open(host)) - dev_err(mmc_dev(host->mmc), + dev_err(mmc_dev(host->mmc), "command timeout, CMD %d\n", host->cmd->opcode); host->cmd->error = -ETIMEDOUT; @@ -544,54 +490,6 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id) -{ - struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; - - schedule_work(&host->switch_work); - - return IRQ_HANDLED; -} - -static void mmc_omap_switch_timer(unsigned long arg) -{ - struct mmc_omap_host *host = (struct mmc_omap_host *) arg; - - schedule_work(&host->switch_work); -} - -static void mmc_omap_switch_handler(struct work_struct *work) -{ - struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work); - struct mmc_card *card; - static int complained = 0; - int cards = 0, cover_open; - - if (host->switch_pin == -1) - return; - cover_open = mmc_omap_cover_is_open(host); - if (cover_open != host->switch_last_state) { - kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); - host->switch_last_state = cover_open; - } - mmc_detect_change(host->mmc, 0); - list_for_each_entry(card, &host->mmc->cards, node) { - if (mmc_card_present(card)) - cards++; - } - if (mmc_omap_cover_is_open(host)) { - if (!complained) { - dev_info(mmc_dev(host->mmc), "cover is open\n"); - complained = 1; - } - if (mmc_omap_enable_poll) - mod_timer(&host->switch_timer, jiffies + - msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY)); - } else { - complained = 0; - } -} - /* Prepare to transfer the next segment of a scatterlist */ static void mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) @@ -1057,7 +955,6 @@ static int __init mmc_omap_probe(struct platform_device *pdev) * the card detect sensing. */ host->power_pin = minfo->power_pin; - host->switch_pin = minfo->switch_pin; host->wp_pin = minfo->wp_pin; host->use_dma = 1; host->dma_ch = -1; @@ -1102,48 +999,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev) host->dev = &pdev->dev; platform_set_drvdata(pdev, host); - if (host->switch_pin >= 0) { - INIT_WORK(&host->switch_work, mmc_omap_switch_handler); - init_timer(&host->switch_timer); - host->switch_timer.function = mmc_omap_switch_timer; - host->switch_timer.data = (unsigned long) host; - if (omap_request_gpio(host->switch_pin) != 0) { - dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n"); - host->switch_pin = -1; - goto no_switch; - } - - omap_set_gpio_direction(host->switch_pin, 1); - ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin), - mmc_omap_switch_irq, IRQF_TRIGGER_RISING, DRIVER_NAME, host); - if (ret) { - dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n"); - omap_free_gpio(host->switch_pin); - host->switch_pin = -1; - goto no_switch; - } - ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); - if (ret == 0) { - ret = device_create_file(&pdev->dev, &dev_attr_enable_poll); - if (ret != 0) - device_remove_file(&pdev->dev, &dev_attr_cover_switch); - } - if (ret) { - dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n"); - free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); - omap_free_gpio(host->switch_pin); - host->switch_pin = -1; - goto no_switch; - } - if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host)) - schedule_work(&host->switch_work); - } - mmc_add_host(mmc); return 0; -no_switch: /* FIXME: Free other resources too. */ if (host) { if (host->iclk && !IS_ERR(host->iclk)) @@ -1182,15 +1041,6 @@ static int mmc_omap_remove(struct platform_device *pdev) if (host->power_pin >= 0) omap_free_gpio(host->power_pin); - if (host->switch_pin >= 0) { - device_remove_file(&pdev->dev, &dev_attr_enable_poll); - device_remove_file(&pdev->dev, &dev_attr_cover_switch); - free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); - omap_free_gpio(host->switch_pin); - host->switch_pin = -1; - del_timer_sync(&host->switch_timer); - flush_scheduled_work(); - } if (host->iclk && !IS_ERR(host->iclk)) clk_put(host->iclk); if (host->fclk && !IS_ERR(host->fclk)) -- cgit v1.2.2 From abfbe5f7854a083ca324282bf7e39f10bc438313 Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 26 Mar 2008 16:08:57 -0400 Subject: MMC: OMAP: Introduce new multislot structure and change driver to use it Introduce new MMC multislot structure and change driver to use it. Note that MMC clocking is now enabled in mmc_omap_select_slot() and disabled in mmc_omap_release_slot(). Signed-off-by: Juha Yrjola Signed-off-by: Jarkko Lavinen Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 402 +++++++++++++++++++++++++++++----------- include/asm-arm/arch-omap/mmc.h | 2 + 2 files changed, 294 insertions(+), 110 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 7d17c899c394..59eac7211842 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -95,6 +96,22 @@ * when the cover switch is open */ #define OMAP_MMC_SWITCH_POLL_DELAY 500 +struct mmc_omap_host; + +struct mmc_omap_slot { + int id; + unsigned int vdd; + u16 saved_con; + u16 bus_mode; + unsigned int fclk_freq; + unsigned powered:1; + + struct mmc_request *mrq; + struct mmc_omap_host *host; + struct mmc_host *mmc; + struct omap_mmc_slot_data *pdata; +}; + struct mmc_omap_host { int initialized; int suspended; @@ -129,13 +146,98 @@ struct mmc_omap_host { unsigned dma_len; short power_pin; - short wp_pin; - struct work_struct switch_work; - struct timer_list switch_timer; - int switch_last_state; + struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS]; + struct mmc_omap_slot *current_slot; + spinlock_t slot_lock; + wait_queue_head_t slot_wq; + int nr_slots; + + struct omap_mmc_platform_data *pdata; }; +static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) +{ + struct mmc_omap_host *host = slot->host; + unsigned long flags; + + if (claimed) + goto no_claim; + spin_lock_irqsave(&host->slot_lock, flags); + while (host->mmc != NULL) { + spin_unlock_irqrestore(&host->slot_lock, flags); + wait_event(host->slot_wq, host->mmc == NULL); + spin_lock_irqsave(&host->slot_lock, flags); + } + host->mmc = slot->mmc; + spin_unlock_irqrestore(&host->slot_lock, flags); +no_claim: + clk_enable(host->fclk); + if (host->current_slot != slot) { + if (host->pdata->switch_slot != NULL) + host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id); + host->current_slot = slot; + } + + /* Doing the dummy read here seems to work around some bug + * at least in OMAP24xx silicon where the command would not + * start after writing the CMD register. Sigh. */ + OMAP_MMC_READ(host, CON); + + OMAP_MMC_WRITE(host, CON, slot->saved_con); +} + +static void mmc_omap_start_request(struct mmc_omap_host *host, + struct mmc_request *req); + +static void mmc_omap_release_slot(struct mmc_omap_slot *slot) +{ + struct mmc_omap_host *host = slot->host; + unsigned long flags; + int i; + + BUG_ON(slot == NULL || host->mmc == NULL); + clk_disable(host->fclk); + + spin_lock_irqsave(&host->slot_lock, flags); + /* Check for any pending requests */ + for (i = 0; i < host->nr_slots; i++) { + struct mmc_omap_slot *new_slot; + struct mmc_request *rq; + + if (host->slots[i] == NULL || host->slots[i]->mrq == NULL) + continue; + + new_slot = host->slots[i]; + /* The current slot should not have a request in queue */ + BUG_ON(new_slot == host->current_slot); + + host->mmc = new_slot->mmc; + spin_unlock_irqrestore(&host->slot_lock, flags); + mmc_omap_select_slot(new_slot, 1); + rq = new_slot->mrq; + new_slot->mrq = NULL; + mmc_omap_start_request(host, rq); + return; + } + + host->mmc = NULL; + wake_up(&host->slot_wq); + spin_unlock_irqrestore(&host->slot_lock, flags); +} + +static ssize_t +mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct mmc_omap_slot *slot = mmc_priv(mmc); + + return sprintf(buf, "%s\n", slot->pdata->name); +} + +static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); + static void mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) { @@ -180,7 +282,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); - if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + if (host->current_slot->bus_mode == MMC_BUSMODE_OPENDRAIN) cmdreg |= 1 << 6; if (cmd->flags & MMC_RSP_BUSY) @@ -189,8 +291,6 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) if (host->data && !(host->data->flags & MMC_DATA_WRITE)) cmdreg |= 1 << 15; - clk_enable(host->fclk); - OMAP_MMC_WRITE(host, CTO, 200); OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); @@ -442,6 +542,8 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (status & OMAP_MMC_STAT_CMD_TOUT) { /* Timeouts are routine with some commands */ if (host->cmd) { + struct mmc_omap_slot *slot = + host->current_slot; dev_err(mmc_dev(host->mmc), "command timeout, CMD %d\n", host->cmd->opcode); @@ -747,11 +849,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) } } -static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) +static void mmc_omap_start_request(struct mmc_omap_host *host, + struct mmc_request *req) { - struct mmc_omap_host *host = mmc_priv(mmc); - - WARN_ON(host->mrq != NULL); + BUG_ON(host->mrq != NULL); host->mrq = req; @@ -760,6 +861,26 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) mmc_omap_start_command(host, req->cmd); if (host->dma_in_use) omap_start_dma(host->dma_ch); + BUG_ON(irqs_disabled()); +} + +static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct mmc_omap_slot *slot = mmc_priv(mmc); + struct mmc_omap_host *host = slot->host; + unsigned long flags; + + spin_lock_irqsave(&host->slot_lock, flags); + if (host->mmc != NULL) { + BUG_ON(slot->mrq != NULL); + slot->mrq = req; + spin_unlock_irqrestore(&host->slot_lock, flags); + return; + } else + host->mmc = mmc; + spin_unlock_irqrestore(&host->slot_lock, flags); + mmc_omap_select_slot(slot, 1); + mmc_omap_start_request(host, req); } static void innovator_fpga_socket_power(int on) @@ -813,7 +934,8 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on) static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) { - struct mmc_omap_host *host = mmc_priv(mmc); + struct mmc_omap_slot *slot = mmc_priv(mmc); + struct mmc_omap_host *host = slot->host; int func_clk_rate = clk_get_rate(host->fclk); int dsor; @@ -830,6 +952,8 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) if (dsor > 250) dsor = 250; + slot->fclk_freq = func_clk_rate / dsor; + if (ios->bus_width == MMC_BUS_WIDTH_4) dsor |= 1 << 15; @@ -838,9 +962,9 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { - struct mmc_omap_host *host = mmc_priv(mmc); - int dsor; - int i; + struct mmc_omap_slot *slot = mmc_priv(mmc); + struct mmc_omap_host *host = slot->host; + int i, dsor; dsor = mmc_omap_calc_divisor(mmc, ios); host->bus_mode = ios->bus_mode; @@ -878,32 +1002,101 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk_disable(host->fclk); } -static int mmc_omap_get_ro(struct mmc_host *mmc) -{ - struct mmc_omap_host *host = mmc_priv(mmc); - - return host->wp_pin && omap_get_gpio_datain(host->wp_pin); -} - static const struct mmc_host_ops mmc_omap_ops = { .request = mmc_omap_request, .set_ios = mmc_omap_set_ios, - .get_ro = mmc_omap_get_ro, }; -static int __init mmc_omap_probe(struct platform_device *pdev) +static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) { - struct omap_mmc_conf *minfo = pdev->dev.platform_data; + struct mmc_omap_slot *slot = NULL; struct mmc_host *mmc; + int r; + + mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev); + if (mmc == NULL) + return -ENOMEM; + + slot = mmc_priv(mmc); + slot->host = host; + slot->mmc = mmc; + slot->id = id; + slot->pdata = &host->pdata->slots[id]; + + host->slots[id] = slot; + + mmc->caps = MMC_CAP_MULTIWRITE; + if (host->pdata->conf.wire4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + mmc->ops = &mmc_omap_ops; + mmc->f_min = 400000; + + if (cpu_class_is_omap2()) + mmc->f_max = 48000000; + else + mmc->f_max = 24000000; + if (host->pdata->max_freq) + mmc->f_max = min(host->pdata->max_freq, mmc->f_max); + mmc->ocr_avail = slot->pdata->ocr_mask; + + /* Use scatterlist DMA to reduce per-transfer costs. + * NOTE max_seg_size assumption that small blocks aren't + * normally used (except e.g. for reading SD registers). + */ + mmc->max_phys_segs = 32; + mmc->max_hw_segs = 32; + mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ + mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; + + r = mmc_add_host(mmc); + if (r < 0) + goto err_remove_host; + + if (slot->pdata->name != NULL) { + r = device_create_file(&mmc->class_dev, + &dev_attr_slot_name); + if (r < 0) + goto err_remove_host; + } + + return 0; + +err_remove_host: + mmc_remove_host(mmc); + mmc_free_host(mmc); + return r; +} + +static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) +{ + struct mmc_host *mmc = slot->mmc; + + if (slot->pdata->name != NULL) + device_remove_file(&mmc->class_dev, &dev_attr_slot_name); + + mmc_remove_host(mmc); + mmc_free_host(mmc); +} + +static int __init mmc_omap_probe(struct platform_device *pdev) +{ + struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_omap_host *host = NULL; struct resource *res; - int ret = 0; + int i, ret = 0; int irq; - if (minfo == NULL) { + if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENXIO; } + if (pdata->nr_slots == 0) { + dev_err(&pdev->dev, "no slots\n"); + return -ENXIO; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -911,28 +1104,39 @@ static int __init mmc_omap_probe(struct platform_device *pdev) return -ENXIO; res = request_mem_region(res->start, res->end - res->start + 1, - pdev->name); + pdev->name); if (res == NULL) return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); - if (mmc == NULL) { + host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL); + if (host == NULL) { ret = -ENOMEM; goto err_free_mem_region; } - host = mmc_priv(mmc); - host->mmc = mmc; - spin_lock_init(&host->dma_lock); init_timer(&host->dma_timer); + spin_lock_init(&host->slot_lock); + init_waitqueue_head(&host->slot_wq); + host->dma_timer.function = mmc_omap_dma_timer; host->dma_timer.data = (unsigned long) host; + host->pdata = pdata; + host->dev = &pdev->dev; + platform_set_drvdata(pdev, host); + host->id = pdev->id; host->mem_res = res; host->irq = irq; + host->use_dma = 1; + host->dma_ch = -1; + + host->irq = irq; + host->phys_base = host->mem_res->start; + host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); + if (cpu_is_omap24xx()) { host->iclk = clk_get(&pdev->dev, "mmc_ick"); if (IS_ERR(host->iclk)) @@ -950,70 +1154,34 @@ static int __init mmc_omap_probe(struct platform_device *pdev) goto err_free_iclk; } - /* REVISIT: - * Also, use minfo->cover to decide how to manage - * the card detect sensing. - */ - host->power_pin = minfo->power_pin; - host->wp_pin = minfo->wp_pin; - host->use_dma = 1; - host->dma_ch = -1; - - host->irq = irq; - host->phys_base = host->mem_res->start; - host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); - - mmc->ops = &mmc_omap_ops; - mmc->f_min = 400000; - mmc->f_max = 24000000; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_MULTIWRITE; + ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); + if (ret) + goto err_free_fclk; - if (minfo->wire4) - mmc->caps |= MMC_CAP_4_BIT_DATA; + if (pdata->init != NULL) { + ret = pdata->init(&pdev->dev); + if (ret < 0) + goto err_free_irq; + } - /* Use scatterlist DMA to reduce per-transfer costs. - * NOTE max_seg_size assumption that small blocks aren't - * normally used (except e.g. for reading SD registers). - */ - mmc->max_phys_segs = 32; - mmc->max_hw_segs = 32; - mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ - mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ - mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - mmc->max_seg_size = mmc->max_req_size; + host->nr_slots = pdata->nr_slots; + for (i = 0; i < pdata->nr_slots; i++) { + ret = mmc_omap_new_slot(host, i); + if (ret < 0) { + while (--i >= 0) + mmc_omap_remove_slot(host->slots[i]); - if (host->power_pin >= 0) { - if ((ret = omap_request_gpio(host->power_pin)) != 0) { - dev_err(mmc_dev(host->mmc), - "Unable to get GPIO pin for MMC power\n"); - goto err_free_fclk; + goto err_plat_cleanup; } - omap_set_gpio_direction(host->power_pin, 0); } - ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); - if (ret) - goto err_free_power_gpio; - - host->dev = &pdev->dev; - platform_set_drvdata(pdev, host); - - mmc_add_host(mmc); - return 0; - /* FIXME: Free other resources too. */ - if (host) { - if (host->iclk && !IS_ERR(host->iclk)) - clk_put(host->iclk); - if (host->fclk && !IS_ERR(host->fclk)) - clk_put(host->fclk); - mmc_free_host(host->mmc); - } -err_free_power_gpio: - if (host->power_pin >= 0) - omap_free_gpio(host->power_pin); +err_plat_cleanup: + if (pdata->cleanup) + pdata->cleanup(&pdev->dev); +err_free_irq: + free_irq(host->irq, host); err_free_fclk: clk_put(host->fclk); err_free_iclk: @@ -1022,7 +1190,7 @@ err_free_iclk: clk_put(host->iclk); } err_free_mmc_host: - mmc_free_host(host->mmc); + kfree(host); err_free_mem_region: release_mem_region(res->start, res->end - res->start + 1); return ret; @@ -1031,16 +1199,18 @@ err_free_mem_region: static int mmc_omap_remove(struct platform_device *pdev) { struct mmc_omap_host *host = platform_get_drvdata(pdev); + int i; platform_set_drvdata(pdev, NULL); BUG_ON(host == NULL); - mmc_remove_host(host->mmc); - free_irq(host->irq, host); + for (i = 0; i < host->nr_slots; i++) + mmc_omap_remove_slot(host->slots[i]); + + if (host->pdata->cleanup) + host->pdata->cleanup(&pdev->dev); - if (host->power_pin >= 0) - omap_free_gpio(host->power_pin); if (host->iclk && !IS_ERR(host->iclk)) clk_put(host->iclk); if (host->fclk && !IS_ERR(host->fclk)) @@ -1049,7 +1219,7 @@ static int mmc_omap_remove(struct platform_device *pdev) release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); - mmc_free_host(host->mmc); + kfree(host); return 0; } @@ -1057,35 +1227,47 @@ static int mmc_omap_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) { - int ret = 0; + int i, ret = 0; struct mmc_omap_host *host = platform_get_drvdata(pdev); - if (host && host->suspended) + if (host == NULL || host->suspended) return 0; - if (host) { - ret = mmc_suspend_host(host->mmc, mesg); - if (ret == 0) - host->suspended = 1; + for (i = 0; i < host->nr_slots; i++) { + struct mmc_omap_slot *slot; + + slot = host->slots[i]; + ret = mmc_suspend_host(slot->mmc, mesg); + if (ret < 0) { + while (--i >= 0) { + slot = host->slots[i]; + mmc_resume_host(slot->mmc); + } + return ret; + } } - return ret; + host->suspended = 1; + return 0; } static int mmc_omap_resume(struct platform_device *pdev) { - int ret = 0; + int i, ret = 0; struct mmc_omap_host *host = platform_get_drvdata(pdev); - if (host && !host->suspended) + if (host == NULL || !host->suspended) return 0; - if (host) { - ret = mmc_resume_host(host->mmc); - if (ret == 0) - host->suspended = 0; - } + for (i = 0; i < host->nr_slots; i++) { + struct mmc_omap_slot *slot; + slot = host->slots[i]; + ret = mmc_resume_host(slot->mmc); + if (ret < 0) + return ret; - return ret; + host->suspended = 0; + } + return 0; } #else #define mmc_omap_suspend NULL diff --git a/include/asm-arm/arch-omap/mmc.h b/include/asm-arm/arch-omap/mmc.h index b70e37b61242..c9588f49eb52 100644 --- a/include/asm-arm/arch-omap/mmc.h +++ b/include/asm-arm/arch-omap/mmc.h @@ -18,6 +18,8 @@ #define OMAP_MMC_MAX_SLOTS 2 struct omap_mmc_platform_data { + struct omap_mmc_conf conf; + unsigned enabled:1; /* number of slots on board */ unsigned nr_slots:2; -- cgit v1.2.2 From 5a0f3f1f71e0bc4a843673e8e7cf09a32bb07c32 Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 26 Mar 2008 16:09:08 -0400 Subject: MMC: OMAP: Add back cover switch support This patch adds back MMC cover switch support in a way that supports multiple slots. Signed-off-by: Juha Yrjola Signed-off-by: Jarkko Lavinen Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 59eac7211842..712a9608acf7 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -106,6 +106,10 @@ struct mmc_omap_slot { unsigned int fclk_freq; unsigned powered:1; + struct work_struct switch_work; + struct timer_list switch_timer; + unsigned cover_open; + struct mmc_request *mrq; struct mmc_omap_host *host; struct mmc_host *mmc; @@ -226,6 +230,25 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot) spin_unlock_irqrestore(&host->slot_lock, flags); } +static inline +int mmc_omap_cover_is_open(struct mmc_omap_slot *slot) +{ + return slot->pdata->get_cover_state(mmc_dev(slot->mmc), slot->id); +} + +static ssize_t +mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct mmc_omap_slot *slot = mmc_priv(mmc); + + return sprintf(buf, "%s\n", mmc_omap_cover_is_open(slot) ? "open" : + "closed"); +} + +static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); + static ssize_t mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, char *buf) @@ -544,9 +567,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (host->cmd) { struct mmc_omap_slot *slot = host->current_slot; - dev_err(mmc_dev(host->mmc), - "command timeout, CMD %d\n", - host->cmd->opcode); + if (!mmc_omap_cover_is_open(slot)) + dev_err(mmc_dev(host->mmc), + "command timeout, CMD %d\n", + host->cmd->opcode); host->cmd->error = -ETIMEDOUT; end_command = 1; } @@ -592,6 +616,42 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) return IRQ_HANDLED; } +void omap_mmc_notify_cover_event(struct device *dev, int slot, int is_closed) +{ + struct mmc_omap_host *host = dev_get_drvdata(dev); + + BUG_ON(slot >= host->nr_slots); + + /* Other subsystems can call in here before we're initialised. */ + if (host->nr_slots == 0 || !host->slots[slot]) + return; + + schedule_work(&host->slots[slot]->switch_work); +} + +static void mmc_omap_switch_timer(unsigned long arg) +{ + struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg; + + schedule_work(&slot->switch_work); +} + +static void mmc_omap_cover_handler(struct work_struct *work) +{ + struct mmc_omap_slot *slot = container_of(work, struct mmc_omap_slot, + switch_work); + int cover_open; + + cover_open = mmc_omap_cover_is_open(slot); + if (cover_open != slot->cover_open) { + sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch"); + slot->cover_open = cover_open; + dev_info(mmc_dev(slot->mmc), "cover is now %s\n", + cover_open ? "open" : "closed"); + } + mmc_detect_change(slot->mmc, slot->id); +} + /* Prepare to transfer the next segment of a scatterlist */ static void mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) @@ -1062,8 +1122,24 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) goto err_remove_host; } + if (slot->pdata->get_cover_state != NULL) { + r = device_create_file(&mmc->class_dev, + &dev_attr_cover_switch); + if (r < 0) + goto err_remove_slot_name; + + INIT_WORK(&slot->switch_work, mmc_omap_cover_handler); + init_timer(&slot->switch_timer); + slot->switch_timer.function = mmc_omap_switch_timer; + slot->switch_timer.data = (unsigned long) slot; + schedule_work(&slot->switch_work); + } + return 0; +err_remove_slot_name: + if (slot->pdata->name != NULL) + device_remove_file(&mmc->class_dev, &dev_attr_slot_name); err_remove_host: mmc_remove_host(mmc); mmc_free_host(mmc); @@ -1076,6 +1152,11 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) if (slot->pdata->name != NULL) device_remove_file(&mmc->class_dev, &dev_attr_slot_name); + if (slot->pdata->get_cover_state != NULL) + device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); + + del_timer_sync(&slot->switch_timer); + flush_scheduled_work(); mmc_remove_host(mmc); mmc_free_host(mmc); -- cgit v1.2.2 From a914ded23565d8688e2b7e435916630def5bdd19 Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 26 Mar 2008 16:09:12 -0400 Subject: MMC: OMAP: New release dma and abort xfer functions New functions to support MMC multislot: mmc_omap_release_dma() and mmc_omap_abort_xfer(). Signed-off-by: Juha Yrjola Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 79 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 712a9608acf7..90e9d68e7998 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -326,27 +326,33 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) OMAP_MMC_WRITE(host, CMD, cmdreg); } +static void +mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data, + int abort) +{ + enum dma_data_direction dma_data_dir; + + BUG_ON(host->dma_ch < 0); + if (data->error) + omap_stop_dma(host->dma_ch); + /* Release DMA channel lazily */ + mod_timer(&host->dma_timer, jiffies + HZ); + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, + dma_data_dir); +} + static void mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) { - if (host->dma_in_use) { - enum dma_data_direction dma_data_dir; - - BUG_ON(host->dma_ch < 0); - if (data->error) - omap_stop_dma(host->dma_ch); - /* Release DMA channel lazily */ - mod_timer(&host->dma_timer, jiffies + HZ); - if (data->flags & MMC_DATA_WRITE) - dma_data_dir = DMA_TO_DEVICE; - else - dma_data_dir = DMA_FROM_DEVICE; - dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, - dma_data_dir); - } + if (host->dma_in_use) + mmc_omap_release_dma(host, data, data->error); + host->data = NULL; host->sg_len = 0; - clk_disable(host->fclk); /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing * dozens of requests until the card finishes writing data. @@ -354,14 +360,44 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) */ if (!data->stop) { + struct mmc_host *mmc; + host->mrq = NULL; - mmc_request_done(host->mmc, data->mrq); + mmc = host->mmc; + mmc_omap_release_slot(host->current_slot); + mmc_request_done(mmc, data->mrq); return; } mmc_omap_start_command(host, data->stop); } +static void +mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) +{ + int loops; + u16 ie; + + if (host->dma_in_use) + mmc_omap_release_dma(host, data, 1); + + host->data = NULL; + host->sg_len = 0; + + ie = OMAP_MMC_READ(host, IE); + OMAP_MMC_WRITE(host, IE, 0); + OMAP_MMC_WRITE(host, CMD, 1 << 7); + loops = 0; + while (!(OMAP_MMC_READ(host, STAT) & OMAP_MMC_STAT_END_OF_CMD)) { + udelay(1); + loops++; + if (loops == 100000) + break; + } + OMAP_MMC_WRITE(host, STAT, OMAP_MMC_STAT_END_OF_CMD); + OMAP_MMC_WRITE(host, IE, ie); +} + static void mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data) { @@ -439,9 +475,14 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) } if (host->data == NULL || cmd->error) { + struct mmc_host *mmc; + + if (host->data != NULL) + mmc_omap_abort_xfer(host, host->data); host->mrq = NULL; - clk_disable(host->fclk); - mmc_request_done(host->mmc, cmd->mrq); + mmc = host->mmc; + mmc_omap_release_slot(host->current_slot); + mmc_request_done(mmc, cmd->mrq); } } -- cgit v1.2.2 From b8f9f0e90a6c3fb999d00301f45bb2213dbe8c00 Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 26 Mar 2008 16:09:16 -0400 Subject: MMC: OMAP: Fix timeout calculation for MMC multislot support Fix the data timeout calculation for MMC multislot support. Signed-off-by: Juha Yrjola Signed-off-by: Jarkko Lavinen Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 90e9d68e7998..0d3cd3c23f8f 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -861,13 +861,12 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_reques static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) { - int timeout; + unsigned int timeout, cycle_ns; u16 reg; - /* Convert ns to clock cycles by assuming 20MHz frequency - * 1 cycle at 20MHz = 500 ns - */ - timeout = req->data->timeout_clks + req->data->timeout_ns / 500; + cycle_ns = 1000000000 / host->current_slot->fclk_freq; + timeout = req->data->timeout_ns / cycle_ns; + timeout += req->data->timeout_clks; /* Check if we need to use timeout multiplier register */ reg = OMAP_MMC_READ(host, SDIO); -- cgit v1.2.2 From 65b5b6e51bdeab1934a147aec41ecea82d3cc31d Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 26 Mar 2008 16:09:22 -0400 Subject: MMC: OMAP: Power functions modified to MMC multislot support Modifications at power functions to MMC multislot support. This patch also move board-specific code out of MMC OMAP driver. Signed-off-by: Juha Yrjola Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 88 +++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 0d3cd3c23f8f..c80cd58cfa07 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -983,52 +983,27 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) mmc_omap_start_request(host, req); } -static void innovator_fpga_socket_power(int on) +static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on, + int vdd) { -#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) - if (on) { - fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), - OMAP1510_FPGA_POWER); - } else { - fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), - OMAP1510_FPGA_POWER); - } -#endif -} + struct mmc_omap_host *host; -/* - * Turn the socket power on/off. Innovator uses FPGA, most boards - * probably use GPIO. - */ -static void mmc_omap_power(struct mmc_omap_host *host, int on) -{ - if (on) { - if (machine_is_omap_innovator()) - innovator_fpga_socket_power(1); - else if (machine_is_omap_h2()) - tps65010_set_gpio_out_value(GPIO3, HIGH); - else if (machine_is_omap_h3()) - /* GPIO 4 of TPS65010 sends SD_EN signal */ - tps65010_set_gpio_out_value(GPIO4, HIGH); - else if (cpu_is_omap24xx()) { - u16 reg = OMAP_MMC_READ(host, CON); - OMAP_MMC_WRITE(host, CON, reg | (1 << 11)); - } else - if (host->power_pin >= 0) - omap_set_gpio_dataout(host->power_pin, 1); - } else { - if (machine_is_omap_innovator()) - innovator_fpga_socket_power(0); - else if (machine_is_omap_h2()) - tps65010_set_gpio_out_value(GPIO3, LOW); - else if (machine_is_omap_h3()) - tps65010_set_gpio_out_value(GPIO4, LOW); - else if (cpu_is_omap24xx()) { - u16 reg = OMAP_MMC_READ(host, CON); - OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11)); - } else - if (host->power_pin >= 0) - omap_set_gpio_dataout(host->power_pin, 0); + host = slot->host; + + if (slot->pdata->set_power != NULL) + slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on, + vdd); + + if (cpu_is_omap24xx()) { + u16 w; + + if (power_on) { + w = OMAP_MMC_READ(host, CON); + OMAP_MMC_WRITE(host, CON, w | (1 << 11)); + } else { + w = OMAP_MMC_READ(host, CON); + OMAP_MMC_WRITE(host, CON, w & ~(1 << 11)); + } } } @@ -1067,23 +1042,31 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) int i, dsor; dsor = mmc_omap_calc_divisor(mmc, ios); - host->bus_mode = ios->bus_mode; - host->hw_bus_mode = host->bus_mode; + + mmc_omap_select_slot(slot, 0); + + if (ios->vdd != slot->vdd) + slot->vdd = ios->vdd; switch (ios->power_mode) { case MMC_POWER_OFF: - mmc_omap_power(host, 0); + mmc_omap_set_power(slot, 0, ios->vdd); break; case MMC_POWER_UP: /* Cannot touch dsor yet, just power up MMC */ - mmc_omap_power(host, 1); - return; + mmc_omap_set_power(slot, 1, ios->vdd); + goto exit; case MMC_POWER_ON: dsor |= 1 << 11; break; } - clk_enable(host->fclk); + if (slot->bus_mode != ios->bus_mode) { + if (slot->pdata->set_bus_mode != NULL) + slot->pdata->set_bus_mode(mmc_dev(mmc), slot->id, + ios->bus_mode); + slot->bus_mode = ios->bus_mode; + } /* On insanely high arm_per frequencies something sometimes * goes somehow out of sync, and the POW bit is not being set, @@ -1091,6 +1074,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * Writing to the CON register twice seems to do the trick. */ for (i = 0; i < 2; i++) OMAP_MMC_WRITE(host, CON, dsor); + slot->saved_con = dsor; if (ios->power_mode == MMC_POWER_ON) { /* Send clock cycles, poll completion */ OMAP_MMC_WRITE(host, IE, 0); @@ -1099,7 +1083,9 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) while ((OMAP_MMC_READ(host, STAT) & 1) == 0); OMAP_MMC_WRITE(host, STAT, 1); } - clk_disable(host->fclk); + +exit: + mmc_omap_release_slot(slot); } static const struct mmc_host_ops mmc_omap_ops = { -- cgit v1.2.2 From 2a50b8889b707b9c7bcd09c0ae4cbcef7dca4e29 Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 26 Mar 2008 16:09:26 -0400 Subject: MMC: OMAP: General cleanup for MMC multislot support General code cleanup, modifications at some dev_* functions and other hacks at mmc_omap_irq() for MMC multislot support. Signed-off-by: Juha Yrjola Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index c80cd58cfa07..535499f11e7c 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -547,11 +547,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) u16 status; int end_command; int end_transfer; - int transfer_error; + int transfer_error, cmd_error; if (host->cmd == NULL && host->data == NULL) { status = OMAP_MMC_READ(host, STAT); - dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); + dev_info(mmc_dev(host->slots[0]->mmc), + "Spurious IRQ 0x%04x\n", status); if (status != 0) { OMAP_MMC_WRITE(host, STAT, status); OMAP_MMC_WRITE(host, IE, 0); @@ -562,12 +563,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) end_command = 0; end_transfer = 0; transfer_error = 0; + cmd_error = 0; while ((status = OMAP_MMC_READ(host, STAT)) != 0) { + int cmd; + OMAP_MMC_WRITE(host, STAT, status); + if (host->cmd != NULL) + cmd = host->cmd->opcode; + else + cmd = -1; #ifdef CONFIG_MMC_DEBUG dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", - status, host->cmd != NULL ? host->cmd->opcode : -1); + status, cmd); mmc_omap_report_irq(status); printk("\n"); #endif @@ -579,12 +587,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) mmc_omap_xfer_data(host, 1); } - if (status & OMAP_MMC_STAT_END_OF_DATA) { + if (status & OMAP_MMC_STAT_END_OF_DATA) end_transfer = 1; - } if (status & OMAP_MMC_STAT_DATA_TOUT) { - dev_dbg(mmc_dev(host->mmc), "data timeout\n"); + dev_dbg(mmc_dev(host->mmc), "data timeout (CMD%d)\n", + cmd); if (host->data) { host->data->error = -ETIMEDOUT; transfer_error = 1; @@ -608,12 +616,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (host->cmd) { struct mmc_omap_slot *slot = host->current_slot; - if (!mmc_omap_cover_is_open(slot)) + if (slot == NULL || + !mmc_omap_cover_is_open(slot)) dev_err(mmc_dev(host->mmc), - "command timeout, CMD %d\n", - host->cmd->opcode); + "command timeout (CMD%d)\n", + cmd); host->cmd->error = -ETIMEDOUT; end_command = 1; + cmd_error = 1; } } @@ -621,9 +631,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (host->cmd) { dev_err(mmc_dev(host->mmc), "command CRC error (CMD%d, arg 0x%08x)\n", - host->cmd->opcode, host->cmd->arg); + cmd, host->cmd->arg); host->cmd->error = -EILSEQ; end_command = 1; + cmd_error = 1; } else dev_err(mmc_dev(host->mmc), "command CRC error without cmd?\n"); @@ -632,13 +643,13 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (status & OMAP_MMC_STAT_CARD_ERR) { dev_dbg(mmc_dev(host->mmc), "ignoring card status error (CMD%d)\n", - host->cmd->opcode); + cmd); end_command = 1; } /* * NOTE: On 1610 the END_OF_CMD may come too early when - * starting a write + * starting a write */ if ((status & OMAP_MMC_STAT_END_OF_CMD) && (!(status & OMAP_MMC_STAT_A_EMPTY))) { @@ -646,13 +657,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } } - if (end_command) { + if (end_command) mmc_omap_cmd_done(host, host->cmd); + if (host->data != NULL) { + if (transfer_error) + mmc_omap_xfer_done(host, host->data); + else if (end_transfer) + mmc_omap_end_of_data(host, host->data); } - if (transfer_error) - mmc_omap_xfer_done(host, host->data); - else if (end_transfer) - mmc_omap_end_of_data(host, host->data); return IRQ_HANDLED; } -- cgit v1.2.2 From eb1860bccd01a75b20fd7298af89b9cbda2202ba Mon Sep 17 00:00:00 2001 From: Jarkko Lavinen Date: Wed, 26 Mar 2008 16:09:29 -0400 Subject: MMC: OMAP: Abort stuck commands When a card is removed while it is being accessed, a command can get stuck so that no timeout or end of command interrupt ever occurs. The command getting stuck is almost always CDM12, but also the other commands can get stuck. Catch a stuck command with a timer and try sending the initialization stream until the controller starts running again and responds with the end of command status. Signed-off-by: Jarkko Lavinen Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 93 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 535499f11e7c..a1dfa74c50ab 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -134,6 +134,9 @@ struct mmc_omap_host { unsigned char bus_mode; unsigned char hw_bus_mode; + struct work_struct cmd_abort; + struct timer_list cmd_timer; + unsigned int sg_len; int sg_idx; u16 * buffer; @@ -314,6 +317,8 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) if (host->data && !(host->data->flags & MMC_DATA_WRITE)) cmdreg |= 1 << 15; + mod_timer(&host->cmd_timer, jiffies + HZ/2); + OMAP_MMC_WRITE(host, CTO, 200); OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); @@ -372,10 +377,38 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) mmc_omap_start_command(host, data->stop); } +static void +mmc_omap_send_abort(struct mmc_omap_host *host) +{ + struct mmc_omap_slot *slot = host->current_slot; + unsigned int restarts, passes, timeout; + u16 stat = 0; + + /* Sending abort takes 80 clocks. Have some extra and round up */ + timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq; + restarts = 0; + while (restarts < 10000) { + OMAP_MMC_WRITE(host, STAT, 0xFFFF); + OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7)); + + passes = 0; + while (passes < timeout) { + stat = OMAP_MMC_READ(host, STAT); + if (stat & OMAP_MMC_STAT_END_OF_CMD) + goto out; + udelay(1); + passes++; + } + + restarts++; + } +out: + OMAP_MMC_WRITE(host, STAT, stat); +} + static void mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) { - int loops; u16 ie; if (host->dma_in_use) @@ -386,16 +419,8 @@ mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) ie = OMAP_MMC_READ(host, IE); OMAP_MMC_WRITE(host, IE, 0); - OMAP_MMC_WRITE(host, CMD,