diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-21 20:23:30 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-21 20:23:30 -0400 |
| commit | 135cedad7457be6a96d5e151dfd48f7888a75e94 (patch) | |
| tree | d2ea838ef41ab7dcb85e655b0e6b6ed585c11fe8 | |
| parent | 8a3227268877b81096d7b7a841aaf51099ad2068 (diff) | |
| parent | e70aa3fac1ac50c7a75ac676a1489dd1ea3b4be5 (diff) | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (26 commits)
mmc: sdio_ops.c should #include "sdio_ops.h"
mmc: proper prototypes for mmc_attach_*()
mmc: make __mmc_release_bus() static
sdhci: improve no card, no reset quirk
MMC: OMAP: Do not busy wait for end of command for ever
MMC: OMAP: Start new commands from work queue instead of irq
MMC: OMAP: Lazy clock shutdown
MMC: OMAP: Move failing command abortion to workqueue
MMC: OMAP: Use tasklet instead of workqueue for cover switch notification
MMC: OMAP: Check the get_cover_state function pointer if not set
MMC: OMAP: Using setup_timer instead of init_timer
MMC: OMAP: Abort stuck commands
MMC: OMAP: General cleanup for MMC multislot support
MMC: OMAP: Power functions modified to MMC multislot support
MMC: OMAP: Fix timeout calculation for MMC multislot support
MMC: OMAP: New release dma and abort xfer functions
MMC: OMAP: Add back cover switch support
MMC: OMAP: Introduce new multislot structure and change driver to use it
MMC: OMAP: Remove cover switch handling to allow adding multislot support
MMC: OMAP: Fix the BYTEBLOCK capability removal
...
| -rw-r--r-- | drivers/mmc/core/core.c | 6 | ||||
| -rw-r--r-- | drivers/mmc/core/core.h | 4 | ||||
| -rw-r--r-- | drivers/mmc/core/host.c | 39 | ||||
| -rw-r--r-- | drivers/mmc/core/sdio_irq.c | 4 | ||||
| -rw-r--r-- | drivers/mmc/core/sdio_ops.c | 1 | ||||
| -rw-r--r-- | drivers/mmc/host/omap.c | 995 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci.c | 100 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci.h | 9 | ||||
| -rw-r--r-- | include/asm-arm/arch-omap/mmc.h | 2 |
9 files changed, 778 insertions, 382 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b96667448eb5..01ced4c5a61d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
| @@ -35,10 +35,6 @@ | |||
| 35 | #include "sd_ops.h" | 35 | #include "sd_ops.h" |
| 36 | #include "sdio_ops.h" | 36 | #include "sdio_ops.h" |
| 37 | 37 | ||
| 38 | extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); | ||
| 39 | extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); | ||
| 40 | extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr); | ||
| 41 | |||
| 42 | static struct workqueue_struct *workqueue; | 38 | static struct workqueue_struct *workqueue; |
| 43 | 39 | ||
| 44 | /* | 40 | /* |
| @@ -516,7 +512,7 @@ static void mmc_power_off(struct mmc_host *host) | |||
| 516 | /* | 512 | /* |
| 517 | * Cleanup when the last reference to the bus operator is dropped. | 513 | * Cleanup when the last reference to the bus operator is dropped. |
| 518 | */ | 514 | */ |
| 519 | void __mmc_release_bus(struct mmc_host *host) | 515 | static void __mmc_release_bus(struct mmc_host *host) |
| 520 | { | 516 | { |
| 521 | BUG_ON(!host); | 517 | BUG_ON(!host); |
| 522 | BUG_ON(host->bus_refs); | 518 | BUG_ON(host->bus_refs); |
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index cfa8e15b5923..cdb332b7dedc 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h | |||
| @@ -46,6 +46,10 @@ void mmc_rescan(struct work_struct *work); | |||
| 46 | void mmc_start_host(struct mmc_host *host); | 46 | void mmc_start_host(struct mmc_host *host); |
| 47 | void mmc_stop_host(struct mmc_host *host); | 47 | void mmc_stop_host(struct mmc_host *host); |
| 48 | 48 | ||
| 49 | int mmc_attach_mmc(struct mmc_host *host, u32 ocr); | ||
| 50 | int mmc_attach_sd(struct mmc_host *host, u32 ocr); | ||
| 51 | int mmc_attach_sdio(struct mmc_host *host, u32 ocr); | ||
| 52 | |||
| 49 | extern int use_spi_crc; | 53 | extern int use_spi_crc; |
| 50 | 54 | ||
| 51 | #endif | 55 | #endif |
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 @@ | |||
| 2 | * linux/drivers/mmc/core/host.c | 2 | * linux/drivers/mmc/core/host.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2003 Russell King, All Rights Reserved. | 4 | * Copyright (C) 2003 Russell King, All Rights Reserved. |
| 5 | * Copyright (C) 2007 Pierre Ossman | 5 | * Copyright (C) 2007-2008 Pierre Ossman |
| 6 | * | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
| @@ -57,12 +57,25 @@ static DEFINE_SPINLOCK(mmc_host_lock); | |||
| 57 | */ | 57 | */ |
| 58 | struct mmc_host *mmc_alloc_host(int extra, struct device *dev) | 58 | struct mmc_host *mmc_alloc_host(int extra, struct device *dev) |
| 59 | { | 59 | { |
| 60 | int err; | ||
| 60 | struct mmc_host *host; | 61 | struct mmc_host *host; |
| 61 | 62 | ||
| 63 | if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) | ||
| 64 | return NULL; | ||
| 65 | |||
| 62 | host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); | 66 | host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); |
| 63 | if (!host) | 67 | if (!host) |
| 64 | return NULL; | 68 | return NULL; |
| 65 | 69 | ||
| 70 | spin_lock(&mmc_host_lock); | ||
| 71 | err = idr_get_new(&mmc_host_idr, host, &host->index); | ||
| 72 | spin_unlock(&mmc_host_lock); | ||
| 73 | if (err) | ||
| 74 | goto free; | ||
| 75 | |||
| 76 | snprintf(host->class_dev.bus_id, BUS_ID_SIZE, | ||
| 77 | "mmc%d", host->index); | ||
| 78 | |||
| 66 | host->parent = dev; | 79 | host->parent = dev; |
| 67 | host->class_dev.parent = dev; | 80 | host->class_dev.parent = dev; |
| 68 | host->class_dev.class = &mmc_host_class; | 81 | host->class_dev.class = &mmc_host_class; |
| @@ -85,6 +98,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) | |||
| 85 | host->max_blk_count = PAGE_CACHE_SIZE / 512; | 98 | host->max_blk_count = PAGE_CACHE_SIZE / 512; |
| 86 | 99 | ||
| 87 | return host; | 100 | return host; |
| 101 | |||
| 102 | free: | ||
| 103 | kfree(host); | ||
| 104 | return NULL; | ||
| 88 | } | 105 | } |
| 89 | 106 | ||
| 90 | EXPORT_SYMBOL(mmc_alloc_host); | 107 | EXPORT_SYMBOL(mmc_alloc_host); |
| @@ -104,18 +121,6 @@ int mmc_add_host(struct mmc_host *host) | |||
| 104 | WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && | 121 | WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && |
| 105 | !host->ops->enable_sdio_irq); | 122 | !host->ops->enable_sdio_irq); |
| 106 | 123 | ||
| 107 | if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) | ||
| 108 | return -ENOMEM; | ||
| 109 | |||
| 110 | spin_lock(&mmc_host_lock); | ||
| 111 | err = idr_get_new(&mmc_host_idr, host, &host->index); | ||
| 112 | spin_unlock(&mmc_host_lock); | ||
| 113 | if (err) | ||
| 114 | return err; | ||
| 115 | |||
| 116 | snprintf(host->class_dev.bus_id, BUS_ID_SIZE, | ||
| 117 | "mmc%d", host->index); | ||
| 118 | |||
| 119 | led_trigger_register_simple(host->class_dev.bus_id, &host->led); | 124 | led_trigger_register_simple(host->class_dev.bus_id, &host->led); |
| 120 | 125 | ||
| 121 | err = device_add(&host->class_dev); | 126 | err = device_add(&host->class_dev); |
| @@ -144,10 +149,6 @@ void mmc_remove_host(struct mmc_host *host) | |||
| 144 | device_del(&host->class_dev); | 149 | device_del(&host->class_dev); |
| 145 | 150 | ||
| 146 | led_trigger_unregister_simple(host->led); | 151 | led_trigger_unregister_simple(host->led); |
| 147 | |||
| 148 | spin_lock(&mmc_host_lock); | ||
| 149 | idr_remove(&mmc_host_idr, host->index); | ||
| 150 | spin_unlock(&mmc_host_lock); | ||
| 151 | } | 152 | } |
| 152 | 153 | ||
| 153 | EXPORT_SYMBOL(mmc_remove_host); | 154 | EXPORT_SYMBOL(mmc_remove_host); |
| @@ -160,6 +161,10 @@ EXPORT_SYMBOL(mmc_remove_host); | |||
| 160 | */ | 161 | */ |
| 161 | void mmc_free_host(struct mmc_host *host) | 162 | void mmc_free_host(struct mmc_host *host) |
| 162 | { | 163 | { |
| 164 | spin_lock(&mmc_host_lock); | ||
| 165 | idr_remove(&mmc_host_idr, host->index); | ||
| 166 | spin_unlock(&mmc_host_lock); | ||
| 167 | |||
| 163 | put_device(&host->class_dev); | 168 | put_device(&host->class_dev); |
| 164 | } | 169 | } |
| 165 | 170 | ||
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) | |||
| 128 | } | 128 | } |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | set_task_state(current, TASK_INTERRUPTIBLE); | 131 | set_current_state(TASK_INTERRUPTIBLE); |
| 132 | if (host->caps & MMC_CAP_SDIO_IRQ) | 132 | if (host->caps & MMC_CAP_SDIO_IRQ) |
| 133 | host->ops->enable_sdio_irq(host, 1); | 133 | host->ops->enable_sdio_irq(host, 1); |
| 134 | if (!kthread_should_stop()) | 134 | if (!kthread_should_stop()) |
| 135 | schedule_timeout(period); | 135 | schedule_timeout(period); |
| 136 | set_task_state(current, TASK_RUNNING); | 136 | set_current_state(TASK_RUNNING); |
| 137 | } while (!kthread_should_stop()); | 137 | } while (!kthread_should_stop()); |
| 138 | 138 | ||
| 139 | if (host->caps & MMC_CAP_SDIO_IRQ) | 139 | if (host->caps & MMC_CAP_SDIO_IRQ) |
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index e1fca588e385..c8fa095a4488 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/mmc/sdio.h> | 17 | #include <linux/mmc/sdio.h> |
| 18 | 18 | ||
| 19 | #include "core.h" | 19 | #include "core.h" |
| 20 | #include "sdio_ops.h" | ||
| 20 | 21 | ||
| 21 | int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) | 22 | int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) |
| 22 | { | 23 | { |
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 90c358b57d1c..14759e9f42ad 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include <asm/mach-types.h> | 32 | #include <asm/mach-types.h> |
| 33 | 33 | ||
| 34 | #include <asm/arch/board.h> | 34 | #include <asm/arch/board.h> |
| 35 | #include <asm/arch/mmc.h> | ||
| 35 | #include <asm/arch/gpio.h> | 36 | #include <asm/arch/gpio.h> |
| 36 | #include <asm/arch/dma.h> | 37 | #include <asm/arch/dma.h> |
| 37 | #include <asm/arch/mux.h> | 38 | #include <asm/arch/mux.h> |
| @@ -93,9 +94,27 @@ | |||
| 93 | 94 | ||
| 94 | /* Specifies how often in millisecs to poll for card status changes | 95 | /* Specifies how often in millisecs to poll for card status changes |
| 95 | * when the cover switch is open */ | 96 | * when the cover switch is open */ |
| 96 | #define OMAP_MMC_SWITCH_POLL_DELAY 500 | 97 | #define OMAP_MMC_COVER_POLL_DELAY 500 |
| 97 | 98 | ||
| 98 | static int mmc_omap_enable_poll = 1; | 99 | struct mmc_omap_host; |
| 100 | |||
| 101 | struct mmc_omap_slot { | ||
| 102 | int id; | ||
| 103 | unsigned int vdd; | ||
| 104 | u16 saved_con; | ||
| 105 | u16 bus_mode; | ||
| 106 | unsigned int fclk_freq; | ||
| 107 | unsigned powered:1; | ||
| 108 | |||
| 109 | struct tasklet_struct cover_tasklet; | ||
| 110 | struct timer_list cover_timer; | ||
| 111 | unsigned cover_open; | ||
| 112 | |||
| 113 | struct mmc_request *mrq; | ||
| 114 | struct mmc_omap_host *host; | ||
| 115 | struct mmc_host *mmc; | ||
| 116 | struct omap_mmc_slot_data *pdata; | ||
| 117 | }; | ||
| 99 | 118 | ||
| 100 | struct mmc_omap_host { | 119 | struct mmc_omap_host { |
| 101 | int initialized; | 120 | int initialized; |
| @@ -115,6 +134,15 @@ struct mmc_omap_host { | |||
| 115 | unsigned char bus_mode; | 134 | unsigned char bus_mode; |
| 116 | unsigned char hw_bus_mode; | 135 | unsigned char hw_bus_mode; |
| 117 | 136 | ||
| 137 | struct work_struct cmd_abort_work; | ||
| 138 | unsigned abort:1; | ||
| 139 | struct timer_list cmd_abort_timer; | ||
| 140 | |||
| 141 | struct work_struct slot_release_work; | ||
| 142 | struct mmc_omap_slot *next_slot; | ||
| 143 | struct work_struct send_stop_work; | ||
| 144 | struct mmc_data *stop_data; | ||
| 145 | |||
| 118 | unsigned int sg_len; | 146 | unsigned int sg_len; |
| 119 | int sg_idx; | 147 | int sg_idx; |
| 120 | u16 * buffer; | 148 | u16 * buffer; |
| @@ -131,63 +159,178 @@ struct mmc_omap_host { | |||
| 131 | unsigned dma_len; | 159 | unsigned dma_len; |
| 132 | 160 | ||
| 133 | short power_pin; | 161 | short power_pin; |
| 134 | short wp_pin; | ||
| 135 | 162 | ||
| 136 | int switch_pin; | 163 | struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS]; |
| 137 | struct work_struct switch_work; | 164 | struct mmc_omap_slot *current_slot; |
| 138 | struct timer_list switch_timer; | 165 | spinlock_t slot_lock; |
| 139 | int switch_last_state; | 166 | wait_queue_head_t slot_wq; |
| 167 | int nr_slots; | ||
| 168 | |||
| 169 | struct timer_list clk_timer; | ||
| 170 | spinlock_t clk_lock; /* for changing enabled state */ | ||
| 171 | unsigned int fclk_enabled:1; | ||
| 172 | |||
| 173 | struct omap_mmc_platform_data *pdata; | ||
| 140 | }; | 174 | }; |
| 141 | 175 | ||
| 142 | static inline int | 176 | void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot) |
| 143 | mmc_omap_cover_is_open(struct mmc_omap_host *host) | ||
| 144 | { | 177 | { |
| 145 | if (host->switch_pin < 0) | 178 | unsigned long tick_ns; |
| 146 | return 0; | 179 | |
| 147 | return omap_get_gpio_datain(host->switch_pin); | 180 | if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) { |
| 181 | tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq; | ||
| 182 | ndelay(8 * tick_ns); | ||
| 183 | } | ||
| 148 | } | 184 | } |
| 149 | 185 | ||
| 150 | static ssize_t | 186 | void mmc_omap_fclk_enable(struct mmc_omap_host *host, unsigned int enable) |
| 151 | mmc_omap_show_cover_switch(struct device *dev, | ||
| 152 | struct device_attribute *attr, char *buf) | ||
| 153 | { | 187 | { |
| 154 | struct mmc_omap_host *host = dev_get_drvdata(dev); | 188 | unsigned long flags; |
| 155 | 189 | ||
| 156 | return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : | 190 | spin_lock_irqsave(&host->clk_lock, flags); |
| 157 | "closed"); | 191 | if (host->fclk_enabled != enable) { |
| 192 | host->fclk_enabled = enable; | ||
| 193 | if (enable) | ||
| 194 | clk_enable(host->fclk); | ||
| 195 | else | ||
| 196 | clk_disable(host->fclk); | ||
| 197 | } | ||
| 198 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
| 158 | } | 199 | } |
| 159 | 200 | ||
| 160 | static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); | 201 | static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) |
| 202 | { | ||
| 203 | struct mmc_omap_host *host = slot->host; | ||
| 204 | unsigned long flags; | ||
| 161 | 205 | ||
| 162 | static ssize_t | 206 | if (claimed) |
| 163 | mmc_omap_show_enable_poll(struct device *dev, | 207 | goto no_claim; |
| 164 | struct device_attribute *attr, char *buf) | 208 | spin_lock_irqsave(&host->slot_lock, flags); |
| 209 | while (host->mmc != NULL) { | ||
| 210 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
| 211 | wait_event(host->slot_wq, host->mmc == NULL); | ||
| 212 | spin_lock_irqsave(&host->slot_lock, flags); | ||
| 213 | } | ||
| 214 | host->mmc = slot->mmc; | ||
| 215 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
| 216 | no_claim: | ||
| 217 | del_timer(&host->clk_timer); | ||
| 218 | if (host->current_slot != slot || !claimed) | ||
| 219 | mmc_omap_fclk_offdelay(host->current_slot); | ||
| 220 | |||
| 221 | if (host->current_slot != slot) { | ||
| 222 | OMAP_MMC_WRITE(host, CON, slot->saved_con & 0xFC00); | ||
| 223 | if (host->pdata->switch_slot != NULL) | ||
| 224 | host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id); | ||
| 225 | host->current_slot = slot; | ||
| 226 | } | ||
| 227 | |||
| 228 | if (claimed) { | ||
| 229 | mmc_omap_fclk_enable(host, 1); | ||
| 230 | |||
| 231 | /* Doing the dummy read here seems to work around some bug | ||
| 232 | * at least in OMAP24xx silicon where the command would not | ||
| 233 | * start after writing the CMD register. Sigh. */ | ||
| 234 | OMAP_MMC_READ(host, CON); | ||
| 235 | |||
| 236 | OMAP_MMC_WRITE(host, CON, slot->saved_con); | ||
| 237 | } else | ||
| 238 | mmc_omap_fclk_enable(host, 0); | ||
| 239 | } | ||
| 240 | |||
| 241 | static void mmc_omap_start_request(struct mmc_omap_host *host, | ||
| 242 | struct mmc_request *req); | ||
| 243 | |||
| 244 | static void mmc_omap_slot_release_work(struct work_struct *work) | ||
| 165 | { | 245 | { |
| 166 | return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); | 246 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, |
| 247 | slot_release_work); | ||
| 248 | struct mmc_omap_slot *next_slot = host->next_slot; | ||
| 249 | struct mmc_request *rq; | ||
| 250 | |||
| 251 | host->next_slot = NULL; | ||
| 252 | mmc_omap_select_slot(next_slot, 1); | ||
| 253 | |||
| 254 | rq = next_slot->mrq; | ||
| 255 | next_slot->mrq = NULL; | ||
| 256 | mmc_omap_start_request(host, rq); | ||
| 167 | } | 257 | } |
| 168 | 258 | ||
| 169 | static ssize_t | 259 | static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) |
| 170 | mmc_omap_store_enable_poll(struct device *dev, | ||
| 171 | struct device_attribute *attr, const char *buf, | ||
| 172 | size_t size) | ||
| 173 | { | 260 | { |
| 174 | int enable_poll; | 261 | struct mmc_omap_host *host = slot->host; |
| 262 | unsigned long flags; | ||
| 263 | int i; | ||
| 264 | |||
| 265 | BUG_ON(slot == NULL || host->mmc == NULL); | ||
| 266 | |||
| 267 | if (clk_enabled) | ||
| 268 | /* Keeps clock running for at least 8 cycles on valid freq */ | ||
| 269 | mod_timer(&host->clk_timer, jiffies + HZ/10); | ||
| 270 | else { | ||
| 271 | del_timer(&host->clk_timer); | ||
| 272 | mmc_omap_fclk_offdelay(slot); | ||
| 273 | mmc_omap_fclk_enable(host, 0); | ||
| 274 | } | ||
| 175 | 275 | ||
| 176 | if (sscanf(buf, "%10d", &enable_poll) != 1) | 276 | spin_lock_irqsave(&host->slot_lock, flags); |
| 177 | return -EINVAL; | 277 | /* Check for any pending requests */ |
| 278 | for (i = 0; i < host->nr_slots; i++) { | ||
| 279 | struct mmc_omap_slot *new_slot; | ||
| 178 | 280 | ||
| 179 | if (enable_poll != mmc_omap_enable_poll) { | 281 | if (host->slots[i] == NULL || host->slots[i]->mrq == NULL) |
| 180 | struct mmc_omap_host *host = dev_get_drvdata(dev); | 282 | continue; |
| 181 | 283 | ||
| 182 | mmc_omap_enable_poll = enable_poll; | 284 | BUG_ON(host->next_slot != NULL); |
| 183 | if (enable_poll && host->switch_pin >= 0) | 285 | new_slot = host->slots[i]; |
| 184 | schedule_work(&host->switch_work); | 286 | /* The current slot should not have a request in queue */ |
| 287 | BUG_ON(new_slot == host->current_slot); | ||
| 288 | |||
| 289 | host->next_slot = new_slot; | ||
| 290 | host->mmc = new_slot->mmc; | ||
| 291 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
| 292 | schedule_work(&host->slot_release_work); | ||
| 293 | return; | ||
| 185 | } | 294 | } |
| 186 | return size; | 295 | |
| 296 | host->mmc = NULL; | ||
| 297 | wake_up(&host->slot_wq); | ||
| 298 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
| 299 | } | ||
| 300 | |||
| 301 | static inline | ||
| 302 | int mmc_omap_cover_is_open(struct mmc_omap_slot *slot) | ||
| 303 | { | ||
| 304 | if (slot->pdata->get_cover_state) | ||
| 305 | return slot->pdata->get_cover_state(mmc_dev(slot->mmc), | ||
| 306 | slot->id); | ||
| 307 | return 0; | ||
| 308 | } | ||
| 309 | |||
| 310 | static ssize_t | ||
| 311 | mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr, | ||
| 312 | char *buf) | ||
| 313 | { | ||
| 314 | struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); | ||
| 315 | struct mmc_omap_slot *slot = mmc_priv(mmc); | ||
| 316 | |||
| 317 | return sprintf(buf, "%s\n", mmc_omap_cover_is_open(slot) ? "open" : | ||
| 318 | "closed"); | ||
| 187 | } | 319 | } |
| 188 | 320 | ||
| 189 | static DEVICE_ATTR(enable_poll, 0664, | 321 | static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); |
| 190 | mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); | 322 | |
| 323 | static ssize_t | ||
| 324 | mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, | ||
| 325 | char *buf) | ||
| 326 | { | ||
| 327 | struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); | ||
| 328 | struct mmc_omap_slot *slot = mmc_priv(mmc); | ||
| 329 | |||
| 330 | return sprintf(buf, "%s\n", slot->pdata->name); | ||
| 331 | } | ||
| 332 | |||
| 333 | static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); | ||
| 191 | 334 | ||
| 192 | static void | 335 | static void |
| 193 | mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | 336 | mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) |
| @@ -233,7 +376,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
| 233 | 376 | ||
| 234 | cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); | 377 | cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); |
| 235 | 378 | ||
| 236 | if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) | 379 | if (host->current_slot->bus_mode == MMC_BUSMODE_OPENDRAIN) |
| 237 | cmdreg |= 1 << 6; | 380 | cmdreg |= 1 << 6; |
| 238 | 381 | ||
| 239 | if (cmd->flags & MMC_RSP_BUSY) | 382 | if (cmd->flags & MMC_RSP_BUSY) |
| @@ -242,7 +385,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
| 242 | if (host->data && !(host->data->flags & MMC_DATA_WRITE)) | 385 | if (host->data && !(host->data->flags & MMC_DATA_WRITE)) |
| 243 | cmdreg |= 1 << 15; | 386 | cmdreg |= 1 << 15; |
| 244 | 387 | ||
| 245 | clk_enable(host->fclk); | 388 | mod_timer(&host->cmd_abort_timer, jiffies + HZ/2); |
| 246 | 389 | ||
| 247 | OMAP_MMC_WRITE(host, CTO, 200); | 390 | OMAP_MMC_WRITE(host, CTO, 200); |
| 248 | OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); | 391 | OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); |
| @@ -257,26 +400,46 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
| 257 | } | 400 | } |
| 258 | 401 | ||
| 259 | static void | 402 | static void |
| 403 | mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data, | ||
| 404 | int abort) | ||
| 405 | { | ||
| 406 | enum dma_data_direction dma_data_dir; | ||
| 407 | |||
| 408 | BUG_ON(host->dma_ch < 0); | ||
| 409 | if (data->error) | ||
| 410 | omap_stop_dma(host->dma_ch); | ||
| 411 | /* Release DMA channel lazily */ | ||
| 412 | mod_timer(&host->dma_timer, jiffies + HZ); | ||
| 413 | if (data->flags & MMC_DATA_WRITE) | ||
| 414 | dma_data_dir = DMA_TO_DEVICE; | ||
| 415 | else | ||
| 416 | dma_data_dir = DMA_FROM_DEVICE; | ||
| 417 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, | ||
| 418 | dma_data_dir); | ||
| 419 | } | ||
| 420 | |||
| 421 | static void mmc_omap_send_stop_work(struct work_struct *work) | ||
| 422 | { | ||
| 423 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, | ||
| 424 | send_stop_work); | ||
| 425 | struct mmc_omap_slot *slot = host->current_slot; | ||
| 426 | struct mmc_data *data = host->stop_data; | ||
| 427 | unsigned long tick_ns; | ||
| 428 | |||
| 429 | tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq; | ||
| 430 | ndelay(8*tick_ns); | ||
| 431 | |||
| 432 | mmc_omap_start_command(host, data->stop); | ||
| 433 | } | ||
| 434 | |||
| 435 | static void | ||
| 260 | mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) | 436 | mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) |
| 261 | { | 437 | { |
| 262 | if (host->dma_in_use) { | 438 | if (host->dma_in_use) |
| 263 | enum dma_data_direction dma_data_dir; | 439 | mmc_omap_release_dma(host, data, data->error); |
| 264 | 440 | ||
| 265 | BUG_ON(host->dma_ch < 0); | ||
| 266 | if (data->error) | ||
| 267 | omap_stop_dma(host->dma_ch); | ||
| 268 | /* Release DMA channel lazily */ | ||
| 269 | mod_timer(&host->dma_timer, jiffies + HZ); | ||
| 270 | if (data->flags & MMC_DATA_WRITE) | ||
| 271 | dma_data_dir = DMA_TO_DEVICE; | ||
| 272 | else | ||
| 273 | dma_data_dir = DMA_FROM_DEVICE; | ||
| 274 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, | ||
| 275 | dma_data_dir); | ||
| 276 | } | ||
| 277 | host->data = NULL; | 441 | host->data = NULL; |
| 278 | host->sg_len = 0; | 442 | host->sg_len = 0; |
| 279 | clk_disable(host->fclk); | ||
| 280 | 443 | ||
| 281 | /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing | 444 | /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing |
| 282 | * dozens of requests until the card finishes writing data. | 445 | * dozens of requests until the card finishes writing data. |
| @@ -284,12 +447,58 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) | |||
| 284 | */ | 447 | */ |
| 285 | 448 | ||
| 286 | if (!data->stop) { | 449 | if (!data->stop) { |
| 450 | struct mmc_host *mmc; | ||
| 451 | |||
| 287 | host->mrq = NULL; | 452 | host->mrq = NULL; |
| 288 | mmc_request_done(host->mmc, data->mrq); | 453 | mmc = host->mmc; |
| 454 | mmc_omap_release_slot(host->current_slot, 1); | ||
| 455 | mmc_request_done(mmc, data->mrq); | ||
| 289 | return; | 456 | return; |
| 290 | } | 457 | } |
| 291 | 458 | ||
| 292 | mmc_omap_start_command(host, data->stop); | 459 | host->stop_data = data; |
| 460 | schedule_work(&host->send_stop_work); | ||
| 461 | } | ||
| 462 | |||
| 463 | static void | ||
| 464 | mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops) | ||
| 465 | { | ||
| 466 | struct mmc_omap_slot *slot = host->current_slot; | ||
| 467 | unsigned int restarts, passes, timeout; | ||
| 468 | u16 stat = 0; | ||
| 469 | |||
| 470 | /* Sending abort takes 80 clocks. Have some extra and round up */ | ||
| 471 | timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq; | ||
| 472 | restarts = 0; | ||
| 473 | while (restarts < maxloops) { | ||
| 474 | OMAP_MMC_WRITE(host, STAT, 0xFFFF); | ||
| 475 | OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7)); | ||
| 476 | |||
| 477 | passes = 0; | ||
| 478 | while (passes < timeout) { | ||
| 479 | stat = OMAP_MMC_READ(host, STAT); | ||
| 480 | if (stat & OMAP_MMC_STAT_END_OF_CMD) | ||
| 481 | goto out; | ||
| 482 | udelay(1); | ||
| 483 | passes++; | ||
| 484 | } | ||
| 485 | |||
| 486 | restarts++; | ||
| 487 | } | ||
| 488 | out: | ||
| 489 | OMAP_MMC_WRITE(host, STAT, stat); | ||
| 490 | } | ||
| 491 | |||
| 492 | static void | ||
| 493 | mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) | ||
| 494 | { | ||
| 495 | if (host->dma_in_use) | ||
| 496 | mmc_omap_release_dma(host, data, 1); | ||
| 497 | |||
| 498 | host->data = NULL; | ||
| 499 | host->sg_len = 0; | ||
| 500 | |||
| 501 | mmc_omap_send_abort(host, 10000); | ||
| 293 | } | 502 | } |
| 294 | 503 | ||
| 295 | static void | 504 | static void |
| @@ -345,6 +554,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
| 345 | { | 554 | { |
| 346 | host->cmd = NULL; | 555 | host->cmd = NULL; |
| 347 | 556 | ||
| 557 | del_timer(&host->cmd_abort_timer); | ||
| 558 | |||
| 348 | if (cmd->flags & MMC_RSP_PRESENT) { | 559 | if (cmd->flags & MMC_RSP_PRESENT) { |
| 349 | if (cmd->flags & MMC_RSP_136) { | 560 | if (cmd->flags & MMC_RSP_136) { |
| 350 | /* response type 2 */ | 561 | /* response type 2 */ |
| @@ -369,10 +580,66 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
| 369 | } | 580 | } |
| 370 | 581 | ||
| 371 | if (host->data == NULL || cmd->error) { | 582 | if (host->data == NULL || cmd->error) { |
| 583 | struct mmc_host *mmc; | ||
| 584 | |||
| 585 | if (host->data != NULL) | ||
| 586 | mmc_omap_abort_xfer(host, host->data); | ||
| 372 | host->mrq = NULL; | 587 | host->mrq = NULL; |
| 373 | clk_disable(host->fclk); | 588 | mmc = host->mmc; |
| 374 | mmc_request_done(host->mmc, cmd->mrq); | 589 | mmc_omap_release_slot(host->current_slot, 1); |
| 590 | mmc_request_done(mmc, cmd->mrq); | ||
| 591 | } | ||
| 592 | } | ||
| 593 | |||
| 594 | /* | ||
| 595 | * Abort stuck command. Can occur when card is removed while it is being | ||
| 596 | * read. | ||
| 597 | */ | ||
| 598 | static void mmc_omap_abort_command(struct work_struct *work) | ||
| 599 | { | ||
| 600 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, | ||
| 601 | cmd_abort_work); | ||
| 602 | BUG_ON(!host->cmd); | ||
| 603 | |||
| 604 | dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n", | ||
| 605 | host->cmd->opcode); | ||
| 606 | |||
| 607 | if (host->cmd->error == 0) | ||
| 608 | host->cmd->error = -ETIMEDOUT; | ||
| 609 | |||
| 610 | if (host->data == NULL) { | ||
| 611 | struct mmc_command *cmd; | ||
| 612 | struct mmc_host *mmc; | ||
| 613 | |||
| 614 | cmd = host->cmd; | ||
| 615 | host->cmd = NULL; | ||
| 616 | mmc_omap_send_abort(host, 10000); | ||
| 617 | |||
| 618 | host->mrq = NULL; | ||
| 619 | mmc = host->mmc; | ||
| 620 | mmc_omap_release_slot(host->current_slot, 1); | ||
| 621 | mmc_request_done(mmc, cmd->mrq); | ||
| 622 | } else | ||
| 623 | mmc_omap_cmd_done(host, host->cmd); | ||
| 624 | |||
| 625 | host->abort = 0; | ||
| 626 | enable_irq(host->irq); | ||
| 627 | } | ||
| 628 | |||
| 629 | static void | ||
| 630 | mmc_omap_cmd_timer(unsigned long data) | ||
| 631 | { | ||
| 632 | struct mmc_omap_host *host = (struct mmc_omap_host *) data; | ||
| 633 | unsigned long flags; | ||
| 634 | |||
| 635 | spin_lock_irqsave(&host->slot_lock, flags); | ||
| 636 | if (host->cmd != NULL && !host->abort) { | ||
| 637 | OMAP_MMC_WRITE(host, IE, 0); | ||
| 638 | disable_irq(host->irq); | ||
| 639 | host->abort = 1; | ||
| 640 | schedule_work(&host->cmd_abort_work); | ||
| 375 | } | 641 | } |
| 642 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
| 376 | } | 643 | } |
| 377 | 644 | ||
| 378 | /* PIO only */ | 645 | /* PIO only */ |
| @@ -388,6 +655,14 @@ mmc_omap_sg_to_buf(struct mmc_omap_host *host) | |||
| 388 | host->buffer_bytes_left = host->total_bytes_left; | 655 | host->buffer_bytes_left = host->total_bytes_left; |
| 389 | } | 656 | } |
| 390 | 657 | ||
| 658 | static void | ||
| 659 | mmc_omap_clk_timer(unsigned long data) | ||
| 660 | { | ||
| 661 | struct mmc_omap_host *host = (struct mmc_omap_host *) data; | ||
| 662 | |||
| 663 | mmc_omap_fclk_enable(host, 0); | ||
| 664 | } | ||
| 665 | |||
| 391 | /* PIO only */ | 666 | /* PIO only */ |
| 392 | static void | 667 | static void |
| 393 | mmc_omap_xfer_data(struct mmc_omap_host *host, int write) | 668 | mmc_omap_xfer_data(struct mmc_omap_host *host, int write) |
| @@ -436,11 +711,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 436 | u16 status; | 711 | u16 status; |
| 437 | int end_command; | 712 | int end_command; |
| 438 | int end_transfer; | 713 | int end_transfer; |
| 439 | int transfer_error; | 714 | int transfer_error, cmd_error; |
| 440 | 715 | ||
| 441 | if (host->cmd == NULL && host->data == NULL) { | 716 | if (host->cmd == NULL && host->data == NULL) { |
| 442 | status = OMAP_MMC_READ(host, STAT); | 717 | status = OMAP_MMC_READ(host, STAT); |
| 443 | dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); | 718 | dev_info(mmc_dev(host->slots[0]->mmc), |
| 719 | "Spurious IRQ 0x%04x\n", status); | ||
| 444 | if (status != 0) { | 720 | if (status != 0) { |
| 445 | OMAP_MMC_WRITE(host, STAT, status); | 721 | OMAP_MMC_WRITE(host, STAT, status); |
| 446 | OMAP_MMC_WRITE(host, IE, 0); | 722 | OMAP_MMC_WRITE(host, IE, 0); |
| @@ -451,12 +727,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 451 | end_command = 0; | 727 | end_command = 0; |
| 452 | end_transfer = 0; | 728 | end_transfer = 0; |
| 453 | transfer_error = 0; | 729 | transfer_error = 0; |
| 730 | cmd_error = 0; | ||
| 454 | 731 | ||
| 455 | while ((status = OMAP_MMC_READ(host, STAT)) != 0) { | 732 | while ((status = OMAP_MMC_READ(host, STAT)) != 0) { |
| 733 | int cmd; | ||
| 734 | |||
| 456 | OMAP_MMC_WRITE(host, STAT, status); | 735 | OMAP_MMC_WRITE(host, STAT, status); |
| 736 | if (host->cmd != NULL) | ||
| 737 | cmd = host->cmd->opcode; | ||
| 738 | else | ||
| 739 | cmd = -1; | ||
| 457 | #ifdef CONFIG_MMC_DEBUG | 740 | #ifdef CONFIG_MMC_DEBUG |
| 458 | dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", | 741 | dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", |
| 459 | status, host->cmd != NULL ? host->cmd->opcode : -1); | 742 | status, cmd); |
| 460 | mmc_omap_report_irq(status); | 743 | mmc_omap_report_irq(status); |
| 461 | printk("\n"); | 744 | printk("\n"); |
| 462 | #endif | 745 | #endif |
| @@ -468,12 +751,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 468 | mmc_omap_xfer_data(host, 1); | 751 | mmc_omap_xfer_data(host, 1); |
| 469 | } | 752 | } |
| 470 | 753 | ||
| 471 | if (status & OMAP_MMC_STAT_END_OF_DATA) { | 754 | if (status & OMAP_MMC_STAT_END_OF_DATA) |
| 472 | end_transfer = 1; | 755 | end_transfer = 1; |
| 473 | } | ||
| 474 | 756 | ||
| 475 | if (status & OMAP_MMC_STAT_DATA_TOUT) { | 757 | if (status & OMAP_MMC_STAT_DATA_TOUT) { |
| 476 | dev_dbg(mmc_dev(host->mmc), "data timeout\n"); | 758 | dev_dbg(mmc_dev(host->mmc), "data timeout (CMD%d)\n", |
| 759 | cmd); | ||
| 477 | if (host->data) { | 760 | if (host->data) { |
| 478 | host->data->error = -ETIMEDOUT; | 761 | host->data->error = -ETIMEDOUT; |
| 479 | transfer_error = 1; | 762 | transfer_error = 1; |
| @@ -495,17 +778,16 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 495 | if (status & OMAP_MMC_STAT_CMD_TOUT) { | 778 | if (status & OMAP_MMC_STAT_CMD_TOUT) { |
| 496 | /* Timeouts are routine with some commands */ | 779 | /* Timeouts are routine with some commands */ |
| 497 | if (host->cmd) { | 780 | if (host->cmd) { |
| 498 | if (host->cmd->opcode != MMC_ALL_SEND_CID && | 781 | struct mmc_omap_slot *slot = |
| 499 | host->cmd->opcode != | 782 | host->current_slot; |
| 500 | MMC_SEND_OP_COND && | 783 | if (slot == NULL || |
| 501 | host->cmd->opcode != | 784 | !mmc_omap_cover_is_open(slot)) |
| 502 | MMC_APP_CMD && | ||
| 503 | !mmc_omap_cover_is_open(host)) | ||
| 504 | dev_err(mmc_dev(host->mmc), | 785 | dev_err(mmc_dev(host->mmc), |
| 505 | "command timeout, CMD %d\n", | 786 | "command timeout (CMD%d)\n", |
| 506 | host->cmd->opcode); | 787 | cmd); |
| 507 | host->cmd->error = -ETIMEDOUT; | 788 | host->cmd->error = -ETIMEDOUT; |
| 508 | end_command = 1; | 789 | end_command = 1; |
| 790 | cmd_error = 1; | ||
| 509 | } | 791 | } |
| 510 | } | 792 | } |
| 511 | 793 | ||
| @@ -513,9 +795,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 513 | if (host->cmd) { | 795 | if (host->cmd) { |
| 514 | dev_err(mmc_dev(host->mmc), | 796 | dev_err(mmc_dev(host->mmc), |
| 515 | "command CRC error (CMD%d, arg 0x%08x)\n", | 797 | "command CRC error (CMD%d, arg 0x%08x)\n", |
| 516 | host->cmd->opcode, host->cmd->arg); | 798 | cmd, host->cmd->arg); |
| 517 | host->cmd->error = -EILSEQ; | 799 | host->cmd->error = -EILSEQ; |
| 518 | end_command = 1; | 800 | end_command = 1; |
| 801 | cmd_error = 1; | ||
| 519 | } else | 802 | } else |
| 520 | dev_err(mmc_dev(host->mmc), | 803 | dev_err(mmc_dev(host->mmc), |
| 521 | "command CRC error without cmd?\n"); | 804 | "command CRC error without cmd?\n"); |
| @@ -524,13 +807,13 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 524 | if (status & OMAP_MMC_STAT_CARD_ERR) { | 807 | if (status & OMAP_MMC_STAT_CARD_ERR) { |
| 525 | dev_dbg(mmc_dev(host->mmc), | 808 | dev_dbg(mmc_dev(host->mmc), |
| 526 | "ignoring card status error (CMD%d)\n", | 809 | "ignoring card status error (CMD%d)\n", |
| 527 | host->cmd->opcode); | 810 | cmd); |
| 528 | end_command = 1; | 811 | end_command = 1; |
| 529 | } | 812 | } |
| 530 | 813 | ||
| 531 | /* | 814 | /* |
| 532 | * NOTE: On 1610 the END_OF_CMD may come too early when | 815 | * NOTE: On 1610 the END_OF_CMD may come too early when |
| 533 | * starting a write | 816 | * starting a write |
| 534 | */ | 817 | */ |
| 535 | if ((status & OMAP_MMC_STAT_END_OF_CMD) && | 818 | if ((status & OMAP_MMC_STAT_END_OF_CMD) && |
| 536 | (!(status & OMAP_MMC_STAT_A_EMPTY))) { | 819 | (!(status & OMAP_MMC_STAT_A_EMPTY))) { |
| @@ -538,63 +821,72 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 538 | } | 821 | } |
| 539 | } | 822 | } |
| 540 | 823 | ||
| 541 | if (end_command) { | 824 | if (cmd_error && host->data) { |
| 825 | del_timer(&host->cmd_abort_timer); | ||
| 826 | host->abort = 1; | ||
| 827 | OMAP_MMC_WRITE(host, IE, 0); | ||
| 828 | disable_irq(host->irq); | ||
| 829 | schedule_work(&host->cmd_abort_work); | ||
| 830 | return IRQ_HANDLED; | ||
| 831 | } | ||
| 832 | |||
| 833 | if (end_command) | ||
| 542 | mmc_omap_cmd_done(host, host->cmd); | 834 | mmc_omap_cmd_done(host, host->cmd); |
| 835 | if (host->data != NULL) { | ||
| 836 | if (transfer_error) | ||
| 837 | mmc_omap_xfer_done(host, host->data); | ||
| 838 | else if (end_transfer) | ||
| 839 | mmc_omap_end_of_data(host, host->data); | ||
| 543 | } | 840 | } |
| 544 | if (transfer_error) | ||
| 545 | mmc_omap_xfer_done(host, host->data); | ||
| 546 | else if (end_transfer) | ||
| 547 | mmc_omap_end_of_data(host, host->data); | ||
| 548 | 841 | ||
| 549 | return IRQ_HANDLED; | 842 | return IRQ_HANDLED; |
| 550 | } | 843 | } |
| 551 | 844 | ||
| 552 | static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id) | 845 | void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed) |
| 553 | { | 846 | { |
| 554 | struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; | 847 | int cover_open; |
| 848 | struct mmc_omap_host *host = dev_get_drvdata(dev); | ||
| 849 | struct mmc_omap_slot *slot = host->slots[num]; | ||
| 555 | 850 | ||
| 556 | schedule_work(&host->switch_work); | 851 | BUG_ON(num >= host->nr_slots); |
| 557 | 852 | ||
| 558 | return IRQ_HANDLED; | 853 | /* Other subsystems can call in here before we're initialised. */ |
| 854 | if (host->nr_slots == 0 || !host->slots[num]) | ||
| 855 | return; | ||
| 856 | |||
| 857 | cover_open = mmc_omap_cover_is_open(slot); | ||
| 858 | if (cover_open != slot->cover_open) { | ||
| 859 | slot->cover_open = cover_open; | ||
| 860 | sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch"); | ||
| 861 | } | ||
| 862 | |||
| 863 | tasklet_hi_schedule(&slot->cover_tasklet); | ||
| 559 | } | 864 | } |
| 560 | 865 | ||
| 561 | static void mmc_omap_switch_timer(unsigned long arg) | 866 | static void mmc_omap_cover_timer(unsigned long arg) |
| 562 | { | 867 | { |
| 563 | struct mmc_omap_host *host = (struct mmc_omap_host *) arg; | 868 | struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg; |
| 564 | 869 | tasklet_schedule(&slot->cover_tasklet); | |
| 565 | schedule_work(&host->switch_work); | ||
| 566 | } | 870 | } |
| 567 | 871 | ||
| 568 | static void mmc_omap_switch_handler(struct work_struct *work) | 872 | static void mmc_omap_cover_handler(unsigned long param) |
| 569 | { | 873 | { |
| 570 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work); | 874 | struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param; |
| 571 | struct mmc_card *card; | 875 | int cover_open = mmc_omap_cover_is_open(slot); |
| 572 | static int complained = 0; | ||
| 573 | int cards = 0, cover_open; | ||
| 574 | 876 | ||
| 575 | if (host->switch_pin == -1) | 877 | mmc_detect_change(slot->mmc, 0); |
| 878 | if (!cover_open) | ||
| 576 | return; | 879 | return; |
| 577 | cover_open = mmc_omap_cover_is_open(host); | 880 | |
| 578 | if (cover_open != host->switch_last_state) { | 881 | /* |
| 579 | kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); | 882 | * If no card is inserted, we postpone polling until |
| 580 | host->switch_last_state = cover_open; | 883 | * the cover has been closed. |
| 581 | } | 884 | */ |
| 582 | mmc_detect_change(host->mmc, 0); | 885 | if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card)) |
| 583 | list_for_each_entry(card, &host->mmc->cards, node) { | 886 | return; |
| 584 | if (mmc_card_present(card)) | 887 | |
| 585 | cards++; | 888 | mod_timer(&slot->cover_timer, |
| 586 | } | 889 | jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY)); |
| 587 | if (mmc_omap_cover_is_open(host)) { | ||
| 588 | if (!complained) { | ||
| 589 | dev_info(mmc_dev(host->mmc), "cover is open\n"); | ||
| 590 | complained = 1; | ||
| 591 | } | ||
| 592 | if (mmc_omap_enable_poll) | ||
| 593 | mod_timer(&host->switch_timer, jiffies + | ||
| 594 | msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY)); | ||
| 595 | } else { | ||
| 596 | complained = 0; | ||
| 597 | } | ||
| 598 | } | 890 | } |
| 599 | 891 | ||
| 600 | /* Prepare to transfer the next segment of a scatterlist */ | 892 | /* Prepare to transfer the next segment of a scatterlist */ |
| @@ -765,13 +1057,12 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_reques | |||
| 765 | 1057 | ||
| 766 | static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) | 1058 | static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) |
| 767 | { | 1059 | { |
| 768 | int timeout; | 1060 | unsigned int timeout, cycle_ns; |
| 769 | u16 reg; | 1061 | u16 reg; |
| 770 | 1062 | ||
| 771 | /* Convert ns to clock cycles by assuming 20MHz frequency | 1063 | cycle_ns = 1000000000 / host->current_slot->fclk_freq; |
| 772 | * 1 cycle at 20MHz = 500 ns | 1064 | timeout = req->data->timeout_ns / cycle_ns; |
| 773 | */ | 1065 | timeout += req->data->timeout_clks; |
| 774 | timeout = req->data->timeout_clks + req->data->timeout_ns / 500; | ||
| 775 | 1066 | ||
| 776 | /* Check if we need to use timeout multiplier register */ | 1067 | /* Check if we need to use timeout multiplier register */ |
| 777 | reg = OMAP_MMC_READ(host, SDIO); | 1068 | reg = OMAP_MMC_READ(host, SDIO); |
| @@ -854,11 +1145,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) | |||
| 854 | } | 1145 | } |
| 855 | } | 1146 | } |
| 856 | 1147 | ||
| 857 | static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) | 1148 | static void mmc_omap_start_request(struct mmc_omap_host *host, |
| 1149 | struct mmc_request *req) | ||
| 858 | { | 1150 | { |
| 859 | struct mmc_omap_host *host = mmc_priv(mmc); | 1151 | BUG_ON(host->mrq != NULL); |
| 860 | |||
| 861 | WARN_ON(host->mrq != NULL); | ||
| 862 | 1152 | ||
| 863 | host->mrq = req; | 1153 | host->mrq = req; |
| 864 | 1154 | ||
| @@ -867,60 +1157,56 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) | |||
| 867 | mmc_omap_start_command(host, req->cmd); | 1157 | mmc_omap_start_command(host, req->cmd); |
| 868 | if (host->dma_in_use) | 1158 | if (host->dma_in_use) |
| 869 | omap_start_dma(host->dma_ch); | 1159 | omap_start_dma(host->dma_ch); |
| 1160 | BUG_ON(irqs_disabled()); | ||
| 870 | } | 1161 | } |
| 871 | 1162 | ||
| 872 | static void innovator_fpga_socket_power(int on) | 1163 | static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) |
| 873 | { | 1164 | { |
| 874 | #if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) | 1165 | struct mmc_omap_slot *slot = mmc_priv(mmc); |
| 875 | if (on) { | 1166 | struct mmc_omap_host *host = slot->host; |
| 876 | fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), | 1167 | unsigned long flags; |
| 877 | OMAP1510_FPGA_POWER); | 1168 | |
| 878 | } else { | 1169 | spin_lock_irqsave(&host->slot_lock, flags); |
| 879 | fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), | 1170 | if (host->mmc != NULL) { |
| 880 | OMAP1510_FPGA_POWER); | 1171 | BUG_ON(slot->mrq != NULL); |
| 881 | } | 1172 | slot->mrq = req; |
| 882 | #endif | 1173 | spin_unlock_irqrestore(&host->slot_lock, flags); |
| 1174 | return; | ||
| 1175 | } else | ||
| 1176 | host->mmc = mmc; | ||
| 1177 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
| 1178 | mmc_omap_select_slot(slot, 1); | ||
| 1179 | mmc_omap_start_request(host, req); | ||
| 883 | } | 1180 | } |
| 884 | 1181 | ||
| 885 | /* | 1182 | static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on, |
| 886 | * Turn the socket power on/off. Innovator uses FPGA, most boards | 1183 | int vdd) |
| 887 | * probably use GPIO. | ||
| 888 | */ | ||
| 889 | static void mmc_omap_power(struct mmc_omap_host *host, int on) | ||
| 890 | { | 1184 | { |
| 891 | if (on) { | 1185 | struct mmc_omap_host *host; |
| 892 | if (machine_is_omap_innovator()) | 1186 | |
| 893 | innovator_fpga_socket_power(1); | 1187 | host = slot->host; |
| 894 | else if (machine_is_omap_h2()) | 1188 | |
| 895 | tps65010_set_gpio_out_value(GPIO3, HIGH); | 1189 | if (slot->pdata->set_power != NULL) |
| 896 | else if (machine_is_omap_h3()) | 1190 | slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on, |
| 897 | /* GPIO 4 of TPS65010 sends SD_EN signal */ | 1191 | vdd); |
| 898 | tps65010_set_gpio_out_value(GPIO4, HIGH); | 1192 | |
| 899 | else if (cpu_is_omap24xx()) { | 1193 | if (cpu_is_omap24xx()) { |
| 900 | u16 reg = OMAP_MMC_READ(host, CON); | 1194 | u16 w; |
| 901 | OMAP_MMC_WRITE(host, CON, reg | (1 << 11)); | 1195 | |
| 902 | } else | 1196 | if (power_on) { |
| 903 | if (host->power_pin >= 0) | 1197 | w = OMAP_MMC_READ(host, CON); |
| 904 | omap_set_gpio_dataout(host->power_pin, 1); | 1198 | OMAP_MMC_WRITE(host, CON, w | (1 << 11)); |
| 905 | } else { | 1199 | } else { |
| 906 | if (machine_is_omap_innovator()) | 1200 | w = OMAP_MMC_READ(host, CON); |
| 907 | innovator_fpga_socket_power(0); | 1201 | OMAP_MMC_WRITE(host, CON, w & ~(1 << 11)); |
| 908 | else if (machine_is_omap_h2()) | 1202 | } |
| 909 | tps65010_set_gpio_out_value(GPIO3, LOW); | ||
| 910 | else if (machine_is_omap_h3()) | ||
| 911 | tps65010_set_gpio_out_value(GPIO4, LOW); | ||
| 912 | else if (cpu_is_omap24xx()) { | ||
| 913 | u16 reg = OMAP_MMC_READ(host, CON); | ||
| 914 | OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11)); | ||
| 915 | } else | ||
| 916 | if (host->power_pin >= 0) | ||
| 917 | omap_set_gpio_dataout(host->power_pin, 0); | ||
| 918 | } | 1203 | } |
| 919 | } | 1204 | } |
| 920 | 1205 | ||
| 921 | static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) | 1206 | static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) |
| 922 | { | 1207 | { |
| 923 | struct mmc_omap_host *host = mmc_priv(mmc); | 1208 | struct mmc_omap_slot *slot = mmc_priv(mmc); |
| 1209 | struct mmc_omap_host *host = slot->host; | ||
| 924 | int func_clk_rate = clk_get_rate(host->fclk); | 1210 | int func_clk_rate = clk_get_rate(host->fclk); |
| 925 | int dsor; | 1211 | int dsor; |
| 926 | 1212 | ||
| @@ -936,7 +1222,8 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 936 | 1222 | ||
| 937 | if (dsor > 250) | 1223 | if (dsor > 250) |
| 938 | dsor = 250; | 1224 | dsor = 250; |
| 939 | dsor++; | 1225 | |
| 1226 | slot->fclk_freq = func_clk_rate / dsor; | ||
| 940 | 1227 | ||
| 941 | if (ios->bus_width == MMC_BUS_WIDTH_4) | 1228 | if (ios->bus_width == MMC_BUS_WIDTH_4) |
| 942 | dsor |= 1 << 15; | 1229 | dsor |= 1 << 15; |
| @@ -946,28 +1233,40 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 946 | 1233 | ||
| 947 | static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 1234 | static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
| 948 | { | 1235 | { |
| 949 | struct mmc_omap_host *host = mmc_priv(mmc); | 1236 | struct mmc_omap_slot *slot = mmc_priv(mmc); |
| 950 | int dsor; | 1237 | struct mmc_omap_host *host = slot->host; |
| 951 | int i; | 1238 | int i, dsor; |
| 1239 | int clk_enabled; | ||
| 1240 | |||
| 1241 | mmc_omap_select_slot(slot, 0); | ||
| 952 | 1242 | ||
| 953 | dsor = mmc_omap_calc_divisor(mmc, ios); | 1243 | dsor = mmc_omap_calc_divisor(mmc, ios); |
| 954 | host->bus_mode = ios->bus_mode; | ||
| 955 | host->hw_bus_mode = host->bus_mode; | ||
| 956 | 1244 | ||
| 1245 | if (ios->vdd != slot->vdd) | ||
| 1246 | slot->vdd = ios->vdd; | ||
| 1247 | |||
| 1248 | clk_enabled = 0; | ||
| 957 | switch (ios->power_mode) { | 1249 | switch (ios->power_mode) { |
| 958 | case MMC_POWER_OFF: | 1250 | case MMC_POWER_OFF: |
| 959 | mmc_omap_power(host, 0); | 1251 | mmc_omap_set_power(slot, 0, ios->vdd); |
| 960 | break; | 1252 | break; |
| 961 | case MMC_POWER_UP: | 1253 | case MMC_POWER_UP: |
| 962 | /* Cannot touch dsor yet, just power up MMC */ | 1254 | /* Cannot touch dsor yet, just power up MMC */ |
| 963 | mmc_omap_power(host, 1); | 1255 | mmc_omap_set_power(slot, 1, ios->vdd); |
| 964 | return; | 1256 | goto exit; |
| 965 | case MMC_POWER_ON: | 1257 | case MMC_POWER_ON: |
| 1258 | mmc_omap_fclk_enable(host, 1); | ||
| 1259 | clk_enabled = 1; | ||
| 966 | dsor |= 1 << 11; | 1260 | dsor |= 1 << 11; |
| 967 | break; | 1261 | break; |
| 968 | } | 1262 | } |
| 969 | 1263 | ||
| 970 | clk_enable(host->fclk); | 1264 | if (slot->bus_mode != ios->bus_mode) { |
| 1265 | if (slot->pdata->set_bus_mode != NULL) | ||
| 1266 | slot->pdata->set_bus_mode(mmc_dev(mmc), slot->id, | ||
| 1267 | ios->bus_mode); | ||
| 1268 | slot->bus_mode = ios->bus_mode; | ||
| 1269 | } | ||
| 971 | 1270 | ||
| 972 | /* On insanely high arm_per frequencies something sometimes | 1271 | /* On insanely high arm_per frequencies something sometimes |
| 973 | * goes somehow out of sync, and the POW bit is not being set, | 1272 | * goes somehow out of sync, and the POW bit is not being set, |
| @@ -975,43 +1274,143 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 975 | * Writing to the CON register twice seems to do the trick. */ | 1274 | * Writing to the CON register twice seems to do the trick. */ |
| 976 | for (i = 0; i < 2; i++) | 1275 | for (i = 0; i < 2; i++) |
| 977 | OMAP_MMC_WRITE(host, CON, dsor); | 1276 | OMAP_MMC_WRITE(host, CON, dsor); |
| 1277 | slot->saved_con = dsor; | ||
| 978 | if (ios->power_mode == MMC_POWER_ON) { | 1278 | if (ios->power_mode == MMC_POWER_ON) { |
| 1279 | /* worst case at 400kHz, 80 cycles makes 200 microsecs */ | ||
| 1280 | int usecs = 250; | ||
| 1281 | |||
| 979 | /* Send clock cycles, poll completion */ | 1282 | /* Send clock cycles, poll completion */ |
| 980 | OMAP_MMC_WRITE(host, IE, 0); | 1283 | OMAP_MMC_WRITE(host, IE, 0); |
| 981 | OMAP_MMC_WRITE(host, STAT, 0xffff); | 1284 | OMAP_MMC_WRITE(host, STAT, 0xffff); |
| 982 | OMAP_MMC_WRITE(host, CMD, 1 << 7); | 1285 | OMAP_MMC_WRITE(host, CMD, 1 << 7); |
| 983 | while ((OMAP_MMC_READ(host, STAT) & 1) == 0); | 1286 | while (usecs > 0 && (OMAP_MMC_READ(host, STAT) & 1) == 0) { |
| 1287 | udelay(1); | ||
| 1288 | usecs--; | ||
| 1289 | } | ||
| 984 | OMAP_MMC_WRITE(host, STAT, 1); | 1290 | OMAP_MMC_WRITE(host, STAT, 1); |
| 985 | } | 1291 | } |
| 986 | clk_disable(host->fclk); | ||
| 987 | } | ||
| 988 | |||
| 989 | static int mmc_omap_get_ro(struct mmc_host *mmc) | ||
| 990 | { | ||
| 991 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
| 992 | 1292 | ||
| 993 | return host->wp_pin && omap_get_gpio_datain(host->wp_pin); | 1293 | exit: |
| 1294 | mmc_omap_release_slot(slot, clk_enabled); | ||
| 994 | } | 1295 | } |
| 995 | 1296 | ||
| 996 | static const struct mmc_host_ops mmc_omap_ops = { | 1297 | static const struct mmc_host_ops mmc_omap_ops = { |
| 997 | .request = mmc_omap_request, | 1298 | .request = mmc_omap_request, |
| 998 | .set_ios = mmc_omap_set_ios, | 1299 | .set_ios = mmc_omap_set_ios, |
| 999 | .get_ro = mmc_omap_get_ro, | ||
| 1000 | }; | 1300 | }; |
| 1001 | 1301 | ||
| 1002 | static int __init mmc_omap_probe(struct platform_device *pdev) | 1302 | static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) |
| 1003 | { | 1303 | { |
| 1004 | struct omap_mmc_conf *minfo = pdev->dev.platform_data; | 1304 | struct mmc_omap_slot *slot = NULL; |
| 1005 | struct mmc_host *mmc; | 1305 | struct mmc_host *mmc; |
| 1306 | int r; | ||
| 1307 | |||
| 1308 | mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev); | ||
| 1309 | if (mmc == NULL) | ||
| 1310 | return -ENOMEM; | ||
| 1311 | |||
| 1312 | slot = mmc_priv(mmc); | ||
| 1313 | slot->host = host; | ||
| 1314 | slot->mmc = mmc; | ||
| 1315 | slot->id = id; | ||
| 1316 | slot->pdata = &host->pdata->slots[id]; | ||
| 1317 | |||
| 1318 | host->slots[id] = slot; | ||
| 1319 | |||
| 1320 | mmc->caps = MMC_CAP_MULTIWRITE; | ||
| 1321 | if (host->pdata->conf.wire4) | ||
| 1322 | mmc->caps |= MMC_CAP_4_BIT_DATA; | ||
| 1323 | |||
| 1324 | mmc->ops = &mmc_omap_ops; | ||
| 1325 | mmc->f_min = 400000; | ||
| 1326 | |||
| 1327 | if (cpu_class_is_omap2()) | ||
| 1328 | mmc->f_max = 48000000; | ||
| 1329 | else | ||
| 1330 | mmc->f_max = 24000000; | ||
| 1331 | if (host->pdata->max_freq) | ||
| 1332 | mmc->f_max = min(host->pdata->max_freq, mmc->f_max); | ||
| 1333 | mmc->ocr_avail = slot->pdata->ocr_mask; | ||
| 1334 | |||
| 1335 | /* Use scatterlist DMA to reduce per-transfer costs. | ||
| 1336 | * NOTE max_seg_size assumption that small blocks aren't | ||
| 1337 | * normally used (except e.g. for reading SD registers). | ||
| 1338 | */ | ||
| 1339 | mmc->max_phys_segs = 32; | ||
| 1340 | mmc->max_hw_segs = 32; | ||
| 1341 | mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ | ||
| 1342 | mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ | ||
| 1343 | mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | ||
| 1344 | mmc->max_seg_size = mmc->max_req_size; | ||
| 1345 | |||
| 1346 | r = mmc_add_host(mmc); | ||
| 1347 | if (r < 0) | ||
| 1348 | goto err_remove_host; | ||
| 1349 | |||
| 1350 | if (slot->pdata->name != NULL) { | ||
| 1351 | r = device_create_file(&mmc->class_dev, | ||
| 1352 | &dev_attr_slot_name); | ||
| 1353 | if (r < 0) | ||
| 1354 | goto err_remove_host; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | if (slot->pdata->get_cover_state != NULL) { | ||
| 1358 | r = device_create_file(&mmc->class_dev, | ||
| 1359 | &dev_attr_cover_switch); | ||
| 1360 | if (r < 0) | ||
| 1361 | goto err_remove_slot_name; | ||
| 1362 | |||
| 1363 | setup_timer(&slot->cover_timer, mmc_omap_cover_timer, | ||
| 1364 | (unsigned long)slot); | ||
| 1365 | tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler, | ||
| 1366 | (unsigned long)slot); | ||
| 1367 | tasklet_schedule(&slot->cover_tasklet); | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | return 0; | ||
| 1371 | |||
| 1372 | err_remove_slot_name: | ||
| 1373 | if (slot->pdata->name != NULL) | ||
| 1374 | device_remove_file(&mmc->class_dev, &dev_attr_slot_name); | ||
| 1375 | err_remove_host: | ||
| 1376 | mmc_remove_host(mmc); | ||
| 1377 | mmc_free_host(mmc); | ||
| 1378 | return r; | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) | ||
| 1382 | { | ||
| 1383 | struct mmc_host *mmc = slot->mmc; | ||
| 1384 | |||
| 1385 | if (slot->pdata->name != NULL) | ||
| 1386 | device_remove_file(&mmc->class_dev, &dev_attr_slot_name); | ||
| 1387 | if (slot->pdata->get_cover_state != NULL) | ||
| 1388 | device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); | ||
| 1389 | |||
| 1390 | tasklet_kill(&slot->cover_tasklet); | ||
| 1391 | del_timer_sync(&slot->cover_timer); | ||
| 1392 | flush_scheduled_work(); | ||
| 1393 | |||
| 1394 | mmc_remove_host(mmc); | ||
| 1395 | mmc_free_host(mmc); | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | static int __init mmc_omap_probe(struct platform_device *pdev) | ||
| 1399 | { | ||
| 1400 | struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; | ||
| 1006 | struct mmc_omap_host *host = NULL; | 1401 | struct mmc_omap_host *host = NULL; |
| 1007 | struct resource *res; | 1402 | struct resource *res; |
| 1008 | int ret = 0; | 1403 | int i, ret = 0; |
| 1009 | int irq; | 1404 | int irq; |
| 1010 | 1405 | ||
| 1011 | if (minfo == NULL) { | 1406 | if (pdata == NULL) { |
| 1012 | dev_err(&pdev->dev, "platform data missing\n"); | 1407 | dev_err(&pdev->dev, "platform data missing\n"); |
| 1013 | return -ENXIO; | 1408 | return -ENXIO; |
| 1014 | } | 1409 | } |
| 1410 | if (pdata->nr_slots == 0) { | ||
| 1411 | dev_err(&pdev->dev, "no slots\n"); | ||
| 1412 | return -ENXIO; | ||
| 1413 | } | ||
| 1015 | 1414 | ||
| 1016 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1415 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 1017 | irq = platform_get_irq(pdev, 0); | 1416 | irq = platform_get_irq(pdev, 0); |
| @@ -1019,28 +1418,46 @@ static int __init mmc_omap_probe(struct platform_device *pdev) | |||
| 1019 | return -ENXIO; | 1418 | return -ENXIO; |
| 1020 | 1419 | ||
| 1021 | res = request_mem_region(res->start, res->end - res->start + 1, | 1420 | res = request_mem_region(res->start, res->end - res->start + 1, |
| 1022 | pdev->name); | 1421 | pdev->name); |
| 1023 | if (res == NULL) | 1422 | if (res == NULL) |
| 1024 | return -EBUSY; | 1423 | return -EBUSY; |
| 1025 | 1424 | ||
| 1026 | mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); | 1425 | host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL); |
| 1027 | if (mmc == NULL) { | 1426 | if (host == NULL) { |
| 1028 | ret = -ENOMEM; | 1427 | ret = -ENOMEM; |
| 1029 | goto err_free_mem_region; | 1428 | goto err_free_mem_region; |
| 1030 | } | 1429 | } |
| 1031 | 1430 | ||
| 1032 | host = mmc_priv(mmc); | 1431 | INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work); |
| 1033 | host->mmc = mmc; | 1432 | INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work); |
| 1433 | |||
| 1434 | INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command); | ||
| 1435 | setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer, | ||
| 1436 | (unsigned long) host); | ||
| 1437 | |||
| 1438 | spin_lock_init(&host->clk_lock); | ||
| 1439 | setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host); | ||
| 1034 | 1440 | ||
| 1035 | spin_lock_init(&host->dma_lock); | 1441 | spin_lock_init(&host->dma_lock); |
| 1036 | init_timer(&host->dma_timer); | 1442 | setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host); |
| 1037 | host->dma_timer.function = mmc_omap_dma_timer; | 1443 | spin_lock_init(&host->slot_lock); |
| 1038 | host->dma_timer.data = (unsigned long) host; | 1444 | init_waitqueue_head(&host->slot_wq); |
| 1445 | |||
| 1446 | host->pdata = pdata; | ||
| 1447 | host->dev = &pdev->dev; | ||
| 1448 | platform_set_drvdata(pdev, host); | ||
| 1039 | 1449 | ||
| 1040 | host->id = pdev->id; | 1450 | host->id = pdev->id; |
| 1041 | host->mem_res = res; | 1451 | host->mem_res = res; |
| 1042 | host->irq = irq; | 1452 | host->irq = irq; |
| 1043 | 1453 | ||
| 1454 | host->use_dma = 1; | ||
| 1455 | host->dma_ch = -1; | ||
| 1456 | |||
| 1457 | host->irq = irq; | ||
| 1458 | host->phys_base = host->mem_res->start; | ||
| 1459 | host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); | ||
| 1460 | |||
| 1044 | if (cpu_is_omap24xx()) { | 1461 | if (cpu_is_omap24xx()) { |
| 1045 | host->iclk = clk_get(&pdev->dev, "mmc_ick"); | 1462 | host->iclk = clk_get(&pdev->dev, "mmc_ick"); |
| 1046 | if (IS_ERR(host->iclk)) | 1463 | if (IS_ERR(host->iclk)) |
| @@ -1058,109 +1475,34 @@ static int __init mmc_omap_probe(struct platform_device *pdev) | |||
| 1058 | goto err_free_iclk; | 1475 | goto err_free_iclk; |
| 1059 | } | 1476 | } |
| 1060 | 1477 | ||
| 1061 | /* REVISIT: | ||
| 1062 | * Also, use minfo->cover to decide how to manage | ||
| 1063 | * the card detect sensing. | ||
| 1064 | */ | ||
| 1065 | host->power_pin = minfo->power_pin; | ||
| 1066 | host->switch_pin = minfo->switch_pin; | ||
| 1067 | host->wp_pin = minfo->wp_pin; | ||
| 1068 | host->use_dma = 1; | ||
| 1069 | host->dma_ch = -1; | ||
| 1070 | |||
| 1071 | host->irq = irq; | ||
| 1072 | host->phys_base = host->mem_res->start; | ||
| 1073 | host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); | ||
| 1074 | |||
| 1075 | mmc->ops = &mmc_omap_ops; | ||
| 1076 | mmc->f_min = 400000; | ||
| 1077 | mmc->f_max = 24000000; | ||
| 1078 | mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | ||
| 1079 | mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; | ||
| 1080 | |||
| 1081 | if (minfo->wire4) | ||
| 1082 | mmc->caps |= MMC_CAP_4_BIT_DATA; | ||
| 1083 | |||
| 1084 | /* Use scatterlist DMA to reduce per-transfer costs. | ||
| 1085 | * NOTE max_seg_size assumption that small blocks aren't | ||
| 1086 | * normally used (except e.g. for reading SD registers). | ||
| 1087 | */ | ||
| 1088 | mmc->max_phys_segs = 32; | ||
| 1089 | mmc->max_hw_segs = 32; | ||
| 1090 | mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ | ||
| 1091 | mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ | ||
| 1092 | mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | ||
| 1093 | mmc->max_seg_size = mmc->max_req_size; | ||
| 1094 | |||
| 1095 | if (host->power_pin >= 0) { | ||
| 1096 | if ((ret = omap_request_gpio(host->power_pin)) != 0) { | ||
| 1097 | dev_err(mmc_dev(host->mmc), | ||
| 1098 | "Unable to get GPIO pin for MMC power\n"); | ||
| 1099 | goto err_free_fclk; | ||
| 1100 | } | ||
| 1101 | omap_set_gpio_direction(host->power_pin, 0); | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); | 1478 | ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); |
| 1105 | if (ret) | 1479 | if (ret) |
| 1106 | goto err_free_power_gpio; | 1480 | goto err_free_fclk; |
| 1107 | 1481 | ||
| 1108 | host->dev = &pdev->dev; | 1482 | if (pdata->init != NULL) { |
| 1109 | platform_set_drvdata(pdev, host); | 1483 | ret = pdata->init(&pdev->dev); |
| 1484 | if (ret < 0) | ||
| 1485 | goto err_free_irq; | ||
| 1486 | } | ||
| 1110 | 1487 | ||
| 1111 | if (host->switch_pin >= 0) { | 1488 | host->nr_slots = pdata->nr_slots; |
| 1112 | INIT_WORK(&host->switch_work, mmc_omap_switch_handler); | 1489 | for (i = 0; i < pdata->nr_slots; i++) { |
| 1113 | init_timer(&host->switch_timer); | 1490 | ret = mmc_omap_new_slot(host, i); |
| 1114 | host->switch_timer.function = mmc_omap_switch_timer; | 1491 | if (ret < 0) { |
| 1115 | host->switch_timer.data = (unsigned long) host; | 1492 | while (--i >= 0) |
| 1116 | if (omap_request_gpio(host->switch_pin) != 0) { | 1493 | mmc_omap_remove_slot(host->slots[i]); |
| 1117 | dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n"); | ||
| 1118 | host->switch_pin = -1; | ||
| 1119 | goto no_switch; | ||
| 1120 | } | ||
| 1121 | 1494 | ||
| 1122 | omap_set_gpio_direction(host->switch_pin, 1); | 1495 | goto err_plat_cleanup; |
| 1123 | ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin), | ||
| 1124 | mmc_omap_switch_irq, IRQF_TRIGGER_RISING, DRIVER_NAME, host); | ||
| 1125 | if (ret) { | ||
| 1126 | dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n"); | ||
| 1127 | omap_free_gpio(host->switch_pin); | ||
| 1128 | host->switch_pin = -1; | ||
| 1129 | goto no_switch; | ||
| 1130 | } | ||
| 1131 | ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); | ||
| 1132 | if (ret == 0) { | ||
| 1133 | ret = device_create_file(&pdev->dev, &dev_attr_enable_poll); | ||
| 1134 | if (ret != 0) | ||
| 1135 | device_remove_file(&pdev->dev, &dev_attr_cover_switch); | ||
| 1136 | } | 1496 | } |
| 1137 | if (ret) { | ||
| 1138 | dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n"); | ||
| 1139 | free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); | ||
| 1140 | omap_free_gpio(host->switch_pin); | ||
| 1141 | host->switch_pin = -1; | ||
| 1142 | goto no_switch; | ||
| 1143 | } | ||
| 1144 | if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host)) | ||
| 1145 | schedule_work(&host->switch_work); | ||
| 1146 | } | 1497 | } |
| 1147 | 1498 | ||
| 1148 | mmc_add_host(mmc); | ||
| 1149 | |||
| 1150 | return 0; | 1499 | return 0; |
| 1151 | 1500 | ||
| 1152 | no_switch: | 1501 | err_plat_cleanup: |
| 1153 | /* FIXME: Free other resources too. */ | 1502 | if (pdata->cleanup) |
| 1154 | if (host) { | 1503 | pdata->cleanup(&pdev->dev); |
| 1155 | if (host->iclk && !IS_ERR(host->iclk)) | 1504 | err_free_irq: |
| 1156 | clk_put(host->iclk); | 1505 | free_irq(host->irq, host); |
| 1157 | if (host->fclk && !IS_ERR(host->fclk)) | ||
| 1158 | clk_put(host->fclk); | ||
| 1159 | mmc_free_host(host->mmc); | ||
| 1160 | } | ||
| 1161 | err_free_power_gpio: | ||
| 1162 | if (host->power_pin >= 0) | ||
| 1163 | omap_free_gpio(host->power_pin); | ||
| 1164 | err_free_fclk: | 1506 | err_free_fclk: |
| 1165 | clk_put(host->fclk); | 1507 | clk_put(host->fclk); |
| 1166 | err_free_iclk: | 1508 | err_free_iclk: |
| @@ -1169,7 +1511,7 @@ err_free_iclk: | |||
| 1169 | clk_put(host->iclk); | 1511 | clk_put(host->iclk); |
| 1170 | } | 1512 | } |
| 1171 | err_free_mmc_host: | 1513 | err_free_mmc_host: |
| 1172 | mmc_free_host(host->mmc); | 1514 | kfree(host); |
| 1173 | err_free_mem_region: | 1515 | err_free_mem_region: |
| 1174 | release_mem_region(res->start, res->end - res->start + 1); | 1516 | release_mem_region(res->start, res->end - res->start + 1); |
| 1175 | return ret; | 1517 | return ret; |
| @@ -1178,25 +1520,18 @@ err_free_mem_region: | |||
| 1178 | static int mmc_omap_remove(struct platform_device *pdev) | 1520 | static int mmc_omap_remove(struct platform_device *pdev) |
| 1179 | { | 1521 | { |
| 1180 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | 1522 | struct mmc_omap_host *host = platform_get_drvdata(pdev); |
| 1523 | int i; | ||
| 1181 | 1524 | ||
| 1182 | platform_set_drvdata(pdev, NULL); | 1525 | platform_set_drvdata(pdev, NULL); |
| 1183 | 1526 | ||
| 1184 | BUG_ON(host == NULL); | 1527 | BUG_ON(host == NULL); |
| 1185 | 1528 | ||
| 1186 | mmc_remove_host(host->mmc); | 1529 | for (i = 0; i < host->nr_slots; i++) |
| 1187 | free_irq(host->irq, host); | 1530 | mmc_omap_remove_slot(host->slots[i]); |
| 1531 | |||
| 1532 | if (host->pdata->cleanup) | ||
| 1533 | host->pdata->cleanup(&pdev->dev); | ||
| 1188 | 1534 | ||
| 1189 | if (host->power_pin >= 0) | ||
| 1190 | omap_free_gpio(host->power_pin); | ||
| 1191 | if (host->switch_pin >= 0) { | ||
| 1192 | device_remove_file(&pdev->dev, &dev_attr_enable_poll); | ||
| 1193 | device_remove_file(&pdev->dev, &dev_attr_cover_switch); | ||
| 1194 | free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); | ||
| 1195 | omap_free_gpio(host->switch_pin); | ||
| 1196 | host->switch_pin = -1; | ||
| 1197 | del_timer_sync(&host->switch_timer); | ||
| 1198 | flush_scheduled_work(); | ||
| 1199 | } | ||
| 1200 | if (host->iclk && !IS_ERR(host->iclk)) | 1535 | if (host->iclk && !IS_ERR(host->iclk)) |
| 1201 | clk_put(host->iclk); | 1536 | clk_put(host->iclk); |
| 1202 | if (host->fclk && !IS_ERR(host->fclk)) | 1537 | if (host->fclk && !IS_ERR(host->fclk)) |
| @@ -1205,7 +1540,7 @@ static int mmc_omap_remove(struct platform_device *pdev) | |||
| 1205 | release_mem_region(pdev->resource[0].start, | 1540 | release_mem_region(pdev->resource[0].start, |
| 1206 | pdev->resource[0].end - pdev->resource[0].start + 1); | 1541 | pdev->resource[0].end - pdev->resource[0].start + 1); |
| 1207 | 1542 | ||
| 1208 | mmc_free_host(host->mmc); | 1543 | kfree(host); |
| 1209 | 1544 | ||
| 1210 | return 0; | 1545 | return 0; |
| 1211 | } | 1546 | } |
| @@ -1213,35 +1548,47 @@ static int mmc_omap_remove(struct platform_device *pdev) | |||
| 1213 | #ifdef CONFIG_PM | 1548 | #ifdef CONFIG_PM |
| 1214 | static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) | 1549 | static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) |
| 1215 | { | 1550 | { |
| 1216 | int ret = 0; | 1551 | int i, ret = 0; |
| 1217 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | 1552 | struct mmc_omap_host *host = platform_get_drvdata(pdev); |
| 1218 | 1553 | ||
| 1219 | if (host && host->suspended) | 1554 | if (host == NULL || host->suspended) |
| 1220 | return 0; | 1555 | return 0; |
| 1221 | 1556 | ||
| 1222 | if (host) { | 1557 | for (i = 0; i < host->nr_slots; i++) { |
| 1223 | ret = mmc_suspend_host(host->mmc, mesg); | 1558 | struct mmc_omap_slot *slot; |
| 1224 | if (ret == 0) | 1559 | |
| 1225 | host->suspended = 1; | 1560 | slot = host->slots[i]; |
| 1561 | ret = mmc_suspend_host(slot->mmc, mesg); | ||
| 1562 | if (ret < 0) { | ||
| 1563 | while (--i >= 0) { | ||
| 1564 | slot = host->slots[i]; | ||
| 1565 | mmc_resume_host(slot->mmc); | ||
| 1566 | } | ||
| 1567 | return ret; | ||
| 1568 | } | ||
| 1226 | } | 1569 | } |
| 1227 | return ret; | 1570 | host->suspended = 1; |
| 1571 | return 0; | ||
| 1228 | } | 1572 | } |
| 1229 | 1573 | ||
| 1230 | static int mmc_omap_resume(struct platform_device *pdev) | 1574 | static int mmc_omap_resume(struct platform_device *pdev) |
| 1231 | { | 1575 | { |
| 1232 | int ret = 0; | 1576 | int i, ret = 0; |
| 1233 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | 1577 | struct mmc_omap_host *host = platform_get_drvdata(pdev); |
| 1234 | 1578 | ||
| 1235 | if (host && !host->suspended) | 1579 | if (host == NULL || !host->suspended) |
| 1236 | return 0; | 1580 | return 0; |
| 1237 | 1581 | ||
| 1238 | if (host) { | 1582 | for (i = 0; i < host->nr_slots; i++) { |
| 1239 | ret = mmc_resume_host(host->mmc); | 1583 | struct mmc_omap_slot *slot; |
| 1240 | if (ret == 0) | 1584 | slot = host->slots[i]; |
| 1241 | host->suspended = 0; | 1585 | ret = mmc_resume_host(slot->mmc); |
| 1242 | } | 1586 | if (ret < 0) |
| 1587 | return ret; | ||
| 1243 | 1588 | ||
| 1244 | return ret; | 1589 | host->suspended = 0; |
| 1590 | } | ||
| 1591 | return 0; | ||
| 1245 | } | 1592 | } |
| 1246 | #else | 1593 | #else |
| 1247 | #define mmc_omap_suspend NULL | 1594 | #define mmc_omap_suspend NULL |
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4b673aa2dc3c..07c2048b230b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver | 2 | * linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. | 4 | * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. |
| 5 | * | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
| @@ -19,6 +19,8 @@ | |||
| 19 | #include <linux/dma-mapping.h> | 19 | #include <linux/dma-mapping.h> |
| 20 | #include <linux/scatterlist.h> | 20 | #include <linux/scatterlist.h> |
| 21 | 21 | ||
| 22 | #include <linux/leds.h> | ||
| 23 | |||
| 22 | #include <linux/mmc/host.h> | 24 | #include <linux/mmc/host.h> |
| 23 | 25 | ||
| 24 | #include "sdhci.h" | 26 | #include "sdhci.h" |
| @@ -30,10 +32,6 @@ | |||
| 30 | 32 | ||
| 31 | static unsigned int debug_quirks = 0; | 33 | static unsigned int debug_quirks = 0; |
| 32 | 34 | ||
| 33 | /* For multi controllers in one platform case */ | ||
| 34 | static u16 chip_index = 0; | ||
| 35 | static spinlock_t index_lock; | ||
| 36 | |||
| 37 | /* | 35 | /* |
| 38 | * Different quirks to handle when the hardware deviates from a strict | 36 | * Different quirks to handle when the hardware deviates from a strict |
| 39 | * interpretation of the SDHCI specification. | 37 | * interpretation of the SDHCI specification. |
| @@ -43,7 +41,7 @@ static spinlock_t index_lock; | |||
| 43 | #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) | 41 | #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) |
| 44 | /* Controller has bad caps bits, but really supports DMA */ | 42 | /* Controller has bad caps bits, but really supports DMA */ |
| 45 | #define SDHCI_QUIRK_FORCE_DMA (1<<1) | 43 | #define SDHCI_QUIRK_FORCE_DMA (1<<1) |
| 46 | /* Controller doesn't like some resets when there is no card inserted. */ | 44 | /* Controller doesn't like to be reset when there is no card inserted. */ |
| 47 | #define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) | 45 | #define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) |
| 48 | /* Controller doesn't like clearing the power reg before a change */ | 46 | /* Controller doesn't like clearing the power reg before a change */ |
| 49 | #define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) | 47 | #define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) |
| @@ -71,13 +69,21 @@ static const struct pci_device_id pci_ids[] __devinitdata = { | |||
| 71 | { | 69 | { |
| 72 | .vendor = PCI_VENDOR_ID_RICOH, | 70 | .vendor = PCI_VENDOR_ID_RICOH, |
| 73 | .device = PCI_DEVICE_ID_RICOH_R5C822, | 71 | .device = PCI_DEVICE_ID_RICOH_R5C822, |
| 74 | .subvendor = PCI_ANY_ID, | 72 | .subvendor = PCI_VENDOR_ID_SAMSUNG, |
| 75 | .subdevice = PCI_ANY_ID, | 73 | .subdevice = PCI_ANY_ID, |
| 76 | .driver_data = SDHCI_QUIRK_FORCE_DMA | | 74 | .driver_data = SDHCI_QUIRK_FORCE_DMA | |
| 77 | SDHCI_QUIRK_NO_CARD_NO_RESET, | 75 | SDHCI_QUIRK_NO_CARD_NO_RESET, |
| 78 | }, | 76 | }, |
| 79 | 77 | ||
| 80 | { | 78 | { |
| 79 | .vendor = PCI_VENDOR_ID_RICOH, | ||
| 80 | .device = PCI_DEVICE_ID_RICOH_R5C822, | ||
| 81 | .subvendor = PCI_ANY_ID, | ||
| 82 | .subdevice = PCI_ANY_ID, | ||
| 83 | .driver_data = SDHCI_QUIRK_FORCE_DMA, | ||
| 84 | }, | ||
| 85 | |||
| 86 | { | ||
| 81 | .vendor = PCI_VENDOR_ID_TI, | 87 | .vendor = PCI_VENDOR_ID_TI, |
| 82 | .device = PCI_DEVICE_ID_TI_XX21_XX11_SD, | 88 | .device = PCI_DEVICE_ID_TI_XX21_XX11_SD, |
| 83 | .subvendor = PCI_ANY_ID, | 89 | .subvendor = PCI_ANY_ID, |
| @@ -256,6 +262,24 @@ static void sdhci_deactivate_led(struct sdhci_host *host) | |||
| 256 | writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); | 262 | writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); |
| 257 | } | 263 | } |
| 258 | 264 | ||
| 265 | #ifdef CONFIG_LEDS_CLASS | ||
| 266 | static void sdhci_led_control(struct led_classdev *led, | ||
| 267 | enum led_brightness brightness) | ||
| 268 | { | ||
| 269 | struct sdhci_host *host = container_of(led, struct sdhci_host, led); | ||
| 270 | unsigned long flags; | ||
| 271 | |||
| 272 | spin_lock_irqsave(&host->lock, flags); | ||
| 273 | |||
| 274 | if (brightness == LED_OFF) | ||
| 275 | sdhci_deactivate_led(host); | ||
| 276 | else | ||
| 277 | sdhci_activate_led(host); | ||
| 278 | |||
| 279 | spin_unlock_irqrestore(&host->lock, flags); | ||
| 280 | } | ||
| 281 | #endif | ||
| 282 | |||
| 259 | /*****************************************************************************\ | 283 | /*****************************************************************************\ |
| 260 | * * | 284 | * * |
| 261 | * Core functions * | 285 | * Core functions * |
| @@ -773,7 +797,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
| 773 | 797 | ||
| 774 | WARN_ON(host->mrq != NULL); | 798 | WARN_ON(host->mrq != NULL); |
| 775 | 799 | ||
| 800 | #ifndef CONFIG_LEDS_CLASS | ||
| 776 | sdhci_activate_led(host); | 801 | sdhci_activate_led(host); |
| 802 | #endif | ||
| 777 | 803 | ||
| 778 | host->mrq = mrq; | 804 | host->mrq = mrq; |
| 779 | 805 | ||
| @@ -965,7 +991,9 @@ static void sdhci_tasklet_finish(unsigned long param) | |||
| 965 | host->cmd = NULL; | 991 | host->cmd = NULL; |
| 966 | host->data = NULL; | 992 | host->data = NULL; |
| 967 | 993 | ||
| 994 | #ifndef CONFIG_LEDS_CLASS | ||
| 968 | sdhci_deactivate_led(host); | 995 | sdhci_deactivate_led(host); |
| 996 | #endif | ||
| 969 | 997 | ||
| 970 | mmiowb(); | 998 | mmiowb(); |
| 971 | spin_unlock_irqrestore(&host->lock, flags); | 999 | spin_unlock_irqrestore(&host->lock, flags); |
| @@ -1105,7 +1133,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) | |||
| 1105 | goto out; | 1133 | goto out; |
| 1106 | } | 1134 | } |
| 1107 | 1135 | ||
| 1108 | DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); | 1136 | DBG("*** %s got interrupt: 0x%08x\n", |
| 1137 | mmc_hostname(host->mmc), intmask); | ||
| 1109 | 1138 | ||
| 1110 | if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { | 1139 | if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { |
| 1111 | writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), | 1140 | writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), |
| @@ -1235,7 +1264,7 @@ static int sdhci_resume (struct pci_dev *pdev) | |||
| 1235 | if (chip->hosts[i]->flags & SDHCI_USE_DMA) | 1264 | if (chip->hosts[i]->flags & SDHCI_USE_DMA) |
| 1236 | pci_set_master(pdev); | 1265 | pci_set_master(pdev); |
| 1237 | ret = request_irq(chip->hosts[i]->irq, sdhci_irq, | 1266 | ret = request_irq(chip->hosts[i]->irq, sdhci_irq, |
| 1238 | IRQF_SHARED, chip->hosts[i]->slot_descr, | 1267 | IRQF_SHARED, mmc_hostname(chip->hosts[i]->mmc), |
| 1239 | chip->hosts[i]); | 1268 | chip->hosts[i]); |
| 1240 | if (ret) | 1269 | if (ret) |
| 1241 | return ret; | 1270 | return ret; |
| @@ -1324,9 +1353,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1324 | 1353 | ||
| 1325 | DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq); | 1354 | DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq); |
| 1326 | 1355 | ||
| 1327 | snprintf(host->slot_descr, 20, "sdhc%d:slot%d", chip->index, slot); | 1356 | ret = pci_request_region(pdev, host->bar, mmc_hostname(mmc)); |
| 1328 | |||
| 1329 | ret = pci_request_region(pdev, host->bar, host->slot_descr); | ||
| 1330 | if (ret) | 1357 | if (ret) |
| 1331 | goto free; | 1358 | goto free; |
| 1332 | 1359 | ||
| @@ -1343,7 +1370,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1343 | version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; | 1370 | version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; |
| 1344 | if (version > 1) { | 1371 | if (version > 1) { |
| 1345 | printk(KERN_ERR "%s: Unknown controller version (%d). " | 1372 | printk(KERN_ERR "%s: Unknown controller version (%d). " |
| 1346 | "You may experience problems.\n", host->slot_descr, | 1373 | "You may experience problems.\n", mmc_hostname(mmc), |
| 1347 | version); | 1374 | version); |
| 1348 | } | 1375 | } |
| 1349 | 1376 | ||
| @@ -1366,13 +1393,13 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1366 | (host->flags & SDHCI_USE_DMA)) { | 1393 | (host->flags & SDHCI_USE_DMA)) { |
| 1367 | printk(KERN_WARNING "%s: Will use DMA " | 1394 | printk(KERN_WARNING "%s: Will use DMA " |
| 1368 | "mode even though HW doesn't fully " | 1395 | "mode even though HW doesn't fully " |
| 1369 | "claim to support it.\n", host->slot_descr); | 1396 | "claim to support it.\n", mmc_hostname(mmc)); |
| 1370 | } | 1397 | } |
| 1371 | 1398 | ||
| 1372 | if (host->flags & SDHCI_USE_DMA) { | 1399 | if (host->flags & SDHCI_USE_DMA) { |
| 1373 | if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { | 1400 | if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { |
| 1374 | printk(KERN_WARNING "%s: No suitable DMA available. " | 1401 | printk(KERN_WARNING "%s: No suitable DMA available. " |
| 1375 | "Falling back to PIO.\n", host->slot_descr); | 1402 | "Falling back to PIO.\n", mmc_hostname(mmc)); |
| 1376 | host->flags &= ~SDHCI_USE_DMA; | 1403 | host->flags &= ~SDHCI_USE_DMA; |
| 1377 | } | 1404 | } |
| 1378 | } | 1405 | } |
| @@ -1386,7 +1413,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1386 | (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; | 1413 | (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; |
| 1387 | if (host->max_clk == 0) { | 1414 | if (host->max_clk == 0) { |
| 1388 | printk(KERN_ERR "%s: Hardware doesn't specify base clock " | 1415 | printk(KERN_ERR "%s: Hardware doesn't specify base clock " |
| 1389 | "frequency.\n", host->slot_descr); | 1416 | "frequency.\n", mmc_hostname(mmc)); |
| 1390 | ret = -ENODEV; | 1417 | ret = -ENODEV; |
| 1391 | goto unmap; | 1418 | goto unmap; |
| 1392 | } | 1419 | } |
| @@ -1396,7 +1423,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1396 | (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; | 1423 | (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; |
| 1397 | if (host->timeout_clk == 0) { | 1424 | if (host->timeout_clk == 0) { |
| 1398 | printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " | 1425 | printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " |
| 1399 | "frequency.\n", host->slot_descr); | 1426 | "frequency.\n", mmc_hostname(mmc)); |
| 1400 | ret = -ENODEV; | 1427 | ret = -ENODEV; |
| 1401 | goto unmap; | 1428 | goto unmap; |
| 1402 | } | 1429 | } |
| @@ -1424,7 +1451,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1424 | 1451 | ||
| 1425 | if (mmc->ocr_avail == 0) { | 1452 | if (mmc->ocr_avail == 0) { |
| 1426 | printk(KERN_ERR "%s: Hardware doesn't report any " | 1453 | printk(KERN_ERR "%s: Hardware doesn't report any " |
| 1427 | "support voltages.\n", host->slot_descr); | 1454 | "support voltages.\n", mmc_hostname(mmc)); |
| 1428 | ret = -ENODEV; | 1455 | ret = -ENODEV; |
| 1429 | goto unmap; | 1456 | goto unmap; |
| 1430 | } | 1457 | } |
| @@ -1458,8 +1485,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1458 | */ | 1485 | */ |
| 1459 | mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; | 1486 | mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; |
| 1460 | if (mmc->max_blk_size >= 3) { | 1487 | if (mmc->max_blk_size >= 3) { |
| 1461 | printk(KERN_WARNING "%s: Invalid maximum block size, assuming 512\n", | 1488 | printk(KERN_WARNING "%s: Invalid maximum block size, " |
| 1462 | host->slot_descr); | 1489 | "assuming 512 bytes\n", mmc_hostname(mmc)); |
| 1463 | mmc->max_blk_size = 512; | 1490 | mmc->max_blk_size = 512; |
| 1464 | } else | 1491 | } else |
| 1465 | mmc->max_blk_size = 512 << mmc->max_blk_size; | 1492 | mmc->max_blk_size = 512 << mmc->max_blk_size; |
| @@ -1480,7 +1507,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1480 | setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); | 1507 | setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); |
| 1481 | 1508 | ||
| 1482 | ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, | 1509 | ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, |
| 1483 | host->slot_descr, host); | 1510 | mmc_hostname(mmc), host); |
| 1484 | if (ret) | 1511 | if (ret) |
| 1485 | goto untasklet; | 1512 | goto untasklet; |
| 1486 | 1513 | ||
| @@ -1490,16 +1517,32 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
| 1490 | sdhci_dumpregs(host); | 1517 | sdhci_dumpregs(host); |
| 1491 | #endif | 1518 | #endif |
| 1492 | 1519 | ||
| 1520 | #ifdef CONFIG_LEDS_CLASS | ||
| 1521 | host->led.name = mmc_hostname(mmc); | ||
| 1522 | host->led.brightness = LED_OFF; | ||
| 1523 | host->led.default_trigger = mmc_hostname(mmc); | ||
| 1524 | host->led.brightness_set = sdhci_led_control; | ||
| 1525 | |||
| 1526 | ret = led_classdev_register(&pdev->dev, &host->led); | ||
| 1527 | if (ret) | ||
| 1528 | goto reset; | ||
| 1529 | #endif | ||
| 1530 | |||
| 1493 | mmiowb(); | 1531 | mmiowb(); |
| 1494 | 1532 | ||
| 1495 | mmc_add_host(mmc); | 1533 | mmc_add_host(mmc); |
| 1496 | 1534 | ||
| 1497 | printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc), | 1535 | printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", |
| 1498 | host->addr, host->irq, | 1536 | mmc_hostname(mmc), host->addr, host->irq, |
| 1499 | (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); | 1537 | (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); |
| 1500 | 1538 | ||
| 1501 | return 0; | 1539 | return 0; |
| 1502 | 1540 | ||
| 1541 | #ifdef CONFIG_LEDS_CLASS | ||
| 1542 | reset: | ||
| 1543 | sdhci_reset(host, SDHCI_RESET_ALL); | ||
| 1544 | free_irq(host->irq, host); | ||
| 1545 | #endif | ||
| 1503 | untasklet: | 1546 | untasklet: |
| 1504 | tasklet_kill(&host->card_tasklet); | 1547 | tasklet_kill(&host->card_tasklet); |
| 1505 | tasklet_kill(&host->finish_tasklet); | 1548 | tasklet_kill(&host->finish_tasklet); |
| @@ -1527,6 +1570,10 @@ static void sdhci_remove_slot(struct pci_dev *pdev, int slot) | |||
| 1527 | 1570 | ||
| 1528 | mmc_remove_host(mmc); | 1571 | mmc_remove_host(mmc); |
| 1529 | 1572 | ||
| 1573 | #ifdef CONFIG_LEDS_CLASS | ||
| 1574 | led_classdev_unregister(&host->led); | ||
| 1575 | #endif | ||
| 1576 | |||
| 1530 | sdhci_reset(host, SDHCI_RESET_ALL); | 1577 | sdhci_reset(host, SDHCI_RESET_ALL); |
| 1531 | 1578 | ||
| 1532 | free_irq(host->irq, host); | 1579 | free_irq(host->irq, host); |
| @@ -1589,11 +1636,6 @@ static int __devinit sdhci_probe(struct pci_dev *pdev, | |||
| 1589 | chip->num_slots = slots; | 1636 | chip->num_slots = slots; |
| 1590 | pci_set_drvdata(pdev, chip); | 1637 | pci_set_drvdata(pdev, chip); |
| 1591 | 1638 | ||
| 1592 | /* Add for multi controller case */ | ||
| 1593 | spin_lock(&index_lock); | ||
| 1594 | chip->index = chip_index++; | ||
| 1595 | spin_unlock(&index_lock); | ||
| 1596 | |||
| 1597 | for (i = 0;i < slots;i++) { | 1639 | for (i = 0;i < slots;i++) { |
| 1598 | ret = sdhci_probe_slot(pdev, i); | 1640 | ret = sdhci_probe_slot(pdev, i); |
| 1599 | if (ret) { | 1641 | if (ret) { |
| @@ -1654,8 +1696,6 @@ static int __init sdhci_drv_init(void) | |||
| 1654 | ": Secure Digital Host Controller Interface driver\n"); | 1696 | ": Secure Digital Host Controller Interface driver\n"); |
| 1655 | printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); | 1697 | printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); |
| 1656 | 1698 | ||
| 1657 | spin_lock_init(&index_lock); | ||
| 1658 | |||
| 1659 | return pci_register_driver(&sdhci_driver); | 1699 | return pci_register_driver(&sdhci_driver); |
| 1660 | } | 1700 | } |
| 1661 | 1701 | ||
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index d5a38f1b755a..7fb02e177a3d 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver | 2 | * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. | 4 | * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. |
| 5 | * | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
| @@ -168,6 +168,10 @@ struct sdhci_host { | |||
| 168 | struct sdhci_chip *chip; | 168 | struct sdhci_chip *chip; |
| 169 | struct mmc_host *mmc; /* MMC structure */ | 169 | struct mmc_host *mmc; /* MMC structure */ |
| 170 | 170 | ||
| 171 | #ifdef CONFIG_LEDS_CLASS | ||
| 172 | struct led_classdev led; /* LED control */ | ||
| 173 | #endif | ||
| 174 | |||
| 171 | spinlock_t lock; /* Mutex */ | 175 | spinlock_t lock; /* Mutex */ |
| 172 | 176 | ||
| 173 | int flags; /* Host attributes */ | 177 | int flags; /* Host attributes */ |
| @@ -190,8 +194,6 @@ struct sdhci_host { | |||
| 190 | int offset; /* Offset into current sg */ | 194 | int offset; /* Offset into current sg */ |
| 191 | int remain; /* Bytes left in current */ | 195 | int remain; /* Bytes left in current */ |
| 192 | 196 | ||
| 193 | char slot_descr[20]; /* Name for reservations */ | ||
| 194 | |||
| 195 | int irq; /* Device IRQ */ | 197 | int irq; /* Device IRQ */ |
| 196 | int bar; /* PCI BAR index */ | 198 | int bar; /* PCI BAR index */ |
| 197 | unsigned long addr; /* Bus address */ | 199 | unsigned long addr; /* Bus address */ |
| @@ -208,7 +210,6 @@ struct sdhci_chip { | |||
| 208 | 210 | ||
| 209 | unsigned long quirks; | 211 | unsigned long quirks; |
| 210 | 212 | ||
| 211 | int index; /* Index for chip0, chip1 ...*/ | ||
| 212 | int num_slots; /* Slots on controller */ | 213 | int num_slots; /* Slots on controller */ |
| 213 | struct sdhci_host *hosts[0]; /* Pointers to hosts */ | 214 | struct sdhci_host *hosts[0]; /* Pointers to hosts */ |
| 214 | }; | 215 | }; |
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 @@ | |||
| 18 | #define OMAP_MMC_MAX_SLOTS 2 | 18 | #define OMAP_MMC_MAX_SLOTS 2 |
| 19 | 19 | ||
| 20 | struct omap_mmc_platform_data { | 20 | struct omap_mmc_platform_data { |
| 21 | struct omap_mmc_conf conf; | ||
| 22 | |||
| 21 | unsigned enabled:1; | 23 | unsigned enabled:1; |
| 22 | /* number of slots on board */ | 24 | /* number of slots on board */ |
| 23 | unsigned nr_slots:2; | 25 | unsigned nr_slots:2; |
