GIT 5a4f0036ec578d8a9a872da3668407c68533e2f1 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc.git#for-andrew commit 7e958c059c4c0ea1424c2461ab5fe1c90c36bc02 Author: Pierre Ossman Date: Sat Jan 20 14:15:13 2007 +0100 mmc: fix host release brown paper bag bug fixed Signed-off-by: Pierre Ossman commit 0dc5dac10483b7b5eb8cb02ec292a811769a9a47 Author: Pierre Ossman Date: Fri Jan 19 23:35:38 2007 +0100 mmc: fix locakable state detection only the mmc path return status bits so move the lockable test. Signed-off-by: Pierre Ossman commit e4a17b119b39c3bc9ec8cf111d6ccf7693ec058a Author: Pierre Ossman Date: Tue Dec 26 15:11:23 2006 +0100 mmc: replace host->card_busy As card_busy was only used to indicate if the host was exclusively claimed and not really used to identify a particular card, replacing it with just a boolean makes things a lot more easily understandable. Signed-off-by: Pierre Ossman commit 96a71eb291f39db6599fcb807c436b7dfd086a54 Author: Pierre Ossman Date: Sun Jan 14 01:41:45 2007 +0100 mmc: let host be parent of cards Change the parent of cards to be a specific host (a class device), not the physical controller. This is particularly useful when the hardware has multiple slots, meaning multiple hosts. Signed-off-by: Pierre Ossman commit 11449c6d6487179211d38b59e886587de9bc710c Author: Adrian Bunk Date: Sat Jan 13 10:56:29 2007 +0100 make mmc_sysfs.c:mmc_key_type static On Thu, Jan 11, 2007 at 10:26:27PM -0800, Andrew Morton wrote: >... > Changes since 2.6.20-rc3-mm1: >... > git-mmc.patch >... > git trees >... This patch makes the needlessly global struct mmc_key_type static. Signed-off-by: Adrian Bunk Signed-off-by: Pierre Ossman commit 5bf010c45783b3b4138344380cb08332f25bf1d6 Author: Pierre Ossman Date: Sat Jan 13 00:12:07 2007 +0100 mmc: fix lock build error Signed-off-by: Frederik Deweerdt Signed-off-by: Pierre Ossman commit e984cec03f1ec94b535bf07a09eac78cfa5b04f5 Author: Pierre Ossman Date: Sun Jan 7 16:59:06 2007 +0100 mmc: Graceful fallback for fancy features MMC high-speed, wide bus support and SD high-speed are functions that aren't critical for correct operation of the card. As such, they shouldn't mark the card as bad or dead when there is a failure activating these features. This is needed in particular on some really stupid hardware (e.g. Winbond's) where not all data transfer commands are supported. Signed-off-by: Pierre Ossman commit 475e631f3833552499d6f26d9c926f374946da68 Author: Pierre Ossman Date: Tue Nov 21 17:45:37 2006 +0100 mmc: Handle wbsd's stupid command list The wbsd hardware is so incredibly brain damaged that it has an internal list of commands that result in data transfers. The result being that commands that aren't on this list aren't supported. Instead of locking up, waiting for a data interrupt that will never come, we try to fail a bit more gracefully. Signed-off-by: Pierre Ossman commit daa0f82903fbbbe288add0941d3778aa48b71fe4 Author: Pierre Ossman Date: Sat Jan 6 23:26:29 2007 +0100 mmc: Fix variable declaration Signed-off-by: Pierre Ossman commit 17a35c615a422fcb716fd6ba9dda8e70b22f0a4f Author: Anderson Briglia Date: Sat Jan 6 22:57:35 2007 +0100 mmc: Lock sysfs interface Implement MMC password force erase, remove password, change password, unlock card and assign password operations. It uses the sysfs mechanism to send commands to the MMC subsystem. Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Anderson Lizardo Signed-off-by: Anderson Briglia Signed-off-by: Pierre Ossman commit 67d4c4509a942827740298c1ae008fb948908a62 Author: Anderson Briglia Date: Sat Jan 6 22:52:39 2007 +0100 mmc: Implement card lock/unlock operation, using the MMC_LOCK_UNLOCK command. Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Anderson Lizardo Signed-off-by: Anderson Briglia Signed-off-by: Pierre Ossman commit d7589978d8bad35388d7edfc4e91b084304f7031 Author: Anderson Briglia Date: Sat Jan 6 22:37:41 2007 +0100 mmc: Implement key retention operations. Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Anderson Lizardo Signed-off-by: Anderson Briglia Signed-off-by: Pierre Ossman commit ab8de6592b3dddbdcc970ea7e42d0769cfc82bf5 Author: Anderson Briglia Date: Sat Jan 6 22:34:58 2007 +0100 mmc: Lockable card state When a card is locked, only commands from the "basic" and "lock card" classes are accepted. To be able to use the other commands, the card must be unlocked first. This patch prevents the device drivers from probing the locked cards. Device probing must be triggered sometime later to make the card available to the block driver. Signed-off-by: Carlos Eduardo Aguiar Signed-off-by: Anderson Lizardo Signed-off-by: Anderson Briglia Signed-off-by: David Brownell Signed-off-by: Pierre Ossman commit 7a5506caabfd9186794e58796f3fc91171221dbe Author: Pierre Ossman Date: Sat Jan 6 13:52:46 2007 +0100 mmc: Fix whitespace damage Fix some trailing whitespace caused by the sdhc patch. Signed-off-by: Pierre Ossman commit 17d31ef06444fda3faf7f16ac61a31def03bb057 Author: Philip Langdale Date: Thu Jan 4 07:04:47 2007 -0800 mmc: Correct definition of R6 This change depends on my SDHC patch and fixes a bug that was revealed during the development of that patch. The R6 response type should be identical to R1 (and R7) but was incorrectly defined differently. Fixing the R6 definition breaks assumptions in these two drivers that response type flags are unique. Pierre and Alex both believe that treating R6 and R7 as R1 will be sufficient. ie: The controllers do not care about the differences between them. Due to lack of hardware, I have done no testing. Signed-off-by: Philip Langdale Cc: Alex Dubov Cc: Pavel Pisa Signed-off-by: Pierre Ossman commit ce30bd294b3740824634a65aa3695951005c002a Author: Philip Langdale Date: Thu Jan 4 06:57:32 2007 -0800 mmc: Add support for SDHC cards Thanks to the generous donation of an SDHC card by John Gilmore, and the surprisingly enlightened decision by the SD Card Association to publish useful specs, I've been able to bash out support for SDHC. The changes are not too profound: i) Add a card flag indicating the card uses block level addressing and check it in the block driver. As we never took advantage of byte-level addressing, this simply involves skipping the block -> byte translation when sending commands. ii) The layout of the CSD is changed - a set of fields are discarded to make space for a larger C_SIZE. We did not reference any of the discarded fields except those related to the C_SIZE. iii) Read and write timeouts are fixed values and not calculated from CSD values. iv) Before invoking SEND_APP_OP_COND, we must invoke the new SEND_IF_COND to inform the card we support SDHC. Signed-off-by: Philipl Langdale Signed-off-by: Pierre Ossman commit 378496ca9d49cd8d5684e06d605e3d9eeda3368a Author: Alex Dubov Date: Mon Dec 11 01:55:38 2006 +1100 tifm_sd: add suspend and resume functionality Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 59c24fb95d6abde89e2bce8c26866d1152e73906 Author: Alex Dubov Date: Mon Dec 11 01:55:37 2006 +1100 tifm_core: add suspend/resume infrastructure for tifm devices Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit e473514543ab7566e6ded73bee39a64ca8e74408 Author: Alex Dubov Date: Mon Dec 11 01:55:36 2006 +1100 tifm_7xx1: prettify Fix some spaces and tabs. No semantic changes are introduced. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 8b70744a537d8af651cc9082ae43d6c355c1ad78 Author: Alex Dubov Date: Mon Dec 11 01:55:35 2006 +1100 tifm_7xx1: recognize device 0xac8f as supported This patch also adds symbolic defines for supported pci ids. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 635f8c29092c6a000f9989ad9f4c963f40c8d6ba Author: Alex Dubov Date: Mon Dec 18 14:20:06 2006 +1100 tifm_7xx1: switch from workqueue to kthread As there's only one work item (media_switcher) to handle and it's effectively serialized with itself, I found it more convenient to use kthread instead of workqueue. This also allows for a working implementation of suspend/resume, which were totally broken in the past version. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 7cb5d8348ac3e850425ef33e29d1e8eff893cf8a Author: Alex Dubov Date: Mon Dec 11 01:55:33 2006 +1100 tifm_7xx1: Merge media insert and media remove functions Hardware does not say whether card was inserted or removed when reporting socket events. Moreover, during suspend, media can be removed or switched to some other card type without notification. Therefore, for each socket in the change set the following is performed: 1. If there's active device in the socket it's unregistered 2. Media detection is performed 3. If detection recognizes supportable media, new device is registered This patch also alters some macros and variable names to enhance clarity. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 70de92174c3f7d8efdb7b2b00904a62dc20decd3 Author: Alex Dubov Date: Mon Dec 11 01:55:32 2006 +1100 tifm_7xx1: simplify eject function Eject function can take advantage of the socket_id field instead of explicit pointer comparison. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 30e5a862125c41eba7a49030ed3bdff3c9c56ac4 Author: Alex Dubov Date: Mon Dec 11 01:55:31 2006 +1100 Add dummy_signal_irq function to save check in ISR Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 3693c54ffcec04b424f4909da230ddf6e000f06d Author: Alex Dubov Date: Mon Dec 11 01:55:30 2006 +1100 Remove unused return value from signal_irq callback Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 6bbf438fa443db2255c98604fdf1db0cef3ad7ea Author: Andrew Morton Date: Thu Dec 7 19:25:45 2006 +0100 git-mmc: tifm_sd warning fix drivers/mmc/tifm_sd.c: In function 'tifm_sd_process_cmd': drivers/mmc/tifm_sd.c:294: warning: format '%ld' expects type 'long int', but argument 4 has type 'size_t' Signed-off-by: Andrew Morton Signed-off-by: Pierre Ossman commit bd4f904e31351bd9be87f3e536e0ca5b9c378326 Author: Alex Dubov Date: Fri Dec 8 16:50:52 2006 +1100 tifm_sd: prettify This patch introduces no semantic changes - it is here for estetic purposes. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit f71ce99abe22482fd79bd5fe536327ec40be768c Author: Alex Dubov Date: Fri Dec 8 16:50:51 2006 +1100 tifm_sd: restructure initialization, removal and command handling In order to support correct suspend and resume several changes were needed: 1. Switch from work_struct to tasklet for command handling. When device suspend is called workqueues are already frozen and can not be used. 2. Separate host initialization code from driver's probe and don't rely on interrupts for host initialization. This, in turn, addresses two problems: a) Resume needs to re-initialize the host, but can not assume that device interrupts were already re-armed. b) Previously, probe will return successfully before really knowing the state of the host, as host interrupts were not armed in time. Now it uses polling to determine the real host state before returning. 3. Separate termination code from driver's remove. Termination may be caused by resume, if media changed type or became unavailable during suspend. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 7780c18ff9786d0ff577c42321fe9190a5423e7f Author: Alex Dubov Date: Fri Dec 8 16:50:50 2006 +1100 tifm_sd: fix hardware timeout setup The register access order when setting hardware timeout was incorrect and causing problems (wrong timeout intervals). This is now fixed. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 063593736541f0684eaa7529de0f4ec4b2042bc6 Author: Alex Dubov Date: Fri Dec 8 16:50:49 2006 +1100 tifm_sd: Switch software timeout handler from work_struct to timer Two changes are introduced to software timeout handler in order to simplify its management: 1. The implementation is switched from work_struct to timer 2. Previously, software timeout was rearmed with each interrupt. Now, current request must complete entirely within timeout interval. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 966ca0e3a812e25b59e17f2d7fc9f4660ebac6e1 Author: Alex Dubov Date: Fri Dec 8 16:50:48 2006 +1100 tifm_sd: use kmap_atomic instead of kmap for PIO data buffer Data buffer for PIO transfer used to be mapped in advance with kmap. Abolish it in favor of on-demand kmap_atomic. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit ac3b06fe06ad30dfc6dbcef37c0c96f0d79200a0 Author: Alex Dubov Date: Fri Dec 8 16:50:47 2006 +1100 tifm_sd: alter order of the states in the command handler Previously, stop command was issued right after BRS (block received/sent) event. Stop command completion event could interfere with the card busy event, causing miscount of the written blocks. This patch ensures that stop command issued as last action for a particular command, after DMA sompletion event and written block count verification. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 037dd570d119d6f91aa1715857bc1e944c2fa841 Author: Alex Dubov Date: Sat Dec 2 21:39:54 2006 +0100 tifm_sd: add new mmc parameters DMA block counter is shorter than transfer block counter (8b vs 11b), because DMA block counter can be restarted mid-transfer. I don't think it'll have any effect on performance, so I'm sticking with DMA block counter limit. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman commit 893f81ef8000f3093b208cb2fc7d6414d56b49d0 Author: Pierre Ossman Date: Tue Nov 21 17:55:45 2006 +0100 mmc: Allow host drivers to specify max block count Many controllers have an upper limit on the number of blocks that can be transferred in one request. Allow the host drivers to specify this and make sure we avoid hitting this limit. Also change the max_sectors field to avoid confusion. This makes it map less directly to the block layer limits, but as they didn't apply directly on MMC cards anyway, this isn't a great loss. Signed-off-by: Pierre Ossman commit 818f8f20e8fa10f6348866c792b4c589090a3912 Author: Pierre Ossman Date: Tue Nov 21 17:54:23 2006 +0100 mmc: Allow host drivers to specify a max block size Most controllers have an upper limit on the block size. Allow the host drivers to specify this and make sure we avoid hitting this limit. Signed-off-by: Pierre Ossman drivers/misc/tifm_7xx1.c | 402 ++++++++++++++++++---------------- drivers/misc/tifm_core.c | 65 ++++-- drivers/mmc/Kconfig | 13 + drivers/mmc/at91_mci.c | 3 drivers/mmc/au1xmmc.c | 3 drivers/mmc/imxmmc.c | 4 drivers/mmc/mmc.c | 306 ++++++++++++++++++++++---- drivers/mmc/mmc.h | 11 + drivers/mmc/mmc_block.c | 12 + drivers/mmc/mmc_queue.c | 2 drivers/mmc/mmc_sysfs.c | 213 ++++++++++++++++++ drivers/mmc/mmci.c | 15 + drivers/mmc/omap.c | 12 + drivers/mmc/pxamci.c | 10 + drivers/mmc/sdhci.c | 37 ++- drivers/mmc/sdhci.h | 1 drivers/mmc/tifm_sd.c | 494 ++++++++++++++++++++++++------------------ drivers/mmc/wbsd.c | 62 +++++ include/linux/mmc/card.h | 11 + include/linux/mmc/host.h | 10 + include/linux/mmc/mmc.h | 5 include/linux/mmc/protocol.h | 21 ++ include/linux/pci_ids.h | 3 include/linux/tifm.h | 35 +-- 24 files changed, 1226 insertions(+), 524 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 2ab7add..e21e490 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -11,66 +11,25 @@ #include #include +#include #define DRIVER_NAME "tifm_7xx1" -#define DRIVER_VERSION "0.6" +#define DRIVER_VERSION "0.7" static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) { - int cnt; - unsigned long flags; - - spin_lock_irqsave(&fm->lock, flags); - if (!fm->inhibit_new_cards) { - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - if (fm->sockets[cnt] == sock) { - fm->remove_mask |= (1 << cnt); - queue_work(fm->wq, &fm->media_remover); - break; - } - } - } - spin_unlock_irqrestore(&fm->lock, flags); -} - -static void tifm_7xx1_remove_media(struct work_struct *work) -{ - struct tifm_adapter *fm = - container_of(work, struct tifm_adapter, media_remover); unsigned long flags; - int cnt; - struct tifm_dev *sock; - if (!class_device_get(&fm->cdev)) - return; spin_lock_irqsave(&fm->lock, flags); - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - if (fm->sockets[cnt] && (fm->remove_mask & (1 << cnt))) { - printk(KERN_INFO DRIVER_NAME - ": demand removing card from socket %d\n", cnt); - sock = fm->sockets[cnt]; - fm->sockets[cnt] = NULL; - fm->remove_mask &= ~(1 << cnt); - - writel(0x0e00, sock->addr + SOCK_CONTROL); - - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_SET_INTERRUPT_ENABLE); - - spin_unlock_irqrestore(&fm->lock, flags); - device_unregister(&sock->dev); - spin_lock_irqsave(&fm->lock, flags); - } - } + fm->socket_change_set |= 1 << sock->socket_id; + wake_up_all(&fm->change_set_notify); spin_unlock_irqrestore(&fm->lock, flags); - class_device_put(&fm->cdev); } static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) { struct tifm_adapter *fm = dev_id; + struct tifm_dev *sock; unsigned int irq_status; unsigned int sock_irq_status, cnt; @@ -84,42 +43,32 @@ static irqreturn_t tifm_7xx1_isr(int irq if (irq_status & TIFM_IRQ_ENABLE) { writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - sock_irq_status = (irq_status >> cnt) & - (TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK); - - if (fm->sockets[cnt]) { - if (sock_irq_status && - fm->sockets[cnt]->signal_irq) - sock_irq_status = fm->sockets[cnt]-> - signal_irq(fm->sockets[cnt], - sock_irq_status); + for (cnt = 0; cnt < fm->num_sockets; cnt++) { + sock = fm->sockets[cnt]; + sock_irq_status = (irq_status >> cnt) + & (TIFM_IRQ_FIFOMASK(1) + | TIFM_IRQ_CARDMASK(1)); - if (irq_status & (1 << cnt)) - fm->remove_mask |= 1 << cnt; - } else { - if (irq_status & (1 << cnt)) - fm->insert_mask |= 1 << cnt; - } + if (sock && sock_irq_status) + sock->signal_irq(sock, sock_irq_status); } + + fm->socket_change_set |= irq_status + & ((1 << fm->num_sockets) - 1); } writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); - if (!fm->inhibit_new_cards) { - if (!fm->remove_mask && !fm->insert_mask) { - writel(TIFM_IRQ_ENABLE, - fm->addr + FM_SET_INTERRUPT_ENABLE); - } else { - queue_work(fm->wq, &fm->media_remover); - queue_work(fm->wq, &fm->media_inserter); - } - } + if (!fm->socket_change_set) + writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); + else + wake_up_all(&fm->change_set_notify); spin_unlock(&fm->lock); return IRQ_HANDLED; } -static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, int is_x2) +static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, + int is_x2) { unsigned int s_state; int cnt; @@ -127,8 +76,8 @@ static tifm_media_id tifm_7xx1_toggle_so writel(0x0e00, sock_addr + SOCK_CONTROL); for (cnt = 0; cnt < 100; cnt++) { - if (!(TIFM_SOCK_STATE_POWERED & - readl(sock_addr + SOCK_PRESENT_STATE))) + if (!(TIFM_SOCK_STATE_POWERED + & readl(sock_addr + SOCK_PRESENT_STATE))) break; msleep(10); } @@ -151,8 +100,8 @@ static tifm_media_id tifm_7xx1_toggle_so } for (cnt = 0; cnt < 100; cnt++) { - if ((TIFM_SOCK_STATE_POWERED & - readl(sock_addr + SOCK_PRESENT_STATE))) + if ((TIFM_SOCK_STATE_POWERED + & readl(sock_addr + SOCK_PRESENT_STATE))) break; msleep(10); } @@ -170,130 +119,209 @@ tifm_7xx1_sock_addr(char __iomem *base_a return base_addr + ((sock_num + 1) << 10); } -static void tifm_7xx1_insert_media(struct work_struct *work) +static int tifm_7xx1_switch_media(void *data) { - struct tifm_adapter *fm = - container_of(work, struct tifm_adapter, media_inserter); + struct tifm_adapter *fm = data; unsigned long flags; tifm_media_id media_id; char *card_name = "xx"; - int cnt, ok_to_register; - unsigned int insert_mask; - struct tifm_dev *new_sock = NULL; + int cnt, rc; + struct tifm_dev *sock; + unsigned int socket_change_set; - if (!class_device_get(&fm->cdev)) - return; - spin_lock_irqsave(&fm->lock, flags); - insert_mask = fm->insert_mask; - fm->insert_mask = 0; - if (fm->inhibit_new_cards) { + while (1) { + rc = wait_event_interruptible(fm->change_set_notify, + fm->socket_change_set); + if (rc == -ERESTARTSYS) + try_to_freeze(); + + spin_lock_irqsave(&fm->lock, flags); + socket_change_set = fm->socket_change_set; + fm->socket_change_set = 0; + + dev_dbg(fm->dev, "checking media set %x\n", + socket_change_set); + + if (kthread_should_stop()) + socket_change_set = (1 << fm->num_sockets) - 1; spin_unlock_irqrestore(&fm->lock, flags); - class_device_put(&fm->cdev); - return; - } - spin_unlock_irqrestore(&fm->lock, flags); - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - if (!(insert_mask & (1 << cnt))) + if (!socket_change_set) continue; - media_id = tifm_7xx1_toggle_sock_power(tifm_7xx1_sock_addr(fm->addr, cnt), - fm->max_sockets == 2); - if (media_id) { - ok_to_register = 0; - new_sock = tifm_alloc_device(fm, cnt); - if (new_sock) { - new_sock->addr = tifm_7xx1_sock_addr(fm->addr, - cnt); - new_sock->media_id = media_id; - switch (media_id) { - case 1: - card_name = "xd"; - break; - case 2: - card_name = "ms"; - break; - case 3: - card_name = "sd"; - break; - default: - break; - } - snprintf(new_sock->dev.bus_id, BUS_ID_SIZE, - "tifm_%s%u:%u", card_name, fm->id, cnt); + spin_lock_irqsave(&fm->lock, flags); + for (cnt = 0; cnt < fm->num_sockets; cnt++) { + if (!(socket_change_set & (1 << cnt))) + continue; + sock = fm->sockets[cnt]; + if (sock) { printk(KERN_INFO DRIVER_NAME - ": %s card detected in socket %d\n", - card_name, cnt); + ": demand removing card from socket %d\n", + cnt); + fm->sockets[cnt] = NULL; + spin_unlock_irqrestore(&fm->lock, flags); + device_unregister(&sock->dev); spin_lock_irqsave(&fm->lock, flags); - if (!fm->sockets[cnt]) { - fm->sockets[cnt] = new_sock; - ok_to_register = 1; + writel(0x0e00, + tifm_7xx1_sock_addr(fm->addr, cnt) + + SOCK_CONTROL); + } + if (kthread_should_stop()) + continue; + + spin_unlock_irqrestore(&fm->lock, flags); + media_id = tifm_7xx1_toggle_sock_power( + tifm_7xx1_sock_addr(fm->addr, cnt), + fm->num_sockets == 2); + if (media_id) { + sock = tifm_alloc_device(fm); + if (sock) { + sock->addr = tifm_7xx1_sock_addr(fm->addr, + cnt); + sock->media_id = media_id; + sock->socket_id = cnt; + switch (media_id) { + case 1: + card_name = "xd"; + break; + case 2: + card_name = "ms"; + break; + case 3: + card_name = "sd"; + break; + default: + tifm_free_device(&sock->dev); + spin_lock_irqsave(&fm->lock, flags); + continue; + } + snprintf(sock->dev.bus_id, BUS_ID_SIZE, + "tifm_%s%u:%u", card_name, + fm->id, cnt); + printk(KERN_INFO DRIVER_NAME + ": %s card detected in socket %d\n", + card_name, cnt); + if (!device_register(&sock->dev)) { + spin_lock_irqsave(&fm->lock, flags); + if (!fm->sockets[cnt]) { + fm->sockets[cnt] = sock; + sock = NULL; + } + spin_unlock_irqrestore(&fm->lock, flags); + } + if (sock) + tifm_free_device(&sock->dev); } + spin_lock_irqsave(&fm->lock, flags); + } + } + + if (!kthread_should_stop()) { + writel(TIFM_IRQ_FIFOMASK(socket_change_set) + | TIFM_IRQ_CARDMASK(socket_change_set), + fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel(TIFM_IRQ_FIFOMASK(socket_change_set) + | TIFM_IRQ_CARDMASK(socket_change_set), + fm->addr + FM_SET_INTERRUPT_ENABLE); + writel(TIFM_IRQ_ENABLE, + fm->addr + FM_SET_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&fm->lock, flags); + } else { + for (cnt = 0; cnt < fm->num_sockets; cnt++) { + if (fm->sockets[cnt]) + fm->socket_change_set |= 1 << cnt; + } + if (!fm->socket_change_set) { + spin_unlock_irqrestore(&fm->lock, flags); + return 0; + } else { spin_unlock_irqrestore(&fm->lock, flags); - if (!ok_to_register || - device_register(&new_sock->dev)) { - spin_lock_irqsave(&fm->lock, flags); - fm->sockets[cnt] = NULL; - spin_unlock_irqrestore(&fm->lock, - flags); - tifm_free_device(&new_sock->dev); - } } } - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_SET_INTERRUPT_ENABLE); } - - writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); - class_device_put(&fm->cdev); + return 0; } +#ifdef CONFIG_PM + static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) { - struct tifm_adapter *fm = pci_get_drvdata(dev); - unsigned long flags; + dev_dbg(&dev->dev, "suspending host\n"); - spin_lock_irqsave(&fm->lock, flags); - fm->inhibit_new_cards = 1; - fm->remove_mask = 0xf; - fm->insert_mask = 0; - writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - spin_unlock_irqrestore(&fm->lock, flags); - flush_workqueue(fm->wq); - - tifm_7xx1_remove_media(&fm->media_remover); - - pci_set_power_state(dev, PCI_D3hot); - pci_disable_device(dev); - pci_save_state(dev); + pci_save_state(dev); + pci_enable_wake(dev, pci_choose_state(dev, state), 0); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); return 0; } static int tifm_7xx1_resume(struct pci_dev *dev) { struct tifm_adapter *fm = pci_get_drvdata(dev); + int cnt, rc; unsigned long flags; + tifm_media_id new_ids[fm->num_sockets]; + pci_set_power_state(dev, PCI_D0); pci_restore_state(dev); - pci_enable_device(dev); - pci_set_power_state(dev, PCI_D0); - pci_set_master(dev); + rc = pci_enable_device(dev); + if (rc) + return rc; + pci_set_master(dev); + dev_dbg(&dev->dev, "resuming host\n"); + + for (cnt = 0; cnt < fm->num_sockets; cnt++) + new_ids[cnt] = tifm_7xx1_toggle_sock_power( + tifm_7xx1_sock_addr(fm->addr, cnt), + fm->num_sockets == 2); spin_lock_irqsave(&fm->lock, flags); - fm->inhibit_new_cards = 0; - writel(TIFM_IRQ_SETALL, fm->addr + FM_INTERRUPT_STATUS); - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, - fm->addr + FM_SET_INTERRUPT_ENABLE); - fm->insert_mask = 0xf; + fm->socket_change_set = 0; + for (cnt = 0; cnt < fm->num_sockets; cnt++) { + if (fm->sockets[cnt]) { + if (fm->sockets[cnt]->media_id == new_ids[cnt]) + fm->socket_change_set |= 1 << cnt; + + fm->sockets[cnt]->media_id = new_ids[cnt]; + } + } + + writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), + fm->addr + FM_SET_INTERRUPT_ENABLE); + if (!fm->socket_change_set) { + spin_unlock_irqrestore(&fm->lock, flags); + return 0; + } else { + fm->socket_change_set = 0; + spin_unlock_irqrestore(&fm->lock, flags); + } + + wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ); + + spin_lock_irqsave(&fm->lock, flags); + writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) + | TIFM_IRQ_CARDMASK(fm->socket_change_set), + fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) + | TIFM_IRQ_CARDMASK(fm->socket_change_set), + fm->addr + FM_SET_INTERRUPT_ENABLE); + writel(TIFM_IRQ_ENABLE, + fm->addr + FM_SET_INTERRUPT_ENABLE); + fm->socket_change_set = 0; + spin_unlock_irqrestore(&fm->lock, flags); return 0; } +#else + +#define tifm_7xx1_suspend NULL +#define tifm_7xx1_resume NULL + +#endif /* CONFIG_PM */ + static int tifm_7xx1_probe(struct pci_dev *dev, - const struct pci_device_id *dev_id) + const struct pci_device_id *dev_id) { struct tifm_adapter *fm; int pci_dev_busy = 0; @@ -324,19 +352,18 @@ static int tifm_7xx1_probe(struct pci_de } fm->dev = &dev->dev; - fm->max_sockets = (dev->device == 0x803B) ? 2 : 4; - fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->max_sockets, - GFP_KERNEL); + fm->num_sockets = (dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM) + ? 4 : 2; + fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets, + GFP_KERNEL); if (!fm->sockets) goto err_out_free; - INIT_WORK(&fm->media_inserter, tifm_7xx1_insert_media); - INIT_WORK(&fm->media_remover, tifm_7xx1_remove_media); fm->eject = tifm_7xx1_eject; pci_set_drvdata(dev, fm); fm->addr = ioremap(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + pci_resource_len(dev, 0)); if (!fm->addr) goto err_out_free; @@ -344,16 +371,15 @@ static int tifm_7xx1_probe(struct pci_de if (rc) goto err_out_unmap; - rc = tifm_add_adapter(fm); + init_waitqueue_head(&fm->change_set_notify); + rc = tifm_add_adapter(fm, tifm_7xx1_switch_media); if (rc) goto err_out_irq; writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, - fm->addr + FM_SET_INTERRUPT_ENABLE); - - fm->insert_mask = 0xf; - + writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), + fm->addr + FM_SET_INTERRUPT_ENABLE); + wake_up_process(fm->media_switcher); return 0; err_out_irq: @@ -377,19 +403,15 @@ static void tifm_7xx1_remove(struct pci_ struct tifm_adapter *fm = pci_get_drvdata(dev); unsigned long flags; + writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + mmiowb(); + free_irq(dev->irq, fm); + spin_lock_irqsave(&fm->lock, flags); - fm->inhibit_new_cards = 1; - fm->remove_mask = 0xf; - fm->insert_mask = 0; - writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + fm->socket_change_set = (1 << fm->num_sockets) - 1; spin_unlock_irqrestore(&fm->lock, flags); - flush_workqueue(fm->wq); - - tifm_7xx1_remove_media(&fm->media_remover); - - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - free_irq(dev->irq, fm); + kthread_stop(fm->media_switcher); tifm_remove_adapter(fm); @@ -404,10 +426,12 @@ static void tifm_7xx1_remove(struct pci_ } static struct pci_device_id tifm_7xx1_pci_tbl [] = { - { PCI_VENDOR_ID_TI, 0x8033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - 0 }, /* xx21 - the one I have */ - { PCI_VENDOR_ID_TI, 0x803B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - 0 }, /* xx12 - should be also supported */ + { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX21_XX11_FM, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, 0 }, /* xx21 - the one I have */ + { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX12_FM, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX20_FM, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, 0 }, { } }; diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index d61df5c..6b10ebe 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -14,7 +14,7 @@ #include #include #define DRIVER_NAME "tifm_core" -#define DRIVER_VERSION "0.6" +#define DRIVER_VERSION "0.7" static DEFINE_IDR(tifm_adapter_idr); static DEFINE_SPINLOCK(tifm_adapter_lock); @@ -60,10 +60,41 @@ static int tifm_uevent(struct device *de return 0; } +#ifdef CONFIG_PM + +static int tifm_device_suspend(struct device *dev, pm_message_t state) +{ + struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); + struct tifm_driver *drv = fm_dev->drv; + + if (drv && drv->suspend) + return drv->suspend(fm_dev, state); + return 0; +} + +static int tifm_device_resume(struct device *dev) +{ + struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); + struct tifm_driver *drv = fm_dev->drv; + + if (drv && drv->resume) + return drv->resume(fm_dev); + return 0; +} + +#else + +#define tifm_device_suspend NULL +#define tifm_device_resume NULL + +#endif /* CONFIG_PM */ + static struct bus_type tifm_bus_type = { .name = "tifm", .match = tifm_match, .uevent = tifm_uevent, + .suspend = tifm_device_suspend, + .resume = tifm_device_resume }; static void tifm_free(struct class_device *cdev) @@ -71,8 +102,6 @@ static void tifm_free(struct class_devic struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); kfree(fm->sockets); - if (fm->wq) - destroy_workqueue(fm->wq); kfree(fm); } @@ -101,7 +130,8 @@ void tifm_free_adapter(struct tifm_adapt } EXPORT_SYMBOL(tifm_free_adapter); -int tifm_add_adapter(struct tifm_adapter *fm) +int tifm_add_adapter(struct tifm_adapter *fm, + int (*mediathreadfn)(void *data)) { int rc; @@ -113,10 +143,10 @@ int tifm_add_adapter(struct tifm_adapter spin_unlock(&tifm_adapter_lock); if (!rc) { snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); - strncpy(fm->wq_name, fm->cdev.class_id, KOBJ_NAME_LEN); + fm->media_switcher = kthread_create(mediathreadfn, + fm, "tifm/%u", fm->id); - fm->wq = create_singlethread_workqueue(fm->wq_name); - if (fm->wq) + if (!IS_ERR(fm->media_switcher)) return class_device_add(&fm->cdev); spin_lock(&tifm_adapter_lock); @@ -141,27 +171,27 @@ EXPORT_SYMBOL(tifm_remove_adapter); void tifm_free_device(struct device *dev) { struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - if (fm_dev->wq) - destroy_workqueue(fm_dev->wq); kfree(fm_dev); } EXPORT_SYMBOL(tifm_free_device); -struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id) +static void tifm_dummy_signal_irq(struct tifm_dev *sock, + unsigned int sock_irq_status) +{ + return; +} + +struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) { struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); if (dev) { spin_lock_init(&dev->lock); - snprintf(dev->wq_name, KOBJ_NAME_LEN, "tifm%u:%u", fm->id, id); - dev->wq = create_singlethread_workqueue(dev->wq_name); - if (!dev->wq) { - kfree(dev); - return NULL; - } + dev->dev.parent = fm->dev; dev->dev.bus = &tifm_bus_type; dev->dev.release = tifm_free_device; + dev->signal_irq = tifm_dummy_signal_irq; } return dev; } @@ -219,6 +249,7 @@ static int tifm_device_remove(struct dev struct tifm_driver *drv = fm_dev->drv; if (drv) { + fm_dev->signal_irq = tifm_dummy_signal_irq; if (drv->remove) drv->remove(fm_dev); fm_dev->drv = NULL; @@ -233,6 +264,8 @@ int tifm_register_driver(struct tifm_dri drv->driver.bus = &tifm_bus_type; drv->driver.probe = tifm_device_probe; drv->driver.remove = tifm_device_remove; + drv->driver.suspend = tifm_device_suspend; + drv->driver.resume = tifm_device_resume; return driver_register(&drv->driver); } diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 4224686..47c1afc 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -19,6 +19,19 @@ config MMC_DEBUG This is an option for use by developers; most people should say N here. This enables MMC core and driver debugging. +config MMC_PASSWORDS + boolean "MMC card lock/unlock passwords (EXPERIMENTAL)" + depends on MMC && EXPERIMENTAL + select KEYS + help + Say Y here to enable the use of passwords to lock and unlock + MMC cards. This uses the access key retention support, using + request_key to look up the key associated with each card. + + For example, if you have an MMC card that was locked using + Symbian OS on your cell phone, you won't be able to read it + on Linux without this support. + config MMC_BLOCK tristate "MMC block device driver" depends on MMC && BLOCK diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index aa152f3..2ce50f3 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -823,6 +823,9 @@ static int __init at91_mci_probe(struct mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_BYTEBLOCK; + mmc->max_blk_size = 4095; + mmc->max_blk_count = mmc->max_req_size; + host = mmc_priv(mmc); host->mmc = mmc; host->buffer = NULL; diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 800527c..7e36060 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -914,6 +914,9 @@ static int __devinit au1xmmc_probe(struc mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; + mmc->max_blk_size = 2048; + mmc->max_blk_count = 512; + mmc->ocr_avail = AU1XMMC_OCR; host = mmc_priv(mmc); diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index bfb9ff6..b060d4b 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -958,8 +958,10 @@ static int imxmci_probe(struct platform_ /* MMC core transfer sizes tunable parameters */ mmc->max_hw_segs = 64; mmc->max_phys_segs = 64; - mmc->max_sectors = 64; /* default 1 << (PAGE_CACHE_SHIFT - 9) */ mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */ + mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */ + mmc->max_blk_size = 2048; + mmc->max_blk_count = 65535; host = mmc_priv(mmc); host->mmc = mmc; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6f2a282..99d723a 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -5,6 +5,8 @@ * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. * SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved. * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. + * MMC password protection (C) 2006 Instituto Nokia de Tecnologia (INdT), + * 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 version 2 as @@ -20,6 +22,7 @@ #include #include #include #include +#include #include #include @@ -103,11 +106,16 @@ mmc_start_request(struct mmc_host *host, mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); - WARN_ON(host->card_busy == NULL); + WARN_ON(!host->claimed); mrq->cmd->error = 0; mrq->cmd->mrq = mrq; if (mrq->data) { + BUG_ON(mrq->data->blksz > host->max_blk_size); + BUG_ON(mrq->data->blocks > host->max_blk_count); + BUG_ON(mrq->data->blocks * mrq->data->blksz > + host->max_req_size); + mrq->cmd->data = mrq->data; mrq->data->error = 0; mrq->data->mrq = mrq; @@ -157,7 +165,7 @@ int mmc_wait_for_cmd(struct mmc_host *ho { struct mmc_request mrq; - BUG_ON(host->card_busy == NULL); + BUG_ON(!host->claimed); memset(&mrq, 0, sizeof(struct mmc_request)); @@ -195,7 +203,7 @@ int mmc_wait_for_app_cmd(struct mmc_host int i, err; - BUG_ON(host->card_busy == NULL); + BUG_ON(!host->claimed); BUG_ON(retries < 0); err = MMC_ERR_INVALID; @@ -289,7 +297,10 @@ void mmc_set_data_timeout(struct mmc_dat else limit_us = 100000; - if (timeout_us > limit_us) { + /* + * SDHC cards always use these fixed values. + */ + if (timeout_us > limit_us || mmc_card_blockaddr(card)) { data->timeout_ns = limit_us * 1000; data->timeout_clks = 0; } @@ -320,14 +331,14 @@ int __mmc_claim_host(struct mmc_host *ho spin_lock_irqsave(&host->lock, flags); while (1) { set_current_state(TASK_UNINTERRUPTIBLE); - if (host->card_busy == NULL) + if (!host->claimed) break; spin_unlock_irqrestore(&host->lock, flags); schedule(); spin_lock_irqsave(&host->lock, flags); } set_current_state(TASK_RUNNING); - host->card_busy = card; + host->claimed = 1; spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); @@ -353,10 +364,10 @@ void mmc_release_host(struct mmc_host *h { unsigned long flags; - BUG_ON(host->card_busy == NULL); + BUG_ON(!host->claimed); spin_lock_irqsave(&host->lock, flags); - host->card_busy = NULL; + host->claimed = 0; spin_unlock_irqrestore(&host->lock, flags); wake_up(&host->wq); @@ -372,7 +383,7 @@ static inline void mmc_set_ios(struct mm mmc_hostname(host), ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, ios->vdd, ios->bus_width); - + host->ops->set_ios(host, ios); } @@ -381,7 +392,7 @@ static int mmc_select_card(struct mmc_ho int err; struct mmc_command cmd; - BUG_ON(host->card_busy == NULL); + BUG_ON(!host->claimed); if (host->card_selected == card) return MMC_ERR_NONE; @@ -588,34 +599,65 @@ static void mmc_decode_csd(struct mmc_ca if (mmc_card_sd(card)) { csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 0) { + + switch (csd_struct) { + case 0: + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); + csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); + csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); + csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + break; + case 1: + /* + * This is a block-addressed SDHC card. Most + * interesting fields are unused and have fixed + * values. To avoid getting tripped by buggy cards, + * we assume those fixed values ourselves. + */ + mmc_card_set_blockaddr(card); + + csd->tacc_ns = 0; /* Unused */ + csd->tacc_clks = 0; /* Unused */ + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + m = UNSTUFF_BITS(resp, 48, 22); + csd->capacity = (1 + m) << 10; + + csd->read_blkbits = 9; + csd->read_partial = 0; + csd->write_misalign = 0; + csd->read_misalign = 0; + csd->r2w_factor = 4; /* Unused */ + csd->write_blkbits = 9; + csd->write_partial = 0; + break; + default: printk("%s: unrecognised CSD structure version %d\n", mmc_hostname(card->host), csd_struct); mmc_card_set_bad(card); return; } - - m = UNSTUFF_BITS(resp, 115, 4); - e = UNSTUFF_BITS(resp, 112, 3); - csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; - csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; - - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); - csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - - e = UNSTUFF_BITS(resp, 47, 3); - m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); - - csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - csd->read_partial = UNSTUFF_BITS(resp, 79, 1); - csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); - csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); - csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); - csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); - csd->write_partial = UNSTUFF_BITS(resp, 21, 1); } else { /* * We only understand CSD structure v1.1 and v1.2. @@ -848,6 +890,41 @@ static int mmc_send_app_op_cond(struct m return err; } +static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2) +{ + struct mmc_command cmd; + int err, sd2; + static const u8 test_pattern = 0xAA; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND + * before SD_APP_OP_COND. This command will harmlessly fail for + * SD 1.0 cards. + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; + cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err == MMC_ERR_NONE) { + if ((cmd.resp[0] & 0xFF) == test_pattern) { + sd2 = 1; + } else { + sd2 = 0; + err = MMC_ERR_FAILED; + } + } else { + /* + * Treat errors as SD 1.0 card. + */ + sd2 = 0; + err = MMC_ERR_NONE; + } + if (rsd2) + *rsd2 = sd2; + return err; +} + /* * Discover cards by requesting their CID. If this command * times out, it is not an error; there are no further cards @@ -890,6 +967,7 @@ static void mmc_discover_cards(struct mm } card->state &= ~MMC_STATE_DEAD; + mmc_card_clear_locked(card); if (host->mode == MMC_MODE_SD) { mmc_card_set_sd(card); @@ -922,6 +1000,9 @@ static void mmc_discover_cards(struct mm err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) mmc_card_set_dead(card); + + if (cmd.resp[0] & R1_CARD_IS_LOCKED) + mmc_card_set_locked(card); } } } @@ -1018,7 +1099,8 @@ static void mmc_process_ext_csds(struct mmc_wait_for_req(host, &mrq); if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - mmc_card_set_dead(card); + printk("%s: unable to read EXT_CSD, performance " + "might suffer.\n", mmc_hostname(card->host)); continue; } @@ -1034,7 +1116,6 @@ static void mmc_process_ext_csds(struct printk("%s: card is mmc v4 but doesn't support " "any high-speed modes.\n", mmc_hostname(card->host)); - mmc_card_set_bad(card); continue; } @@ -1215,7 +1296,9 @@ static void mmc_read_switch_caps(struct mmc_wait_for_req(host, &mrq); if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - mmc_card_set_dead(card); + printk("%s: unable to read switch capabilities, " + "performance might suffer.\n", + mmc_hostname(card->host)); continue; } @@ -1247,12 +1330,8 @@ static void mmc_read_switch_caps(struct mmc_wait_for_req(host, &mrq); - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - mmc_card_set_dead(card); - continue; - } - - if ((status[16] & 0xF) != 1) { + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE || + (status[16] & 0xF) != 1) { printk(KERN_WARNING "%s: Problem switching card " "into high-speed mode!\n", mmc_hostname(host)); @@ -1334,6 +1413,10 @@ static void mmc_setup(struct mmc_host *h mmc_power_up(host); mmc_idle_cards(host); + err = mmc_send_if_cond(host, host->ocr_avail, NULL); + if (err != MMC_ERR_NONE) { + return; + } err = mmc_send_app_op_cond(host, 0, &ocr); /* @@ -1386,10 +1469,21 @@ static void mmc_setup(struct mmc_host *h * all get the idea that they should be ready for CMD2. * (My SanDisk card seems to need this.) */ - if (host->mode == MMC_MODE_SD) - mmc_send_app_op_cond(host, host->ocr, NULL); - else + if (host->mode == MMC_MODE_SD) { + int err, sd2; + err = mmc_send_if_cond(host, host->ocr, &sd2); + if (err == MMC_ERR_NONE) { + /* + * If SD_SEND_IF_COND indicates an SD 2.0 + * compliant card and we should set bit 30 + * of the ocr to indicate that we can handle + * block-addressed SDHC cards. + */ + mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL); + } + } else { mmc_send_op_cond(host, host->ocr, NULL); + } mmc_discover_cards(host); @@ -1408,6 +1502,123 @@ static void mmc_setup(struct mmc_host *h mmc_process_ext_csds(host); } +#ifdef CONFIG_MMC_PASSWORDS + +/** + * mmc_lock_unlock - send LOCK_UNLOCK command to a specific card. + * @card: card to which the LOCK_UNLOCK command should be sent + * @key: key containing the MMC password + * @mode: LOCK_UNLOCK mode + * + */ +int mmc_lock_unlock(struct mmc_card *card, struct key *key, int mode) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + struct mmc_key_payload *mpayload; + unsigned long erase_timeout; + int err, data_size; + u8 *data_buf; + + mpayload = NULL; + data_size = 1; + if (!(mode & MMC_LOCK_MODE_ERASE)) { + mpayload = rcu_dereference(key->payload.data); + data_size = 2 + mpayload->datalen; + } + + data_buf = kmalloc(data_size, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + memset(data_buf, 0, data_size); + + data_buf[0] |= mode; + if (mode & MMC_LOCK_MODE_UNLOCK) + data_buf[0] &= ~MMC_LOCK_MODE_UNLOCK; + + if (!(mode & MMC_LOCK_MODE_ERASE)) { + data_buf[1] = mpayload->datalen; + memcpy(data_buf + 2, mpayload->data, mpayload->datalen); + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = data_size; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + goto out; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_LOCK_UNLOCK; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1B | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, card, 1); + + data.blksz = data_size; + data.blocks = 1; + data.flags = MMC_DATA_WRITE; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, data_buf, data_size); + err = mmc_wait_for_req(card->host, &mrq); + if (err != MMC_ERR_NONE) + goto out; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + /* set timeout for forced erase operation to 3 min. (see MMC spec) */ + erase_timeout = jiffies + 180 * HZ; + do { + /* we cannot use "retries" here because the + * R1_LOCK_UNLOCK_FAILED bit is cleared by subsequent reads to + * the status register, hiding the error condition */ + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err != MMC_ERR_NONE) + break; + /* the other modes don't need timeout checking */ + if (!(mode & MMC_LOCK_MODE_ERASE)) + continue; + if (time_after(jiffies, erase_timeout)) { + dev_dbg(&card->dev, "forced erase timed out\n"); + err = MMC_ERR_TIMEOUT; + break; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + if (cmd.resp[0] & R1_LOCK_UNLOCK_FAILED) { + dev_dbg(&card->dev, "LOCK_UNLOCK operation failed\n"); + err = MMC_ERR_FAILED; + } + + if (cmd.resp[0] & R1_CARD_IS_LOCKED) + mmc_card_set_locked(card); + else + mmc_card_clear_locked(card); + +out: + kfree(data_buf); + + return err; +} + +#endif /* CONFIG_MMC_PASSWORDS */ /** * mmc_detect_change - process change of state on a MMC socket @@ -1519,8 +1730,11 @@ struct mmc_host *mmc_alloc_host(int extr */ host->max_hw_segs = 1; host->max_phys_segs = 1; - host->max_sectors = 1 << (PAGE_CACHE_SHIFT - 9); host->max_seg_size = PAGE_CACHE_SIZE; + + host->max_req_size = PAGE_CACHE_SIZE; + host->max_blk_size = 512; + host->max_blk_count = PAGE_CACHE_SIZE / 512; } return host; diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h index 149affe..b72385b 100644 --- a/drivers/mmc/mmc.h +++ b/drivers/mmc/mmc.h @@ -19,6 +19,17 @@ int mmc_add_host_sysfs(struct mmc_host * void mmc_remove_host_sysfs(struct mmc_host *host); void mmc_free_host_sysfs(struct mmc_host *host); +/* core-internal data */ +struct mmc_key_payload { + struct rcu_head rcu; /* RCU destructor */ + unsigned short datalen; /* length of this data */ + char data[0]; /* actual data */ +}; + +struct key; + +extern int mmc_lock_unlock(struct mmc_card *card, struct key *key, int mode); + int mmc_schedule_work(struct work_struct *work); int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); void mmc_flush_scheduled_work(void); diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 8771357..19ccfed 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -237,13 +237,17 @@ static int mmc_blk_issue_rq(struct mmc_q brq.mrq.cmd = &brq.cmd; brq.mrq.data = &brq.data; - brq.cmd.arg = req->sector << 9; + brq.cmd.arg = req->sector; + if (!mmc_card_blockaddr(card)) + brq.cmd.arg <<= 9; brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; brq.data.blksz = 1 << md->block_bits; - brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.stop.opcode = MMC_STOP_TRANSMISSION; brq.stop.arg = 0; brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); + if (brq.data.blocks > card->host->max_blk_count) + brq.data.blocks = card->host->max_blk_count; mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); @@ -494,6 +498,10 @@ mmc_blk_set_blksize(struct mmc_blk_data struct mmc_command cmd; int err; + /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ + if (mmc_card_blockaddr(card)) + return 0; + mmc_card_claim_host(card); cmd.opcode = MMC_SET_BLOCKLEN; cmd.arg = 1 << md->block_bits; diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c index 3e35a43..c27e426 100644 --- a/drivers/mmc/mmc_queue.c +++ b/drivers/mmc/mmc_queue.c @@ -147,7 +147,7 @@ int mmc_init_queue(struct mmc_queue *mq, blk_queue_prep_rq(mq->queue, mmc_prep_request); blk_queue_bounce_limit(mq->queue, limit); - blk_queue_max_sectors(mq->queue, host->max_sectors); + blk_queue_max_sectors(mq->queue, host->max_req_size / 512); blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); blk_queue_max_segment_size(mq->queue, host->max_seg_size); diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index e334acd..16386dd 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -2,6 +2,8 @@ * linux/drivers/mmc/mmc_sysfs.c * * Copyright (C) 2003 Russell King, All Rights Reserved. + * MMC password protection (C) 2006 Instituto Nokia de Tecnologia (INdT), + * 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 version 2 as @@ -14,9 +16,12 @@ #include #include #include #include +#include +#include #include #include +#include #include "mmc.h" @@ -61,6 +66,108 @@ static struct device_attribute mmc_dev_a static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr); +static struct key_type mmc_key_type; + +#ifdef CONFIG_MMC_PASSWORDS + +static ssize_t +mmc_lockable_show(struct device *dev, struct device_attribute *att, char *buf) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + + if (!mmc_card_lockable(card)) + return sprintf(buf, "unsupported\n"); + else + return sprintf(buf, "%slocked\n", mmc_card_locked(card) ? + "" : "un"); +} + +/* + * implement MMC password functions: force erase, remove password, change + * password, unlock card and assign password. + */ +static ssize_t +mmc_lockable_store(struct device *dev, struct device_attribute *att, + const char *data, size_t len) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + int err; + struct key *mmc_key; + + err = mmc_card_claim_host(card); + if (err != MMC_ERR_NONE) + goto error; + + err = mmc_card_lockable(card); + if (!err) + goto error; + + if (mmc_card_locked(card) && !strncmp(data, "erase", 5)) { + /* forced erase only works while card is locked */ + mmc_lock_unlock(card, NULL, MMC_LOCK_MODE_ERASE); + goto out; + } else if (!mmc_card_locked(card) && !strncmp(data, "remove", 6)) { + /* remove password only works while card is unlocked */ + mmc_key = request_key(&mmc_key_type, "mmc:key", "remove"); + + if (!IS_ERR(mmc_key)) { + err = mmc_lock_unlock(card, mmc_key, MMC_LOCK_MODE_CLR_PWD); + if (!err) + goto out; + } else + goto request_key_error; + } else if (!mmc_card_locked(card) && ((!strncmp(data, "assign", 6)) || + (!strncmp(data, "change", 6)))) { + + /* assign or change */ + if(!strncmp(data, "assign", 6)) + mmc_key = request_key(&mmc_key_type, "mmc:key", "assign"); + else + mmc_key = request_key(&mmc_key_type, "mmc:key", "change"); + + if (!IS_ERR(mmc_key)) { + err = mmc_lock_unlock(card, mmc_key, MMC_LOCK_MODE_SET_PWD); + if (!err) + goto out; + } else + goto request_key_error; + } else if (mmc_card_locked(card) && !strncmp(data, "unlock", 6)) { + /* unlock */ + mmc_key = request_key(&mmc_key_type, "mmc:key", "unlock"); + if (!IS_ERR(mmc_key)) { + err = mmc_lock_unlock(card, mmc_key, MMC_LOCK_MODE_UNLOCK); + if (err) { + dev_dbg(&card->dev, "Wrong password\n"); + goto error; + } + else { + mmc_card_release_host(card); + goto out_unlocked; + } + } else + goto request_key_error; + } + +request_key_error: + dev_dbg(&card->dev, "request_key returned error %ld\n", PTR_ERR(mmc_key)); +error: + mmc_card_release_host(card); + return -EINVAL; +out: + mmc_card_release_host(card); + return len; + +out_unlocked: + device_release_driver(dev); + device_attach(dev); + return len; +} + +static struct device_attribute mmc_dev_attr_lockable = + __ATTR(lockable, S_IWUSR | S_IRUGO, + mmc_lockable_show, mmc_lockable_store); + +#endif static void mmc_release_card(struct device *dev) { @@ -73,10 +180,19 @@ static void mmc_release_card(struct devi * This currently matches any MMC driver to any MMC card - drivers * themselves make the decision whether to drive this card in their * probe method. However, we force "bad" cards to fail. + * + * We also fail for all locked cards; drivers expect to be able to do block + * I/O still on probe(), which is not possible while the card is locked. + * Device probing must be triggered sometime later to make the card available + * to the block driver. */ static int mmc_bus_match(struct device *dev, struct device_driver *drv) { struct mmc_card *card = dev_to_mmc_card(dev); + if (mmc_card_lockable(card) && mmc_card_locked(card)) { + dev_dbg(&card->dev, "card is locked; binding is deferred\n"); + return 0; + } return !mmc_card_bad(card); } @@ -199,7 +315,7 @@ void mmc_init_card(struct mmc_card *card memset(card, 0, sizeof(struct mmc_card)); card->host = host; device_initialize(&card->dev); - card->dev.parent = mmc_dev(host); + card->dev.parent = mmc_classdev(host); card->dev.bus = &mmc_bus_type; card->dev.release = mmc_release_card; } @@ -221,6 +337,11 @@ int mmc_register_card(struct mmc_card *c if (ret) device_del(&card->dev); } +#ifdef CONFIG_MMC_PASSWORDS + ret = device_create_file(&card->dev, &mmc_dev_attr_lockable); + if (ret) + device_del(&card->dev); +#endif } return ret; } @@ -235,6 +356,9 @@ void mmc_remove_card(struct mmc_card *ca if (mmc_card_sd(card)) device_remove_file(&card->dev, &mmc_dev_attr_scr); +#ifdef CONFIG_MMC_PASSWORDS + device_remove_file(&card->dev, &mmc_dev_attr_lockable); +#endif device_del(&card->dev); } @@ -256,6 +380,70 @@ static struct class mmc_host_class = { static DEFINE_IDR(mmc_host_idr); static DEFINE_SPINLOCK(mmc_host_lock); +#ifdef CONFIG_MMC_PASSWORDS + +#define MMC_KEYLEN_MAXBYTES 32 + +static int mmc_key_instantiate(struct key *key, const void *data, size_t datalen) +{ + struct mmc_key_payload *mpayload; + int ret; + + ret = -EINVAL; + if (datalen <= 0 || datalen > MMC_KEYLEN_MAXBYTES || !data) { + pr_debug("Invalid data\n"); + goto error; + } + + ret = key_payload_reserve(key, datalen); + if (ret < 0) { + pr_debug("ret = %d\n", ret); + goto error; + } + + ret = -ENOMEM; + mpayload = kmalloc(sizeof(*mpayload) + datalen, GFP_KERNEL); + if (!mpayload) { + pr_debug("Unable to allocate mpayload structure\n"); + goto error; + } + mpayload->datalen = datalen; + memcpy(mpayload->data, data, datalen); + + rcu_assign_pointer(key->payload.data, mpayload); + + /* ret = 0 if there is no error */ + ret = 0; + +error: + return ret; +} + +static int mmc_key_match(const struct key *key, const void *description) +{ + return strcmp(key->description, description) == 0; +} + +/* + * dispose of the data dangling from the corpse of a mmc key + */ +static void mmc_key_destroy(struct key *key) +{ + struct mmc_key_payload *mpayload = key->payload.data; + + kfree(mpayload); +} + +static struct key_type mmc_key_type = { + .name = "mmc", + .def_datalen = MMC_KEYLEN_MAXBYTES, + .instantiate = mmc_key_instantiate, + .match = mmc_key_match, + .destroy = mmc_key_destroy, +}; + +#endif + /* * Internal function. Allocate a new MMC host. */ @@ -345,16 +533,31 @@ static int __init mmc_init(void) return -ENOMEM; ret = bus_register(&mmc_bus_type); - if (ret == 0) { - ret = class_register(&mmc_host_class); - if (ret) - bus_unregister(&mmc_bus_type); + if (ret) + goto error_bus; + ret = class_register(&mmc_host_class); + if (ret) + goto error_class; +#ifdef CONFIG_MMC_PASSWORDS + ret = register_key_type(&mmc_key_type); + if (ret) { + class_unregister(&mmc_host_class); + goto error_class; } +#endif + return 0; + +error_class: + bus_unregister(&mmc_bus_type); +error_bus: return ret; } static void __exit mmc_exit(void) { +#ifdef CONFIG_MMC_PASSWORDS + unregister_key_type(&mmc_key_type); +#endif class_unregister(&mmc_host_class); bus_unregister(&mmc_bus_type); destroy_workqueue(workqueue); diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c index ccfe656..5941dd9 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -524,15 +524,24 @@ static int mmci_probe(struct amba_device /* * Since we only have a 16-bit data length register, we must * ensure that we don't exceed 2^16-1 bytes in a single request. - * Choose 64 (512-byte) sectors as the limit. */ - mmc->max_sectors = 64; + mmc->max_req_size = 65535; /* * Set the maximum segment size. Since we aren't doing DMA * (yet) we are only limited by the data length register. */ - mmc->max_seg_size = mmc->max_sectors << 9; + mmc->max_seg_size = mmc->max_req_size; + + /* + * Block size can be up to 2048 bytes, but must be a power of two. + */ + mmc->max_blk_size = 2048; + + /* + * No limit on the number of blocks transferred. + */ + mmc->max_blk_count = mmc->max_req_size; spin_lock_init(&host->lock); diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c index d30540b..9c9fead 100644 --- a/drivers/mmc/omap.c +++ b/drivers/mmc/omap.c @@ -203,12 +203,18 @@ mmc_omap_start_command(struct mmc_omap_h cmdtype = 0; /* Our hardware needs to know exact type */ +<<<<<<< HEAD/drivers/mmc/omap.c switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: break; case MMC_RSP_R1: case MMC_RSP_R1B: /* resp 1, 1b, 6, 7 */ +======= + switch (RSP_TYPE(mmc_resp_type(cmd))) { + case RSP_TYPE(MMC_RSP_R1): + /* resp 1, 1b, 6, 7 */ +>>>>>>> /drivers/mmc/omap.c resptype = 1; break; case MMC_RSP_R2: @@ -1099,8 +1105,10 @@ static int __init mmc_omap_probe(struct */ mmc->max_phys_segs = 32; mmc->max_hw_segs = 32; - mmc->max_sectors = 256; /* NBLK max 11-bits, OMAP also limited by DMA */ - mmc->max_seg_size = mmc->max_sectors * 512; + 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; if (host->power_pin >= 0) { if ((ret = omap_request_gpio(host->power_pin)) != 0) { diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index 6073d99..9774fc6 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -450,6 +450,16 @@ static int pxamci_probe(struct platform_ */ mmc->max_seg_size = PAGE_SIZE; + /* + * Block length register is 10 bits. + */ + mmc->max_blk_size = 1023; + + /* + * Block count register is 16 bits. + */ + mmc->max_blk_count = 65535; + host = mmc_priv(mmc); host->mmc = mmc; host->dma = -1; diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index c2d13d7..80f7ecb 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -372,7 +372,7 @@ static void sdhci_prepare_data(struct sd /* Sanity checks */ BUG_ON(data->blksz * data->blocks > 524288); - BUG_ON(data->blksz > host->max_block); + BUG_ON(data->blksz > host->mmc->max_blk_size); BUG_ON(data->blocks > 65535); /* timeout in us */ @@ -1274,15 +1274,6 @@ static int __devinit sdhci_probe_slot(st if (caps & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; - host->max_block = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; - if (host->max_block >= 3) { - printk(KERN_ERR "%s: Invalid maximum block size.\n", - host->slot_descr); - ret = -ENODEV; - goto unmap; - } - host->max_block = 512 << host->max_block; - /* * Set host parameters. */ @@ -1326,15 +1317,33 @@ static int __devinit sdhci_probe_slot(st /* * Maximum number of sectors in one transfer. Limited by DMA boundary - * size (512KiB), which means (512 KiB/512=) 1024 entries. + * size (512KiB). */ - mmc->max_sectors = 1024; + mmc->max_req_size = 524288; /* * Maximum segment size. Could be one segment with the maximum number - * of sectors. + * of bytes. + */ + mmc->max_seg_size = mmc->max_req_size; + + /* + * Maximum block size. This varies from controller to controller and + * is specified in the capabilities register. + */ + mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; + if (mmc->max_blk_size >= 3) { + printk(KERN_ERR "%s: Invalid maximum block size.\n", + host->slot_descr); + ret = -ENODEV; + goto unmap; + } + mmc->max_blk_size = 512 << mmc->max_blk_size; + + /* + * Maximum block count. */ - mmc->max_seg_size = mmc->max_sectors * 512; + mmc->max_blk_count = 65535; /* * Init tasklets. diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index f9d1a0a..bc6bf7e 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -174,7 +174,6 @@ #define SDHCI_USE_DMA (1<<0) unsigned int max_clk; /* Max possible freq (MHz) */ unsigned int timeout_clk; /* Timeout freq (KHz) */ - unsigned int max_block; /* Max block size (bytes) */ unsigned int clock; /* Current clock (MHz) */ unsigned short power; /* Current voltage */ diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index fa4a528..7e607b7 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -17,7 +17,7 @@ #include #include #define DRIVER_NAME "tifm_sd" -#define DRIVER_VERSION "0.6" +#define DRIVER_VERSION "0.7" static int no_dma = 0; static int fixed_timeout = 0; @@ -79,7 +79,6 @@ typedef enum { enum { FIFO_RDY = 0x0001, /* hardware dependent value */ - HOST_REG = 0x0002, EJECT = 0x0004, EJECT_DONE = 0x0008, CARD_BUSY = 0x0010, @@ -95,46 +94,59 @@ struct tifm_sd { card_state_t state; unsigned int clk_freq; unsigned int clk_div; - unsigned long timeout_jiffies; // software timeout - 2 sec + unsigned long timeout_jiffies; + struct tasklet_struct finish_tasklet; + struct timer_list timer; struct mmc_request *req; - struct work_struct cmd_handler; - struct delayed_work abort_handler; - wait_queue_head_t can_eject; + wait_queue_head_t notify; size_t written_blocks; - char *buffer; size_t buffer_size; size_t buffer_pos; }; +static char* tifm_sd_kmap_atomic(struct mmc_data *data) +{ + return kmap_atomic(data->sg->page, KM_BIO_SRC_IRQ) + data->sg->offset; +} + +static void tifm_sd_kunmap_atomic(char *buffer, struct mmc_data *data) +{ + kunmap_atomic(buffer - data->sg->offset, KM_BIO_SRC_IRQ); +} + static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, - unsigned int host_status) + unsigned int host_status) { struct mmc_command *cmd = host->req->cmd; unsigned int t_val = 0, cnt = 0; + char *buffer; if (host_status & TIFM_MMCSD_BRS) { /* in non-dma rx mode BRS fires when fifo is still not empty */ - if (host->buffer && (cmd->data->flags & MMC_DATA_READ)) { + if (no_dma && (cmd->data->flags & MMC_DATA_READ)) { + buffer = tifm_sd_kmap_atomic(host->req->data); while (host->buffer_size > host->buffer_pos) { t_val = readl(sock->addr + SOCK_MMCSD_DATA); - host->buffer[host->buffer_pos++] = t_val & 0xff; - host->buffer[host->buffer_pos++] = + buffer[host->buffer_pos++] = t_val & 0xff; + buffer[host->buffer_pos++] = (t_val >> 8) & 0xff; } + tifm_sd_kunmap_atomic(buffer, host->req->data); } return 1; - } else if (host->buffer) { + } else if (no_dma) { + buffer = tifm_sd_kmap_atomic(host->req->data); if ((cmd->data->flags & MMC_DATA_READ) && (host_status & TIFM_MMCSD_AF)) { for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { t_val = readl(sock->addr + SOCK_MMCSD_DATA); if (host->buffer_size > host->buffer_pos) { - host->buffer[host->buffer_pos++] = + buffer[host->buffer_pos++] = t_val & 0xff; - host->buffer[host->buffer_pos++] = + buffer[host->buffer_pos++] = (t_val >> 8) & 0xff; } } @@ -142,14 +154,16 @@ static int tifm_sd_transfer_data(struct && (host_status & TIFM_MMCSD_AE)) { for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { if (host->buffer_size > host->buffer_pos) { - t_val = host->buffer[host->buffer_pos++] & 0x00ff; - t_val |= ((host->buffer[host->buffer_pos++]) << 8) - & 0xff00; + t_val = buffer[host->buffer_pos++] + & 0x00ff; + t_val |= ((buffer[host->buffer_pos++]) + << 8) & 0xff00; writel(t_val, - sock->addr + SOCK_MMCSD_DATA); + sock->addr + SOCK_MMCSD_DATA); } } } + tifm_sd_kunmap_atomic(buffer, host->req->data); } return 0; } @@ -206,7 +220,7 @@ static void tifm_sd_exec(struct tifm_sd cmd_mask |= TIFM_MMCSD_READ; dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", - cmd->opcode, cmd->arg, cmd_mask); + cmd->opcode, cmd->arg, cmd_mask); writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); @@ -239,65 +253,78 @@ change_state: tifm_sd_fetch_resp(cmd, sock); if (cmd->data) { host->state = BRS; - } else + } else { host->state = READY; + } goto change_state; } break; case BRS: if (tifm_sd_transfer_data(sock, host, host_status)) { - if (!host->req->stop) { - if (cmd->data->flags & MMC_DATA_WRITE) { - host->state = CARD; + if (cmd->data->flags & MMC_DATA_WRITE) { + host->state = CARD; + } else { + if (no_dma) { + if (host->req->stop) { + tifm_sd_exec(host, host->req->stop); + host->state = SCMD; + } else { + host->state = READY; + } } else { - host->state = - host->buffer ? READY : FIFO; + host->state = FIFO; } - goto change_state; } - tifm_sd_exec(host, host->req->stop); - host->state = SCMD; + goto change_state; } break; case SCMD: if (host_status & TIFM_MMCSD_EOC) { tifm_sd_fetch_resp(host->req->stop, sock); - if (cmd->error) { - host->state = READY; - } else if (cmd->data->flags & MMC_DATA_WRITE) { - host->state = CARD; - } else { - host->state = host->buffer ? READY : FIFO; - } + host->state = READY; goto change_state; } break; case CARD: + dev_dbg(&sock->dev, "waiting for CARD, have %zd blocks\n", + host->written_blocks); if (!(host->flags & CARD_BUSY) && (host->written_blocks == cmd->data->blocks)) { - host->state = host->buffer ? READY : FIFO; + if (no_dma) { + if (host->req->stop) { + tifm_sd_exec(host, host->req->stop); + host->state = SCMD; + } else { + host->state = READY; + } + } else { + host->state = FIFO; + } goto change_state; } break; case FIFO: if (host->flags & FIFO_RDY) { - host->state = READY; host->flags &= ~FIFO_RDY; + if (host->req->stop) { + tifm_sd_exec(host, host->req->stop); + host->state = SCMD; + } else { + host->state = READY; + } goto change_state; } break; case READY: - queue_work(sock->wq, &host->cmd_handler); + tasklet_schedule(&host->finish_tasklet); return; } - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); } /* Called from interrupt handler */ -static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, - unsigned int sock_irq_status) +static void tifm_sd_signal_irq(struct tifm_dev *sock, + unsigned int sock_irq_status) { struct tifm_sd *host; unsigned int host_status = 0, fifo_status = 0; @@ -305,7 +332,6 @@ static unsigned int tifm_sd_signal_irq(s spin_lock(&sock->lock); host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); - cancel_delayed_work(&host->abort_handler); if (sock_irq_status & FIFO_EVENT) { fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); @@ -318,19 +344,17 @@ static unsigned int tifm_sd_signal_irq(s host_status = readl(sock->addr + SOCK_MMCSD_STATUS); writel(host_status, sock->addr + SOCK_MMCSD_STATUS); - if (!(host->flags & HOST_REG)) - queue_work(sock->wq, &host->cmd_handler); if (!host->req) goto done; if (host_status & TIFM_MMCSD_ERRMASK) { if (host_status & TIFM_MMCSD_CERR) error_code = MMC_ERR_FAILED; - else if (host_status & - (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) + else if (host_status + & (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) error_code = MMC_ERR_TIMEOUT; - else if (host_status & - (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) + else if (host_status + & (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) error_code = MMC_ERR_BADCRC; writel(TIFM_FIFO_INT_SETALL, @@ -340,12 +364,11 @@ static unsigned int tifm_sd_signal_irq(s if (host->req->stop) { if (host->state == SCMD) { host->req->stop->error = error_code; - } else if(host->state == BRS) { + } else if (host->state == BRS + || host->state == CARD + || host->state == FIFO) { host->req->cmd->error = error_code; tifm_sd_exec(host, host->req->stop); - queue_delayed_work(sock->wq, - &host->abort_handler, - host->timeout_jiffies); host->state = SCMD; goto done; } else { @@ -359,8 +382,8 @@ static unsigned int tifm_sd_signal_irq(s if (host_status & TIFM_MMCSD_CB) host->flags |= CARD_BUSY; - if ((host_status & TIFM_MMCSD_EOFB) && - (host->flags & CARD_BUSY)) { + if ((host_status & TIFM_MMCSD_EOFB) + && (host->flags & CARD_BUSY)) { host->written_blocks++; host->flags &= ~CARD_BUSY; } @@ -370,22 +393,22 @@ static unsigned int tifm_sd_signal_irq(s tifm_sd_process_cmd(sock, host, host_status); done: dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n", - host_status, fifo_status); + host_status, fifo_status); spin_unlock(&sock->lock); - return sock_irq_status; } -static void tifm_sd_prepare_data(struct tifm_sd *card, struct mmc_command *cmd) +static void tifm_sd_prepare_data(struct tifm_sd *host, struct mmc_command *cmd) { - struct tifm_dev *sock = card->dev; + struct tifm_dev *sock = host->dev; unsigned int dest_cnt; /* DMA style IO */ - + dev_dbg(&sock->dev, "setting dma for %d blocks\n", + cmd->data->blocks); writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(ilog2(cmd->data->blksz) - 2, - sock->addr + SOCK_FIFO_PAGE_SIZE); + sock->addr + SOCK_FIFO_PAGE_SIZE); writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); @@ -399,7 +422,7 @@ static void tifm_sd_prepare_data(struct if (cmd->data->flags & MMC_DATA_WRITE) { writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); writel(dest_cnt | TIFM_DMA_TX | TIFM_DMA_EN, - sock->addr + SOCK_DMA_CONTROL); + sock->addr + SOCK_DMA_CONTROL); } else { writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); writel(dest_cnt | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); @@ -407,7 +430,7 @@ static void tifm_sd_prepare_data(struct } static void tifm_sd_set_data_timeout(struct tifm_sd *host, - struct mmc_data *data) + struct mmc_data *data) { struct tifm_dev *sock = host->dev; unsigned int data_timeout = data->timeout_clks; @@ -416,22 +439,21 @@ static void tifm_sd_set_data_timeout(str return; data_timeout += data->timeout_ns / - ((1000000000 / host->clk_freq) * host->clk_div); - data_timeout *= 10; // call it fudge factor for now + ((1000000000UL / host->clk_freq) * host->clk_div); if (data_timeout < 0xffff) { - writel((~TIFM_MMCSD_DPE) & - readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), - sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + writel((~TIFM_MMCSD_DPE) + & readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); } else { - writel(TIFM_MMCSD_DPE | - readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), - sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); data_timeout = (data_timeout >> 10) + 1; - if(data_timeout > 0xffff) + if (data_timeout > 0xffff) data_timeout = 0; /* set to unlimited */ writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + writel(TIFM_MMCSD_DPE + | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); } } @@ -474,11 +496,10 @@ static void tifm_sd_request(struct mmc_h } host->req = mrq; + mod_timer(&host->timer, jiffies + host->timeout_jiffies); host->state = CMD; - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + sock->addr + SOCK_CONTROL); tifm_sd_exec(host, mrq->cmd); spin_unlock_irqrestore(&sock->lock, flags); return; @@ -493,9 +514,9 @@ err_out: mmc_request_done(mmc, mrq); } -static void tifm_sd_end_cmd(struct work_struct *work) +static void tifm_sd_end_cmd(unsigned long data) { - struct tifm_sd *host = container_of(work, struct tifm_sd, cmd_handler); + struct tifm_sd *host = (struct tifm_sd*)data; struct tifm_dev *sock = host->dev; struct mmc_host *mmc = tifm_get_drvdata(sock); struct mmc_request *mrq; @@ -504,6 +525,7 @@ static void tifm_sd_end_cmd(struct work_ spin_lock_irqsave(&sock->lock, flags); + del_timer(&host->timer); mrq = host->req; host->req = NULL; host->state = IDLE; @@ -517,8 +539,8 @@ static void tifm_sd_end_cmd(struct work_ r_data = mrq->cmd->data; if (r_data) { if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks * - r_data->blksz; + r_data->bytes_xfered = host->written_blocks + * r_data->blksz; } else { r_data->bytes_xfered = r_data->blocks - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; @@ -532,7 +554,7 @@ static void tifm_sd_end_cmd(struct work_ } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + sock->addr + SOCK_CONTROL); spin_unlock_irqrestore(&sock->lock, flags); mmc_request_done(mmc, mrq); @@ -544,15 +566,6 @@ static void tifm_sd_request_nodma(struct struct tifm_dev *sock = host->dev; unsigned long flags; struct mmc_data *r_data = mrq->cmd->data; - char *t_buffer = NULL; - - if (r_data) { - t_buffer = kmap(r_data->sg->page); - if (!t_buffer) { - printk(KERN_ERR DRIVER_NAME ": kmap failed\n"); - goto err_out; - } - } spin_lock_irqsave(&sock->lock, flags); if (host->flags & EJECT) { @@ -569,15 +582,14 @@ static void tifm_sd_request_nodma(struct if (r_data) { tifm_sd_set_data_timeout(host, r_data); - host->buffer = t_buffer + r_data->sg->offset; - host->buffer_size = mrq->cmd->data->blocks * - mrq->cmd->data->blksz; + host->buffer_size = mrq->cmd->data->blocks + * mrq->cmd->data->blksz; - writel(TIFM_MMCSD_BUFINT | - readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + writel(TIFM_MMCSD_BUFINT + | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) | - (TIFM_MMCSD_FIFO_SIZE - 1), + writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) + | (TIFM_MMCSD_FIFO_SIZE - 1), sock->addr + SOCK_MMCSD_BUFFER_CONFIG); host->written_blocks = 0; @@ -588,26 +600,22 @@ static void tifm_sd_request_nodma(struct } host->req = mrq; + mod_timer(&host->timer, jiffies + host->timeout_jiffies); host->state = CMD; - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + sock->addr + SOCK_CONTROL); tifm_sd_exec(host, mrq->cmd); spin_unlock_irqrestore(&sock->lock, flags); return; err_out: - if (t_buffer) - kunmap(r_data->sg->page); - mrq->cmd->error = MMC_ERR_TIMEOUT; mmc_request_done(mmc, mrq); } -static void tifm_sd_end_cmd_nodma(struct work_struct *work) +static void tifm_sd_end_cmd_nodma(unsigned long data) { - struct tifm_sd *host = container_of(work, struct tifm_sd, cmd_handler); + struct tifm_sd *host = (struct tifm_sd*)data; struct tifm_dev *sock = host->dev; struct mmc_host *mmc = tifm_get_drvdata(sock); struct mmc_request *mrq; @@ -616,6 +624,7 @@ static void tifm_sd_end_cmd_nodma(struct spin_lock_irqsave(&sock->lock, flags); + del_timer(&host->timer); mrq = host->req; host->req = NULL; host->state = IDLE; @@ -633,8 +642,8 @@ static void tifm_sd_end_cmd_nodma(struct sock->addr + SOCK_MMCSD_INT_ENABLE); if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks * - r_data->blksz; + r_data->bytes_xfered = host->written_blocks + * r_data->blksz; } else { r_data->bytes_xfered = r_data->blocks - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; @@ -642,29 +651,44 @@ static void tifm_sd_end_cmd_nodma(struct r_data->bytes_xfered += r_data->blksz - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; } - host->buffer = NULL; host->buffer_pos = 0; host->buffer_size = 0; } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + sock->addr + SOCK_CONTROL); spin_unlock_irqrestore(&sock->lock, flags); - if (r_data) - kunmap(r_data->sg->page); - mmc_request_done(mmc, mrq); } -static void tifm_sd_abort(struct work_struct *work) +static void tifm_sd_terminate(struct tifm_sd *host) { - struct tifm_sd *host = - container_of(work, struct tifm_sd, abort_handler.work); + struct tifm_dev *sock = host->dev; + unsigned long flags; + + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + mmiowb(); + spin_lock_irqsave(&sock->lock, flags); + host->flags |= EJECT; + if (host->req) { + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + tasklet_schedule(&host->finish_tasklet); + } + spin_unlock_irqrestore(&sock->lock, flags); +} + +static void tifm_sd_abort(unsigned long data) +{ + struct tifm_sd *host = (struct tifm_sd*)data; printk(KERN_ERR DRIVER_NAME - ": card failed to respond for a long period of time"); + ": card failed to respond for a long period of time"); + + tifm_sd_terminate(host); tifm_eject(host->dev); } @@ -683,9 +707,9 @@ static void tifm_sd_ios(struct mmc_host writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), sock->addr + SOCK_MMCSD_CONFIG); } else { - writel((~TIFM_MMCSD_4BBUS) & - readl(sock->addr + SOCK_MMCSD_CONFIG), - sock->addr + SOCK_MMCSD_CONFIG); + writel((~TIFM_MMCSD_4BBUS) + & readl(sock->addr + SOCK_MMCSD_CONFIG), + sock->addr + SOCK_MMCSD_CONFIG); } if (ios->clock) { @@ -704,23 +728,24 @@ static void tifm_sd_ios(struct mmc_host if ((20000000 / clk_div1) > (24000000 / clk_div2)) { host->clk_freq = 20000000; host->clk_div = clk_div1; - writel((~TIFM_CTRL_FAST_CLK) & - readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + writel((~TIFM_CTRL_FAST_CLK) + & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); } else { host->clk_freq = 24000000; host->clk_div = clk_div2; - writel(TIFM_CTRL_FAST_CLK | - readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + writel(TIFM_CTRL_FAST_CLK + | readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); } } else { host->clk_div = 0; } host->clk_div &= TIFM_MMCSD_CLKMASK; - writel(host->clk_div | ((~TIFM_MMCSD_CLKMASK) & - readl(sock->addr + SOCK_MMCSD_CONFIG)), - sock->addr + SOCK_MMCSD_CONFIG); + writel(host->clk_div + | ((~TIFM_MMCSD_CLKMASK) + & readl(sock->addr + SOCK_MMCSD_CONFIG)), + sock->addr + SOCK_MMCSD_CONFIG); if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) host->flags |= OPENDRAIN; @@ -734,7 +759,7 @@ static void tifm_sd_ios(struct mmc_host // allow removal. if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) { host->flags |= EJECT_DONE; - wake_up_all(&host->can_eject); + wake_up_all(&host->notify); } spin_unlock_irqrestore(&sock->lock, flags); @@ -762,20 +787,67 @@ static struct mmc_host_ops tifm_sd_ops = .get_ro = tifm_sd_ro }; -static void tifm_sd_register_host(struct work_struct *work) +static int tifm_sd_initialize_host(struct tifm_sd *host) { - struct tifm_sd *host = container_of(work, struct tifm_sd, cmd_handler); + int rc; + unsigned int host_status = 0; struct tifm_dev *sock = host->dev; - struct mmc_host *mmc = tifm_get_drvdata(sock); - unsigned long flags; - spin_lock_irqsave(&sock->lock, flags); - host->flags |= HOST_REG; - PREPARE_WORK(&host->cmd_handler, - no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd); - spin_unlock_irqrestore(&sock->lock, flags); - dev_dbg(&sock->dev, "adding host\n"); - mmc_add_host(mmc); + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + mmiowb(); + host->clk_div = 61; + host->clk_freq = 20000000; + writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + + /* wait up to 0.51 sec for reset */ + for (rc = 2; rc <= 256; rc <<= 1) { + if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { + rc = 0; + break; + } + msleep(rc); + } + + if (rc) { + printk(KERN_ERR DRIVER_NAME + ": controller failed to reset\n"); + return -ENODEV; + } + + writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + // command timeout fixed to 64 clocks for now + writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); + writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); + + /* INAB should take much less than reset */ + for (rc = 1; rc <= 16; rc <<= 1) { + host_status = readl(sock->addr + SOCK_MMCSD_STATUS); + writel(host_status, sock->addr + SOCK_MMCSD_STATUS); + if (!(host_status & TIFM_MMCSD_ERRMASK) + && (host_status & TIFM_MMCSD_EOC)) { + rc = 0; + break; + } + msleep(rc); + } + + if (rc) { + printk(KERN_ERR DRIVER_NAME + ": card not ready - probe failed on initialization\n"); + return -ENODEV; + } + + writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, + sock->addr + SOCK_MMCSD_INT_ENABLE); + mmiowb(); + + return 0; } static int tifm_sd_probe(struct tifm_dev *sock) @@ -784,8 +856,8 @@ static int tifm_sd_probe(struct tifm_dev struct tifm_sd *host; int rc = -EIO; - if (!(TIFM_SOCK_STATE_OCCUPIED & - readl(sock->addr + SOCK_PRESENT_STATE))) { + if (!(TIFM_SOCK_STATE_OCCUPIED + & readl(sock->addr + SOCK_PRESENT_STATE))) { printk(KERN_WARNING DRIVER_NAME ": card gone, unexpectedly\n"); return rc; } @@ -795,109 +867,99 @@ static int tifm_sd_probe(struct tifm_dev return -ENOMEM; host = mmc_priv(mmc); - host->dev = sock; - host->clk_div = 61; - init_waitqueue_head(&host->can_eject); - INIT_WORK(&host->cmd_handler, tifm_sd_register_host); - INIT_DELAYED_WORK(&host->abort_handler, tifm_sd_abort); - tifm_set_drvdata(sock, mmc); - sock->signal_irq = tifm_sd_signal_irq; - - host->clk_freq = 20000000; + host->dev = sock; host->timeout_jiffies = msecs_to_jiffies(1000); + init_waitqueue_head(&host->notify); + tasklet_init(&host->finish_tasklet, + no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, + (unsigned long)host); + setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); + tifm_sd_ops.request = no_dma ? tifm_sd_request_nodma : tifm_sd_request; mmc->ops = &tifm_sd_ops; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; mmc->f_min = 20000000 / 60; mmc->f_max = 24000000; mmc->max_hw_segs = 1; mmc->max_phys_segs = 1; - mmc->max_sectors = 127; - mmc->max_seg_size = mmc->max_sectors << 11; //2k maximum hw block length - - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); + // limited by DMA counter - it's safer to stick with + // block counter has 11 bits though + mmc->max_blk_count = 256; + // 2k maximum hw block length + mmc->max_blk_size = 2048; + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; + sock->signal_irq = tifm_sd_signal_irq; + rc = tifm_sd_initialize_host(host); - for (rc = 0; rc < 50; rc++) { - /* Wait for reset ack */ - if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { - rc = 0; - break; - } - msleep(10); - } + if (!rc) + rc = mmc_add_host(mmc); + if (rc) + goto out_free_mmc; - if (rc) { - printk(KERN_ERR DRIVER_NAME - ": card not ready - probe failed\n"); - mmc_free_host(mmc); - return -ENODEV; - } + return 0; +out_free_mmc: + mmc_free_host(mmc); + return rc; +} - writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); - writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, - sock->addr + SOCK_MMCSD_INT_ENABLE); +static void tifm_sd_remove(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); - writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); // command timeout 64 clocks for now - writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); + del_timer_sync(&host->timer); + tifm_sd_terminate(host); + wait_event_timeout(host->notify, host->flags & EJECT_DONE, + host->timeout_jiffies); + tasklet_kill(&host->finish_tasklet); + mmc_remove_host(mmc); - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); + /* The meaning of the bit majority in this constant is unknown. */ + writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); - return 0; + tifm_set_drvdata(sock, NULL); + mmc_free_host(mmc); } -static int tifm_sd_host_is_down(struct tifm_dev *sock) +#ifdef CONFIG_PM + +static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) { struct mmc_host *mmc = tifm_get_drvdata(sock); - struct tifm_sd *host = mmc_priv(mmc); - unsigned long flags; - int rc = 0; + int rc; - spin_lock_irqsave(&sock->lock, flags); - rc = (host->flags & EJECT_DONE); - spin_unlock_irqrestore(&sock->lock, flags); + rc = mmc_suspend_host(mmc, state); + /* The meaning of the bit majority in this constant is unknown. */ + writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); return rc; } -static void tifm_sd_remove(struct tifm_dev *sock) +static int tifm_sd_resume(struct tifm_dev *sock) { struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); - unsigned long flags; - spin_lock_irqsave(&sock->lock, flags); - host->flags |= EJECT; - if (host->req) - queue_work(sock->wq, &host->cmd_handler); - spin_unlock_irqrestore(&sock->lock, flags); - wait_event_timeout(host->can_eject, tifm_sd_host_is_down(sock), - host->timeout_jiffies); + if (sock->media_id != FM_SD + || tifm_sd_initialize_host(host)) { + tifm_eject(sock); + return 0; + } else { + return mmc_resume_host(mmc); + } +} - if (host->flags & HOST_REG) - mmc_remove_host(mmc); +#else - /* The meaning of the bit majority in this constant is unknown. */ - writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); +#define tifm_sd_suspend NULL +#define tifm_sd_resume NULL - tifm_set_drvdata(sock, NULL); - mmc_free_host(mmc); -} +#endif /* CONFIG_PM */ static tifm_media_id tifm_sd_id_tbl[] = { FM_SD, 0 @@ -910,7 +972,9 @@ static struct tifm_driver tifm_sd_driver }, .id_table = tifm_sd_id_tbl, .probe = tifm_sd_probe, - .remove = tifm_sd_remove + .remove = tifm_sd_remove, + .suspend = tifm_sd_suspend, + .resume = tifm_sd_resume }; static int __init tifm_sd_init(void) diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 7a28267..c1dd6ad 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver * - * Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2004-2006 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 @@ -910,6 +910,45 @@ static void wbsd_request(struct mmc_host */ if (cmd->data && (cmd->error == MMC_ERR_NONE)) { /* + * The hardware is so delightfully stupid that it has a list + * of "data" commands. If a command isn't on this list, it'll + * just go back to the idle state and won't send any data + * interrupts. + */ + switch (cmd->opcode) { + case 11: + case 17: + case 18: + case 20: + case 24: + case 25: + case 26: + case 27: + case 30: + case 42: + case 56: + break; + + /* ACMDs. We don't keep track of state, so we just treat them + * like any other command. */ + case 51: + break; + + default: +#ifdef CONFIG_MMC_DEBUG + printk(KERN_WARNING "%s: Data command %d is not " + "supported by this controller.\n", + mmc_hostname(host->mmc), cmd->opcode); +#endif + cmd->data->error = MMC_ERR_INVALID; + + if (cmd->data->stop) + wbsd_send_command(host, cmd->data->stop); + + goto done; + }; + + /* * Dirty fix for hardware bug. */ if (host->dma == -1) @@ -1343,16 +1382,27 @@ static int __devinit wbsd_alloc_mmc(stru mmc->max_phys_segs = 128; /* - * Maximum number of sectors in one transfer. Also limited by 64kB - * buffer. + * Maximum request size. Also limited by 64KiB buffer. */ - mmc->max_sectors = 128; + mmc->max_req_size = 65536; /* * Maximum segment size. Could be one segment with the maximum number - * of segments. + * of bytes. + */ + mmc->max_seg_size = mmc->max_req_size; + + /* + * Maximum block size. We have 12 bits (= 4095) but have to subtract + * space for CRC. So the maximum is 4095 - 4*2 = 4087. + */ + mmc->max_blk_size = 4087; + + /* + * Maximum block count. There is no real limit so the maximum + * request size will be the only restriction. */ - mmc->max_seg_size = mmc->max_sectors * 512; + mmc->max_blk_count = mmc->max_req_size; dev_set_drvdata(dev, mmc); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d0e6a54..bf369a5 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -71,6 +71,8 @@ #define MMC_STATE_BAD (1<<2) /* unreco #define MMC_STATE_SDCARD (1<<3) /* is an SD card */ #define MMC_STATE_READONLY (1<<4) /* card is read-only */ #define MMC_STATE_HIGHSPEED (1<<5) /* card is in high speed mode */ +#define MMC_STATE_LOCKED (1<<6) /* card is currently locked */ +#define MMC_STATE_BLOCKADDR (1<<7) /* card uses block-addressing */ u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ u32 raw_scr[2]; /* raw card SCR */ @@ -87,6 +89,11 @@ #define mmc_card_bad(c) ((c)->state & M #define mmc_card_sd(c) ((c)->state & MMC_STATE_SDCARD) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) +#define mmc_card_locked(c) ((c)->state & MMC_STATE_LOCKED) +#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) + +#define mmc_card_lockable(c) (((c)->csd.cmdclass & CCC_LOCK_CARD) && \ + ((c)->host->caps & MMC_CAP_BYTEBLOCK)) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD) @@ -94,6 +101,10 @@ #define mmc_card_set_bad(c) ((c)->state #define mmc_card_set_sd(c) ((c)->state |= MMC_STATE_SDCARD) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) +#define mmc_card_set_locked(c) ((c)->state |= MMC_STATE_LOCKED) +#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) + +#define mmc_card_clear_locked(c) ((c)->state &= ~MMC_STATE_LOCKED) #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) ((c)->dev.bus_id) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c15ae19..913e575 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -92,8 +92,10 @@ #define MMC_CAP_BYTEBLOCK (1 << 2) /* Ca unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */ unsigned short max_phys_segs; /* see blk_queue_max_phys_segments */ - unsigned short max_sectors; /* see blk_queue_max_sectors */ unsigned short unused; + unsigned int max_req_size; /* maximum number of bytes in one req */ + unsigned int max_blk_size; /* maximum size of one mmc block */ + unsigned int max_blk_count; /* maximum number of blocks in one req */ /* private data */ struct mmc_ios ios; /* current io bus settings */ @@ -106,8 +108,9 @@ #define MMC_MODE_SD 1 struct list_head cards; /* devices attached to this host */ wait_queue_head_t wq; - spinlock_t lock; /* card_busy lock */ - struct mmc_card *card_busy; /* the MMC card claiming host */ + spinlock_t lock; /* claimed lock */ + unsigned int claimed:1; /* host exclusively claimed */ + struct mmc_card *card_selected; /* the selected MMC card */ struct delayed_work detect; @@ -126,6 +129,7 @@ static inline void *mmc_priv(struct mmc_ } #define mmc_dev(x) ((x)->parent) +#define mmc_classdev(x) (&(x)->class_dev) #define mmc_hostname(x) ((x)->class_dev.bus_id) extern int mmc_suspend_host(struct mmc_host *, pm_message_t); diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index bcf2490..ceb6bfb 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -42,7 +42,12 @@ #define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_ #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_PRESENT) +<<<<<<< HEAD/include/linux/mmc/mmc.h #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +======= +#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +>>>>>>> /include/linux/mmc/mmc.h #define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE)) diff --git a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h index 2dce60c..f5d91d7 100644 --- a/include/linux/mmc/protocol.h +++ b/include/linux/mmc/protocol.h @@ -79,9 +79,12 @@ #define MMC_APP_CMD 55 /* #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ /* SD commands type argument response */ - /* class 8 */ + /* class 0 */ /* This is basically the same command as for MMC with some quirks. */ #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */ +#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ + + /* class 10 */ #define SD_SWITCH 6 /* adtc [31:0] See below R1 */ /* Application commands */ @@ -115,6 +118,14 @@ #define SD_APP_SEND_SCR 51 /* */ /* + * SD_SEND_IF_COND argument format: + * + * [31:12] Reserved (0) + * [11:8] Host Voltage Supply Flags + * [7:0] Check Pattern (0xAA) + */ + +/* MMC status in R1 Type e : error bit @@ -312,5 +323,13 @@ #define SCR_SPEC_VER_2 2 #define SD_BUS_WIDTH_1 0 #define SD_BUS_WIDTH_4 2 +/* + * MMC_LOCK_UNLOCK modes + */ +#define MMC_LOCK_MODE_ERASE (1<<3) +#define MMC_LOCK_MODE_UNLOCK (1<<2) +#define MMC_LOCK_MODE_CLR_PWD (1<<1) +#define MMC_LOCK_MODE_SET_PWD (1<<0) + #endif /* MMC_MMC_PROTOCOL_H */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index f7a416c..c07ac8a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -735,9 +735,11 @@ #define PCI_VENDOR_ID_TI 0x104c #define PCI_DEVICE_ID_TI_TVP4020 0x3d07 #define PCI_DEVICE_ID_TI_4450 0x8011 #define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 +#define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 #define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 #define PCI_DEVICE_ID_TI_X515 0x8036 #define PCI_DEVICE_ID_TI_XX12 0x8039 +#define PCI_DEVICE_ID_TI_XX12_FM 0x803b #define PCI_DEVICE_ID_TI_1130 0xac12 #define PCI_DEVICE_ID_TI_1031 0xac13 #define PCI_DEVICE_ID_TI_1131 0xac15 @@ -765,6 +767,7 @@ #define PCI_DEVICE_ID_TI_1520 0xac55 #define PCI_DEVICE_ID_TI_1510 0xac56 #define PCI_DEVICE_ID_TI_X620 0xac8d #define PCI_DEVICE_ID_TI_X420 0xac8e +#define PCI_DEVICE_ID_TI_XX20_FM 0xac8f #define PCI_VENDOR_ID_SONY 0x104d diff --git a/include/linux/tifm.h b/include/linux/tifm.h index dfb8052..3deb0a6 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -17,7 +17,7 @@ #include #include #include #include -#include +#include /* Host registers (relative to pci base address): */ enum { @@ -62,11 +62,10 @@ enum { #define TIFM_IRQ_ENABLE 0x80000000 -#define TIFM_IRQ_SOCKMASK 0x00000001 -#define TIFM_IRQ_CARDMASK 0x00000100 -#define TIFM_IRQ_FIFOMASK 0x00010000 +#define TIFM_IRQ_SOCKMASK(x) (x) +#define TIFM_IRQ_CARDMASK(x) ((x) << 8) +#define TIFM_IRQ_FIFOMASK(x) ((x) << 16) #define TIFM_IRQ_SETALL 0xffffffff -#define TIFM_IRQ_SETALLSOCK 0x0000000f #define TIFM_CTRL_LED 0x00000040 #define TIFM_CTRL_FAST_CLK 0x00000100 @@ -89,10 +88,9 @@ struct tifm_dev { char __iomem *addr; spinlock_t lock; tifm_media_id media_id; - char wq_name[KOBJ_NAME_LEN]; - struct workqueue_struct *wq; + unsigned int socket_id; - unsigned int (*signal_irq)(struct tifm_dev *sock, + void (*signal_irq)(struct tifm_dev *sock, unsigned int sock_irq_status); struct tifm_driver *drv; @@ -103,24 +101,23 @@ struct tifm_driver { tifm_media_id *id_table; int (*probe)(struct tifm_dev *dev); void (*remove)(struct tifm_dev *dev); + int (*suspend)(struct tifm_dev *dev, + pm_message_t state); + int (*resume)(struct tifm_dev *dev); struct device_driver driver; }; struct tifm_adapter { char __iomem *addr; - unsigned int irq_status; - unsigned int insert_mask; - unsigned int remove_mask; spinlock_t lock; + unsigned int irq_status; + unsigned int socket_change_set; + wait_queue_head_t change_set_notify; unsigned int id; - unsigned int max_sockets; - char wq_name[KOBJ_NAME_LEN]; - unsigned int inhibit_new_cards; - struct workqueue_struct *wq; - struct work_struct media_inserter; - struct work_struct media_remover; + unsigned int num_sockets; struct tifm_dev **sockets; + struct task_struct *media_switcher; struct class_device cdev; struct device *dev; @@ -130,9 +127,9 @@ struct tifm_adapter { struct tifm_adapter *tifm_alloc_adapter(void); void tifm_free_device(struct device *dev); void tifm_free_adapter(struct tifm_adapter *fm); -int tifm_add_adapter(struct tifm_adapter *fm); +int tifm_add_adapter(struct tifm_adapter *fm, int (*mediathreadfn)(void *data)); void tifm_remove_adapter(struct tifm_adapter *fm); -struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id); +struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm); int tifm_register_driver(struct tifm_driver *drv); void tifm_unregister_driver(struct tifm_driver *drv); void tifm_eject(struct tifm_dev *sock);