From 82999770d6926193f50b42e713a92ee4028398e3 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Thu, 25 Jan 2007 10:29:24 +0100 Subject: mmc: au1xmmc: implement proper ro switch detection au1xmmc: implement proper R/O switch detection. Signed-off-by: Manuel Lauss Signed-off-by: Pierre Ossman --- drivers/mmc/au1xmmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 800527cf40d..212e41f78df 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -152,8 +152,9 @@ static inline int au1xmmc_card_inserted(struct au1xmmc_host *host) ? 1 : 0; } -static inline int au1xmmc_card_readonly(struct au1xmmc_host *host) +static int au1xmmc_card_readonly(struct mmc_host *mmc) { + struct au1xmmc_host *host = mmc_priv(mmc); return (bcsr->status & au1xmmc_card_table[host->id].wpstatus) ? 1 : 0; } @@ -878,6 +879,7 @@ static void au1xmmc_init_dma(struct au1xmmc_host *host) static const struct mmc_host_ops au1xmmc_ops = { .request = au1xmmc_request, .set_ios = au1xmmc_set_ios, + .get_ro = au1xmmc_card_readonly, }; static int __devinit au1xmmc_probe(struct platform_device *pdev) -- cgit v1.2.3 From 279bc4450989215e741c2c9d3a726f1ac96ede40 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Thu, 25 Jan 2007 10:27:41 +0100 Subject: mmc: au1xmmc: return errors for unknown response types au1xmmc: return error when encountering unhandled/unknown response type. Signed-off-by: Manuel Lauss Signed-off-by: Pierre Ossman --- drivers/mmc/au1xmmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 212e41f78df..6d6fe6e6d41 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -194,6 +194,8 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT); switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + break; case MMC_RSP_R1: mmccmd |= SD_CMD_RT_1; break; @@ -206,6 +208,10 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, case MMC_RSP_R3: mmccmd |= SD_CMD_RT_3; break; + default: + printk(KERN_INFO "au1xmmc: unhandled response type %02x\n", + mmc_resp_type(cmd)); + return MMC_ERR_INVALID; } switch(cmd->opcode) { -- cgit v1.2.3 From f22ee4edf63e7480511112d9965c71e07be3f8b7 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 26 Dec 2006 15:11:23 +0100 Subject: 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 --- drivers/mmc/mmc.c | 16 ++++++++-------- include/linux/mmc/host.h | 5 +++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6f2a282e2b9..105f419d08c 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -103,7 +103,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) 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; @@ -157,7 +157,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries { struct mmc_request mrq; - BUG_ON(host->card_busy == NULL); + BUG_ON(!host->claimed); memset(&mrq, 0, sizeof(struct mmc_request)); @@ -195,7 +195,7 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca, int i, err; - BUG_ON(host->card_busy == NULL); + BUG_ON(!host->claimed); BUG_ON(retries < 0); err = MMC_ERR_INVALID; @@ -320,14 +320,14 @@ int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card) 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 +353,10 @@ void mmc_release_host(struct mmc_host *host) { 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); @@ -381,7 +381,7 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) 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; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c15ae1986b9..dc4c6e39519 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -106,8 +106,9 @@ struct mmc_host { 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; -- cgit v1.2.3 From 11354d03afe9dd0d114e078057158baad4b4eee9 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 14 Jan 2007 01:41:45 +0100 Subject: 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 --- drivers/mmc/mmc_sysfs.c | 2 +- include/linux/mmc/host.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index e334acd045b..d32698b02d7 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -199,7 +199,7 @@ void mmc_init_card(struct mmc_card *card, struct mmc_host *host) 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; } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index dc4c6e39519..ae98d6766bd 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -127,6 +127,7 @@ static inline void *mmc_priv(struct mmc_host *host) } #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); -- cgit v1.2.3 From 9e9dc5f29f2eb65153a15c4fdb12b4382e3a75b2 Mon Sep 17 00:00:00 2001 From: Darren Salt Date: Sat, 27 Jan 2007 15:32:31 +0100 Subject: mmc: Power quirk for ENE controllers Support for these devices was broken for 2.6.18-rc1 and later by commit 146ad66eac836c0b976c98f428d73e1f6a75270d, which added voltage level support. This restores the previous behaviour for these devices by ensuring that when the voltage is changed, only one write to set the voltage is performed. It may be that both writes are needed if the voltage is being changed between two non-zero values or that it's safe to ensure that only one write is done if the hardware only supports one voltage; I don't know whether either is the case nor can I test since I have only the one SD reader (1524:0550), and it supports just the one voltage. Signed-off-by: Darren Salt Signed-off-by: Pierre Ossman --- drivers/mmc/sdhci.c | 22 +++++++++++++++++++--- include/linux/pci_ids.h | 1 + 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index c2d13d7e991..175a9427b9b 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -37,6 +37,7 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_FORCE_DMA (1<<1) /* Controller doesn't like some resets when there is no card inserted. */ #define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) +#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -65,6 +66,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = SDHCI_QUIRK_FORCE_DMA, }, + { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB712_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, + }, + { /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) }, @@ -674,10 +683,17 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) if (host->power == power) return; - writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); - - if (power == (unsigned short)-1) + if (power == (unsigned short)-1) { + writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); goto out; + } + + /* + * Spec says that we should clear the power reg before setting + * a new value. Some controllers don't seem to like this though. + */ + if (!(host->chip->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) + writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); pwr = SDHCI_POWER_ON; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3d1d21035de..d37f46aaaea 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1971,6 +1971,7 @@ #define PCI_DEVICE_ID_TOPIC_TP560 0x0000 #define PCI_VENDOR_ID_ENE 0x1524 +#define PCI_DEVICE_ID_ENE_CB712_SD 0x0550 #define PCI_DEVICE_ID_ENE_1211 0x1211 #define PCI_DEVICE_ID_ENE_1225 0x1225 #define PCI_DEVICE_ID_ENE_1410 0x1410 -- cgit v1.2.3 From fba68bd2dab1ac99af3c5a963ec9581cfa9f1725 Mon Sep 17 00:00:00 2001 From: Philip Langdale Date: Thu, 4 Jan 2007 06:57:32 -0800 Subject: 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 --- drivers/mmc/mmc.c | 140 ++++++++++++++++++++++++++++++++++--------- drivers/mmc/mmc_block.c | 8 ++- include/linux/mmc/card.h | 3 + include/linux/mmc/mmc.h | 1 + include/linux/mmc/protocol.h | 13 +++- 5 files changed, 135 insertions(+), 30 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 105f419d08c..b48c277312d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -289,7 +289,10 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, 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; } @@ -372,7 +375,7 @@ static inline void mmc_set_ios(struct mmc_host *host) 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); } @@ -588,34 +591,65 @@ static void mmc_decode_csd(struct mmc_card *card) 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 +882,41 @@ static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) 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 @@ -1334,6 +1403,10 @@ static void mmc_setup(struct mmc_host *host) 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 +1459,21 @@ static void mmc_setup(struct mmc_host *host) * 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); diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 87713572293..5a4eacac0bb 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -237,7 +237,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) 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); @@ -494,6 +496,10 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) 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/include/linux/mmc/card.h b/include/linux/mmc/card.h index d0e6a549761..e45712acfac 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -71,6 +71,7 @@ struct mmc_card { #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_BLOCKADDR (1<<6) /* 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 +88,7 @@ struct mmc_card { #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_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD) @@ -94,6 +96,7 @@ struct mmc_card { #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_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) ((c)->dev.bus_id) diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index bcf24909d67..cdc54be804f 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -43,6 +43,7 @@ struct mmc_command { #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_PRESENT) #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) #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 2dce60c43f4..c90b6768329 100644 --- a/include/linux/mmc/protocol.h +++ b/include/linux/mmc/protocol.h @@ -79,9 +79,12 @@ #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 */ @@ -114,6 +117,14 @@ * [3:0] Function group 1 */ +/* + * 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 -- cgit v1.2.3 From 1289335a2ab57d00c638c3954dc86d6c4eab5606 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 8 Dec 2006 16:50:47 +1100 Subject: 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 --- drivers/mmc/tifm_sd.c | 57 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index fa4a52886b9..f22c3968f2d 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -17,7 +17,7 @@ #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; @@ -239,50 +239,65 @@ 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; @@ -340,7 +355,9 @@ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, 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, -- cgit v1.2.3 From 255ef22e89ecedcc594428444a72a29cb66153f5 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 8 Dec 2006 16:50:48 +1100 Subject: 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 --- drivers/mmc/tifm_sd.c | 52 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index f22c3968f2d..3e762770afc 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -103,38 +103,51 @@ struct tifm_sd { wait_queue_head_t can_eject; 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) { 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 +155,16 @@ static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, && (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); } } } + tifm_sd_kunmap_atomic(buffer, host->req->data); } return 0; } @@ -561,15 +576,6 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) 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) { @@ -586,7 +592,6 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) 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; @@ -615,9 +620,6 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) return; err_out: - if (t_buffer) - kunmap(r_data->sg->page); - mrq->cmd->error = MMC_ERR_TIMEOUT; mmc_request_done(mmc, mrq); } @@ -659,7 +661,6 @@ static void tifm_sd_end_cmd_nodma(struct work_struct *work) 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; } @@ -669,9 +670,6 @@ static void tifm_sd_end_cmd_nodma(struct work_struct *work) spin_unlock_irqrestore(&sock->lock, flags); - if (r_data) - kunmap(r_data->sg->page); - mmc_request_done(mmc, mrq); } -- cgit v1.2.3 From 0803dd0c2594c7dc70c14ab00ea21e68605f5ba1 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 8 Dec 2006 16:50:49 +1100 Subject: 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 --- drivers/mmc/tifm_sd.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 3e762770afc..69b78eb16ef 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -95,11 +95,11 @@ 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 timer_list timer; struct mmc_request *req; struct work_struct cmd_handler; - struct delayed_work abort_handler; wait_queue_head_t can_eject; size_t written_blocks; @@ -321,8 +321,6 @@ change_state: return; } - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); } /* Called from interrupt handler */ @@ -335,7 +333,6 @@ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, 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); @@ -375,9 +372,6 @@ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, || 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 { @@ -506,9 +500,8 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) } 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); tifm_sd_exec(host, mrq->cmd); @@ -536,6 +529,7 @@ static void tifm_sd_end_cmd(struct work_struct *work) spin_lock_irqsave(&sock->lock, flags); + del_timer(&host->timer); mrq = host->req; host->req = NULL; host->state = IDLE; @@ -610,9 +604,8 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) } 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); tifm_sd_exec(host, mrq->cmd); @@ -635,6 +628,7 @@ static void tifm_sd_end_cmd_nodma(struct work_struct *work) spin_lock_irqsave(&sock->lock, flags); + del_timer(&host->timer); mrq = host->req; host->req = NULL; host->state = IDLE; @@ -673,14 +667,11 @@ static void tifm_sd_end_cmd_nodma(struct work_struct *work) mmc_request_done(mmc, mrq); } -static void tifm_sd_abort(struct work_struct *work) +static void tifm_sd_abort(unsigned long data) { - struct tifm_sd *host = - container_of(work, struct tifm_sd, abort_handler.work); - printk(KERN_ERR DRIVER_NAME - ": card failed to respond for a long period of time"); - tifm_eject(host->dev); + ": card failed to respond for a long period of time"); + tifm_eject(((struct tifm_sd*)data)->dev); } static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -785,6 +776,7 @@ static void tifm_sd_register_host(struct work_struct *work) unsigned long flags; spin_lock_irqsave(&sock->lock, flags); + del_timer(&host->timer); host->flags |= HOST_REG; PREPARE_WORK(&host->cmd_handler, no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd); @@ -814,7 +806,7 @@ static int tifm_sd_probe(struct tifm_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); + setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); tifm_set_drvdata(sock, mmc); sock->signal_irq = tifm_sd_signal_irq; @@ -866,8 +858,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) writel(host->clk_div | TIFM_MMCSD_POWER, sock->addr + SOCK_MMCSD_CONFIG); - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); + mod_timer(&host->timer, jiffies + host->timeout_jiffies); return 0; } @@ -891,6 +882,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) struct tifm_sd *host = mmc_priv(mmc); unsigned long flags; + del_timer_sync(&host->timer); spin_lock_irqsave(&sock->lock, flags); host->flags |= EJECT; if (host->req) -- cgit v1.2.3 From 83d420ba92bdd52127e4548ae8050a48f655ce3b Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 8 Dec 2006 16:50:50 +1100 Subject: 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 --- drivers/mmc/tifm_sd.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 69b78eb16ef..5817a138a1e 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -442,22 +442,21 @@ static void tifm_sd_set_data_timeout(struct tifm_sd *host, 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) 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); } } -- cgit v1.2.3 From 8e02f8581cd2f9c12a03be7641d5c2c427170feb Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 8 Dec 2006 16:50:51 +1100 Subject: 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 --- drivers/misc/tifm_7xx1.c | 3 +- drivers/misc/tifm_core.c | 11 +-- drivers/mmc/tifm_sd.c | 205 +++++++++++++++++++++++++---------------------- include/linux/tifm.h | 5 +- 4 files changed, 116 insertions(+), 108 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 2ab7add78f9..50c4cdabb65 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -201,11 +201,12 @@ static void tifm_7xx1_insert_media(struct work_struct *work) fm->max_sockets == 2); if (media_id) { ok_to_register = 0; - new_sock = tifm_alloc_device(fm, cnt); + new_sock = tifm_alloc_device(fm); if (new_sock) { new_sock->addr = tifm_7xx1_sock_addr(fm->addr, cnt); new_sock->media_id = media_id; + new_sock->socket_id = cnt; switch (media_id) { case 1: card_name = "xd"; diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index d61df5c3ac3..21eb0ab7c32 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -141,24 +141,17 @@ 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) +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; diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 5817a138a1e..85a5974318b 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -79,7 +79,6 @@ typedef enum { enum { FIFO_RDY = 0x0001, /* hardware dependent value */ - HOST_REG = 0x0002, EJECT = 0x0004, EJECT_DONE = 0x0008, CARD_BUSY = 0x0010, @@ -97,10 +96,10 @@ struct tifm_sd { unsigned int clk_div; unsigned long timeout_jiffies; + struct tasklet_struct finish_tasklet; struct timer_list timer; struct mmc_request *req; - struct work_struct cmd_handler; - wait_queue_head_t can_eject; + wait_queue_head_t notify; size_t written_blocks; size_t buffer_size; @@ -317,7 +316,7 @@ change_state: } break; case READY: - queue_work(sock->wq, &host->cmd_handler); + tasklet_schedule(&host->finish_tasklet); return; } @@ -345,8 +344,6 @@ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, 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; @@ -517,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; @@ -616,9 +613,9 @@ err_out: 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; @@ -666,11 +663,33 @@ static void tifm_sd_end_cmd_nodma(struct work_struct *work) mmc_request_done(mmc, mrq); } +static void tifm_sd_terminate(struct tifm_sd *host) +{ + 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"); - tifm_eject(((struct tifm_sd*)data)->dev); + + tifm_sd_terminate(host); + tifm_eject(host->dev); } static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -739,7 +758,7 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) // 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); @@ -767,21 +786,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); - del_timer(&host->timer); - 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) @@ -801,77 +866,37 @@ static int tifm_sd_probe(struct tifm_dev *sock) 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); - setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); - 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 + sock->signal_irq = tifm_sd_signal_irq; + rc = tifm_sd_initialize_host(host); - 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); - - 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) { - printk(KERN_ERR DRIVER_NAME - ": card not ready - probe failed\n"); - mmc_free_host(mmc); - 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); - writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, - sock->addr + SOCK_MMCSD_INT_ENABLE); - - 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); - - mod_timer(&host->timer, jiffies + host->timeout_jiffies); + if (!rc) + rc = mmc_add_host(mmc); + if (rc) + goto out_free_mmc; return 0; -} - -static int tifm_sd_host_is_down(struct tifm_dev *sock) -{ - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct tifm_sd *host = mmc_priv(mmc); - unsigned long flags; - int rc = 0; - - spin_lock_irqsave(&sock->lock, flags); - rc = (host->flags & EJECT_DONE); - spin_unlock_irqrestore(&sock->lock, flags); +out_free_mmc: + mmc_free_host(mmc); return rc; } @@ -879,27 +904,17 @@ static void tifm_sd_remove(struct tifm_dev *sock) { struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); - unsigned long flags; del_timer_sync(&host->timer); - 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 (host->flags & HOST_REG) - mmc_remove_host(mmc); + 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); /* 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); tifm_set_drvdata(sock, NULL); mmc_free_host(mmc); diff --git a/include/linux/tifm.h b/include/linux/tifm.h index dfb8052eee5..9caa28e2a63 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -89,8 +89,7 @@ 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, unsigned int sock_irq_status); @@ -132,7 +131,7 @@ void tifm_free_device(struct device *dev); void tifm_free_adapter(struct tifm_adapter *fm); int tifm_add_adapter(struct tifm_adapter *fm); 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); -- cgit v1.2.3 From 2e8ce5e7414e74fe8904495b1f22cf00d3349398 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 8 Dec 2006 16:50:52 +1100 Subject: 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 --- drivers/mmc/tifm_sd.c | 98 ++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 85a5974318b..37fe0c3ecb8 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -118,7 +118,7 @@ static void tifm_sd_kunmap_atomic(char *buffer, struct mmc_data *data) } 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; @@ -159,7 +159,7 @@ static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, t_val |= ((buffer[host->buffer_pos++]) << 8) & 0xff00; writel(t_val, - sock->addr + SOCK_MMCSD_DATA); + sock->addr + SOCK_MMCSD_DATA); } } } @@ -220,7 +220,7 @@ static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) 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); @@ -324,7 +324,7 @@ change_state: /* Called from interrupt handler */ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, - unsigned int sock_irq_status) + unsigned int sock_irq_status) { struct tifm_sd *host; unsigned int host_status = 0, fifo_status = 0; @@ -350,11 +350,11 @@ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, 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, @@ -382,8 +382,8 @@ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, 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; } @@ -393,22 +393,23 @@ static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, 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); @@ -422,7 +423,7 @@ static void tifm_sd_prepare_data(struct tifm_sd *card, struct mmc_command *cmd) 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); @@ -430,7 +431,7 @@ static void tifm_sd_prepare_data(struct tifm_sd *card, struct mmc_command *cmd) } 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; @@ -448,7 +449,7 @@ static void tifm_sd_set_data_timeout(struct tifm_sd *host, sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); } else { 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 @@ -499,7 +500,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) mod_timer(&host->timer, jiffies + host->timeout_jiffies); host->state = CMD; 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; @@ -539,8 +540,8 @@ static void tifm_sd_end_cmd(unsigned long data) 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; @@ -554,7 +555,7 @@ static void tifm_sd_end_cmd(unsigned long data) } 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); @@ -582,14 +583,14 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) if (r_data) { tifm_sd_set_data_timeout(host, r_data); - 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; @@ -603,7 +604,7 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) mod_timer(&host->timer, jiffies + host->timeout_jiffies); host->state = CMD; 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; @@ -642,8 +643,8 @@ static void tifm_sd_end_cmd_nodma(unsigned long data) 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; @@ -656,7 +657,7 @@ static void tifm_sd_end_cmd_nodma(unsigned long data) } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + sock->addr + SOCK_CONTROL); spin_unlock_irqrestore(&sock->lock, flags); @@ -707,9 +708,9 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) 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) { @@ -728,23 +729,24 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) 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; @@ -855,8 +857,8 @@ static int tifm_sd_probe(struct tifm_dev *sock) 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; } @@ -914,7 +916,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) /* The meaning of the bit majority in this constant is unknown. */ writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); + sock->addr + SOCK_CONTROL); tifm_set_drvdata(sock, NULL); mmc_free_host(mmc); -- cgit v1.2.3 From 50743f4cb1d655c7fbe25af58a9d0db6bf76d687 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:30 +1100 Subject: Remove unused return value from signal_irq callback Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 4 ++-- drivers/mmc/tifm_sd.c | 5 ++--- include/linux/tifm.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 50c4cdabb65..375b56742f2 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -13,7 +13,7 @@ #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) { @@ -91,7 +91,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) if (fm->sockets[cnt]) { if (sock_irq_status && fm->sockets[cnt]->signal_irq) - sock_irq_status = fm->sockets[cnt]-> + fm->sockets[cnt]-> signal_irq(fm->sockets[cnt], sock_irq_status); diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 37fe0c3ecb8..2adfe346701 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -323,8 +323,8 @@ change_state: } /* 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; @@ -395,7 +395,6 @@ done: dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n", host_status, fifo_status); spin_unlock(&sock->lock); - return sock_irq_status; } static void tifm_sd_prepare_data(struct tifm_sd *host, struct mmc_command *cmd) diff --git a/include/linux/tifm.h b/include/linux/tifm.h index 9caa28e2a63..5b0baef4e61 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -91,7 +91,7 @@ struct tifm_dev { tifm_media_id media_id; 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; -- cgit v1.2.3 From 217334d14d28e6a671e6dd2c7a35c9070b0721ea Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:31 +1100 Subject: Add dummy_signal_irq function to save check in ISR Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 11 +++++------ drivers/misc/tifm_core.c | 8 ++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 375b56742f2..7fbf5d58bfc 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -71,6 +71,7 @@ static void tifm_7xx1_remove_media(struct work_struct *work) 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; @@ -85,15 +86,13 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); for (cnt = 0; cnt < fm->max_sockets; cnt++) { + sock = fm->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) - fm->sockets[cnt]-> - signal_irq(fm->sockets[cnt], - sock_irq_status); + if (sock) { + if (sock_irq_status) + sock->signal_irq(sock, sock_irq_status); if (irq_status & (1 << cnt)) fm->remove_mask |= 1 << cnt; diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 21eb0ab7c32..3eaf2c985b7 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -145,6 +145,12 @@ void tifm_free_device(struct device *dev) } EXPORT_SYMBOL(tifm_free_device); +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); @@ -155,6 +161,7 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) 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; } @@ -212,6 +219,7 @@ static int tifm_device_remove(struct device *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; -- cgit v1.2.3 From 1499ead31ede528a657c50761c4780c40f929d6d Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:32 +1100 Subject: 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 --- drivers/misc/tifm_7xx1.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 7fbf5d58bfc..24b20a417a2 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -17,18 +17,12 @@ 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; - } - } + fm->remove_mask |= 1 << sock->socket_id; + queue_work(fm->wq, &fm->media_remover); } spin_unlock_irqrestore(&fm->lock, flags); } -- cgit v1.2.3 From 6412d927313f08808d61b7efba8da43717d4e8d2 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:33 +1100 Subject: 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 --- drivers/misc/tifm_7xx1.c | 254 +++++++++++++++++++++++------------------------ include/linux/tifm.h | 19 ++-- 2 files changed, 134 insertions(+), 139 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 24b20a417a2..5ab81dd3785 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -21,47 +21,12 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) spin_lock_irqsave(&fm->lock, flags); if (!fm->inhibit_new_cards) { - fm->remove_mask |= 1 << sock->socket_id; - queue_work(fm->wq, &fm->media_remover); + fm->socket_change_set |= 1 << sock->socket_id; + queue_work(fm->wq, &fm->media_switcher); } 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); - } - } - 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; @@ -79,32 +44,27 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) if (irq_status & TIFM_IRQ_ENABLE) { writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - for (cnt = 0; cnt < fm->max_sockets; cnt++) { + for (cnt = 0; cnt < fm->num_sockets; cnt++) { sock = fm->sockets[cnt]; - sock_irq_status = (irq_status >> cnt) & - (TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK); - - if (sock) { - if (sock_irq_status) - sock->signal_irq(sock, sock_irq_status); + 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) { + if (!fm->socket_change_set) { 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); + queue_work(fm->wq, &fm->media_switcher); } } @@ -163,84 +123,125 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num) return base_addr + ((sock_num + 1) << 10); } -static void tifm_7xx1_insert_media(struct work_struct *work) +static void tifm_7xx1_switch_media(struct work_struct *work) { struct tifm_adapter *fm = - container_of(work, struct tifm_adapter, media_inserter); + container_of(work, struct tifm_adapter, media_switcher); 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; + 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) { + 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 (fm->inhibit_new_cards) + 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))) - 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); - if (new_sock) { - new_sock->addr = tifm_7xx1_sock_addr(fm->addr, - cnt); - new_sock->media_id = media_id; - new_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: - break; - } - snprintf(new_sock->dev.bus_id, BUS_ID_SIZE, - "tifm_%s%u:%u", card_name, fm->id, cnt); + if (!socket_change_set) + break; + + 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 (fm->inhibit_new_cards) + 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 (!fm->inhibit_new_cards) { + 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); + break; + } 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); + break; + } 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); } @@ -251,13 +252,12 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) spin_lock_irqsave(&fm->lock, flags); fm->inhibit_new_cards = 1; - fm->remove_mask = 0xf; - fm->insert_mask = 0; + fm->socket_change_set = 0xf; 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); + tifm_7xx1_switch_media(&fm->media_switcher); pci_set_power_state(dev, PCI_D3hot); pci_disable_device(dev); @@ -279,9 +279,9 @@ static int tifm_7xx1_resume(struct pci_dev *dev) 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, + writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - fm->insert_mask = 0xf; + fm->socket_change_set = 0xf; spin_unlock_irqrestore(&fm->lock, flags); return 0; } @@ -318,14 +318,13 @@ static int tifm_7xx1_probe(struct pci_dev *dev, } fm->dev = &dev->dev; - fm->max_sockets = (dev->device == 0x803B) ? 2 : 4; - fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->max_sockets, + fm->num_sockets = (dev->device == 0x803B) ? 2 : 4; + 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); + INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media); fm->eject = tifm_7xx1_eject; pci_set_drvdata(dev, fm); @@ -343,10 +342,10 @@ static int tifm_7xx1_probe(struct pci_dev *dev, goto err_out_irq; writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, + writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - fm->insert_mask = 0xf; + fm->socket_change_set = 0xf; return 0; @@ -373,14 +372,13 @@ static void tifm_7xx1_remove(struct pci_dev *dev) spin_lock_irqsave(&fm->lock, flags); fm->inhibit_new_cards = 1; - fm->remove_mask = 0xf; - fm->insert_mask = 0; + fm->socket_change_set = 0xf; 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); + tifm_7xx1_switch_media(&fm->media_switcher); writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); free_irq(dev->irq, fm); diff --git a/include/linux/tifm.h b/include/linux/tifm.h index 5b0baef4e61..eaf9e1f4878 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -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 @@ -108,18 +107,16 @@ struct tifm_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; unsigned int id; - unsigned int max_sockets; + unsigned int num_sockets; + struct tifm_dev **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; - struct tifm_dev **sockets; + struct work_struct media_switcher; struct class_device cdev; struct device *dev; -- cgit v1.2.3 From 7146f0d3bd2bcd0100a5db54f4ba9edc1042fe01 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 18 Dec 2006 14:20:06 +1100 Subject: 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 --- drivers/misc/tifm_7xx1.c | 151 ++++++++++++++++++++++++++++------------------- drivers/misc/tifm_core.c | 11 ++-- include/linux/tifm.h | 10 ++-- 3 files changed, 98 insertions(+), 74 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 5ab81dd3785..d3e8ff46c23 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -11,6 +11,7 @@ #include #include +#include #define DRIVER_NAME "tifm_7xx1" #define DRIVER_VERSION "0.7" @@ -20,10 +21,8 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) unsigned long flags; spin_lock_irqsave(&fm->lock, flags); - if (!fm->inhibit_new_cards) { - fm->socket_change_set |= 1 << sock->socket_id; - queue_work(fm->wq, &fm->media_switcher); - } + fm->socket_change_set |= 1 << sock->socket_id; + wake_up_all(&fm->change_set_notify); spin_unlock_irqrestore(&fm->lock, flags); } @@ -59,14 +58,10 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) } writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); - if (!fm->inhibit_new_cards) { - if (!fm->socket_change_set) { - writel(TIFM_IRQ_ENABLE, - fm->addr + FM_SET_INTERRUPT_ENABLE); - } else { - queue_work(fm->wq, &fm->media_switcher); - } - } + 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; @@ -123,21 +118,22 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num) return base_addr + ((sock_num + 1) << 10); } -static void tifm_7xx1_switch_media(struct work_struct *work) +static int tifm_7xx1_switch_media(void *data) { - struct tifm_adapter *fm = - container_of(work, struct tifm_adapter, media_switcher); + struct tifm_adapter *fm = data; unsigned long flags; tifm_media_id media_id; char *card_name = "xx"; - int cnt; + int cnt, rc; struct tifm_dev *sock; unsigned int socket_change_set; - if (!class_device_get(&fm->cdev)) - return; - 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; @@ -145,12 +141,12 @@ static void tifm_7xx1_switch_media(struct work_struct *work) dev_dbg(fm->dev, "checking media set %x\n", socket_change_set); - if (fm->inhibit_new_cards) + if (kthread_should_stop()) socket_change_set = (1 << fm->num_sockets) - 1; spin_unlock_irqrestore(&fm->lock, flags); if (!socket_change_set) - break; + continue; spin_lock_irqsave(&fm->lock, flags); for (cnt = 0; cnt < fm->num_sockets; cnt++) { @@ -169,7 +165,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work) tifm_7xx1_sock_addr(fm->addr, cnt) + SOCK_CONTROL); } - if (fm->inhibit_new_cards) + if (kthread_should_stop()) continue; spin_unlock_irqrestore(&fm->lock, flags); @@ -218,7 +214,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work) } } - if (!fm->inhibit_new_cards) { + if (!kthread_should_stop()) { writel(TIFM_IRQ_FIFOMASK(socket_change_set) | TIFM_IRQ_CARDMASK(socket_change_set), fm->addr + FM_CLEAR_INTERRUPT_ENABLE); @@ -228,7 +224,6 @@ static void tifm_7xx1_switch_media(struct work_struct *work) writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); spin_unlock_irqrestore(&fm->lock, flags); - break; } else { for (cnt = 0; cnt < fm->num_sockets; cnt++) { if (fm->sockets[cnt]) @@ -236,56 +231,93 @@ static void tifm_7xx1_switch_media(struct work_struct *work) } if (!fm->socket_change_set) { spin_unlock_irqrestore(&fm->lock, flags); - break; + return 0; } else { spin_unlock_irqrestore(&fm->lock, flags); } } } - 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->socket_change_set = 0xf; - writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - spin_unlock_irqrestore(&fm->lock, flags); - flush_workqueue(fm->wq); - - tifm_7xx1_switch_media(&fm->media_switcher); - - 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); + 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); - fm->socket_change_set = 0xf; + 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) { @@ -324,7 +356,6 @@ static int tifm_7xx1_probe(struct pci_dev *dev, if (!fm->sockets) goto err_out_free; - INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media); fm->eject = tifm_7xx1_eject; pci_set_drvdata(dev, fm); @@ -337,16 +368,15 @@ static int tifm_7xx1_probe(struct pci_dev *dev, 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_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - - fm->socket_change_set = 0xf; - + wake_up_process(fm->media_switcher); return 0; err_out_irq: @@ -370,18 +400,15 @@ static void tifm_7xx1_remove(struct pci_dev *dev) 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->socket_change_set = 0xf; - 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_switch_media(&fm->media_switcher); - - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - free_irq(dev->irq, fm); + kthread_stop(fm->media_switcher); tifm_remove_adapter(fm); diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 3eaf2c985b7..4d62dab2ada 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -71,8 +71,6 @@ static void tifm_free(struct class_device *cdev) 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 +99,8 @@ void tifm_free_adapter(struct tifm_adapter *fm) } 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 +112,10 @@ int tifm_add_adapter(struct tifm_adapter *fm) 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); diff --git a/include/linux/tifm.h b/include/linux/tifm.h index eaf9e1f4878..e5a8295f9fb 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include /* Host registers (relative to pci base address): */ enum { @@ -110,13 +110,11 @@ struct tifm_adapter { spinlock_t lock; unsigned int irq_status; unsigned int socket_change_set; + wait_queue_head_t change_set_notify; unsigned int id; unsigned int num_sockets; struct tifm_dev **sockets; - char wq_name[KOBJ_NAME_LEN]; - unsigned int inhibit_new_cards; - struct workqueue_struct *wq; - struct work_struct media_switcher; + struct task_struct *media_switcher; struct class_device cdev; struct device *dev; @@ -126,7 +124,7 @@ 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); int tifm_register_driver(struct tifm_driver *drv); -- cgit v1.2.3 From b5ad6761533c3f7e97c93b2333a0f88490d44f36 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:35 +1100 Subject: 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 --- drivers/misc/tifm_7xx1.c | 13 ++++++++----- include/linux/pci_ids.h | 3 +++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index d3e8ff46c23..ea6ad9f2d24 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -350,7 +350,8 @@ static int tifm_7xx1_probe(struct pci_dev *dev, } fm->dev = &dev->dev; - fm->num_sockets = (dev->device == 0x803B) ? 2 : 4; + 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) @@ -423,10 +424,12 @@ static void tifm_7xx1_remove(struct pci_dev *dev) } 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/include/linux/pci_ids.h b/include/linux/pci_ids.h index d37f46aaaea..ccd706f876e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -735,9 +735,11 @@ #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_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 -- cgit v1.2.3 From 8b40adab9c6cb63cede72c3ce3c3fee1157719e0 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:36 +1100 Subject: tifm_7xx1: prettify Fix some spaces and tabs. No semantic changes are introduced. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index ea6ad9f2d24..e21e490fedb 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -43,7 +43,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) if (irq_status & TIFM_IRQ_ENABLE) { writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - for (cnt = 0; cnt < fm->num_sockets; cnt++) { + for (cnt = 0; cnt < fm->num_sockets; cnt++) { sock = fm->sockets[cnt]; sock_irq_status = (irq_status >> cnt) & (TIFM_IRQ_FIFOMASK(1) @@ -53,8 +53,8 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) sock->signal_irq(sock, sock_irq_status); } - fm->socket_change_set |= irq_status - & ((1 << fm->num_sockets) - 1); + fm->socket_change_set |= irq_status + & ((1 << fm->num_sockets) - 1); } writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); @@ -67,7 +67,8 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) 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; @@ -75,8 +76,8 @@ static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, int is 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); } @@ -99,8 +100,8 @@ static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, int is } 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); } @@ -176,7 +177,7 @@ static int tifm_7xx1_switch_media(void *data) sock = tifm_alloc_device(fm); if (sock) { sock->addr = tifm_7xx1_sock_addr(fm->addr, - cnt); + cnt); sock->media_id = media_id; sock->socket_id = cnt; switch (media_id) { @@ -195,10 +196,11 @@ static int tifm_7xx1_switch_media(void *data) continue; } snprintf(sock->dev.bus_id, BUS_ID_SIZE, - "tifm_%s%u:%u", card_name, fm->id, cnt); + "tifm_%s%u:%u", card_name, + fm->id, cnt); printk(KERN_INFO DRIVER_NAME - ": %s card detected in socket %d\n", - card_name, cnt); + ": %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]) { @@ -319,7 +321,7 @@ static int tifm_7xx1_resume(struct pci_dev *dev) #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; @@ -353,7 +355,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev, 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); + GFP_KERNEL); if (!fm->sockets) goto err_out_free; @@ -361,7 +363,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev, 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; @@ -376,7 +378,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev, writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), - fm->addr + FM_SET_INTERRUPT_ENABLE); + fm->addr + FM_SET_INTERRUPT_ENABLE); wake_up_process(fm->media_switcher); return 0; -- cgit v1.2.3 From 41d78f7405659b55e082c5f0b3d1b625e75e1294 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:37 +1100 Subject: tifm_core: add suspend/resume infrastructure for tifm devices Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_core.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/tifm.h | 3 +++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 4d62dab2ada..6b10ebe9d93 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -14,7 +14,7 @@ #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 *dev, char **envp, int num_envp, 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) @@ -233,6 +264,8 @@ int tifm_register_driver(struct tifm_driver *drv) 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/include/linux/tifm.h b/include/linux/tifm.h index e5a8295f9fb..3deb0a6c137 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -101,6 +101,9 @@ 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; }; -- cgit v1.2.3 From dba4accab17bd2e2e09088f746257a8c14af1cc2 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Mon, 11 Dec 2006 01:55:38 +1100 Subject: tifm_sd: add suspend and resume functionality Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 2adfe346701..ce19b045ca6 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -921,6 +921,41 @@ static void tifm_sd_remove(struct tifm_dev *sock) mmc_free_host(mmc); } +#ifdef CONFIG_PM + +static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + int rc; + + 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 int tifm_sd_resume(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); + + if (sock->media_id != FM_SD + || tifm_sd_initialize_host(host)) { + tifm_eject(sock); + return 0; + } else { + return mmc_resume_host(mmc); + } +} + +#else + +#define tifm_sd_suspend NULL +#define tifm_sd_resume NULL + +#endif /* CONFIG_PM */ + static tifm_media_id tifm_sd_id_tbl[] = { FM_SD, 0 }; @@ -932,7 +967,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) -- cgit v1.2.3 From fe4a3c7a20f14d86022a8132adbf6ddb98e7197c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 21 Nov 2006 17:54:23 +0100 Subject: 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/mmc/at91_mci.c | 2 ++ drivers/mmc/au1xmmc.c | 2 ++ drivers/mmc/imxmmc.c | 1 + drivers/mmc/mmc.c | 4 ++++ drivers/mmc/mmci.c | 5 +++++ drivers/mmc/omap.c | 1 + drivers/mmc/pxamci.c | 5 +++++ drivers/mmc/sdhci.c | 24 ++++++++++++++---------- drivers/mmc/sdhci.h | 1 - drivers/mmc/tifm_sd.c | 4 +++- drivers/mmc/wbsd.c | 6 ++++++ include/linux/mmc/host.h | 1 + 12 files changed, 44 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index aa152f31851..e28850dec9e 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -823,6 +823,8 @@ static int __init at91_mci_probe(struct platform_device *pdev) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_BYTEBLOCK; + mmc->max_blk_size = 4095; + host = mmc_priv(mmc); host->mmc = mmc; host->buffer = NULL; diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 6d6fe6e6d41..74e6ac0c7f7 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -922,6 +922,8 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev) mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; + mmc->max_blk_size = 2048; + mmc->ocr_avail = AU1XMMC_OCR; host = mmc_priv(mmc); diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index bfb9ff69320..2107f8a8605 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -960,6 +960,7 @@ static int imxmci_probe(struct platform_device *pdev) 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_blk_size = 2048; host = mmc_priv(mmc); host->mmc = mmc; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b48c277312d..9bda3fddad1 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -108,6 +108,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) mrq->cmd->error = 0; mrq->cmd->mrq = mrq; if (mrq->data) { + BUG_ON(mrq->data->blksz > host->max_blk_size); + mrq->cmd->data = mrq->data; mrq->data->error = 0; mrq->data->mrq = mrq; @@ -1605,6 +1607,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->max_phys_segs = 1; host->max_sectors = 1 << (PAGE_CACHE_SHIFT - 9); host->max_seg_size = PAGE_CACHE_SIZE; + + host->max_blk_size = 512; } return host; diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c index ccfe6561be2..5d48e008189 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -534,6 +534,11 @@ static int mmci_probe(struct amba_device *dev, void *id) */ mmc->max_seg_size = mmc->max_sectors << 9; + /* + * Block size can be up to 2048 bytes, but must be a power of two. + */ + mmc->max_blk_size = 2048; + spin_lock_init(&host->lock); writel(0, host->base + MMCIMASK0); diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c index d30540b2761..fa69a0dc596 100644 --- a/drivers/mmc/omap.c +++ b/drivers/mmc/omap.c @@ -1099,6 +1099,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev) */ mmc->max_phys_segs = 32; mmc->max_hw_segs = 32; + mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ mmc->max_sectors = 256; /* NBLK max 11-bits, OMAP also limited by DMA */ mmc->max_seg_size = mmc->max_sectors * 512; diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index 6073d998b11..9fc9aed1a5e 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -450,6 +450,11 @@ static int pxamci_probe(struct platform_device *pdev) */ mmc->max_seg_size = PAGE_SIZE; + /* + * Block length register is 10 bits. + */ + mmc->max_blk_size = 1023; + host = mmc_priv(mmc); host->mmc = mmc; host->dma = -1; diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 175a9427b9b..155aafe69bf 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -381,7 +381,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) /* 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 */ @@ -1290,15 +1290,6 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) 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. */ @@ -1352,6 +1343,19 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) */ mmc->max_seg_size = mmc->max_sectors * 512; + /* + * 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; + /* * Init tasklets. */ diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index f9d1a0a6f03..bc6bf7e7757 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -174,7 +174,6 @@ struct sdhci_host { 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 ce19b045ca6..bdfad15371b 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -886,7 +886,9 @@ static int tifm_sd_probe(struct tifm_dev *sock) 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 + //2k maximum hw block length + mmc->max_seg_size = mmc->max_sectors << 11; + mmc->max_blk_size = 2048; sock->signal_irq = tifm_sd_signal_irq; rc = tifm_sd_initialize_host(host); diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 7a282672f8e..5711beecb4e 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -1354,6 +1354,12 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) */ mmc->max_seg_size = mmc->max_sectors * 512; + /* + * 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; + dev_set_drvdata(dev, mmc); return 0; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ae98d6766bd..2da0c918a8c 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -94,6 +94,7 @@ struct mmc_host { 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_blk_size; /* maximum size of one mmc block */ /* private data */ struct mmc_ios ios; /* current io bus settings */ -- cgit v1.2.3 From 55db890a838c7b37256241b1fc53d6344aa79cc0 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 21 Nov 2006 17:55:45 +0100 Subject: 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 --- drivers/mmc/at91_mci.c | 1 + drivers/mmc/au1xmmc.c | 1 + drivers/mmc/imxmmc.c | 3 ++- drivers/mmc/mmc.c | 6 +++++- drivers/mmc/mmc_block.c | 4 +++- drivers/mmc/mmc_queue.c | 2 +- drivers/mmc/mmci.c | 10 +++++++--- drivers/mmc/omap.c | 5 +++-- drivers/mmc/pxamci.c | 5 +++++ drivers/mmc/sdhci.c | 13 +++++++++---- drivers/mmc/tifm_sd.c | 9 ++++++--- drivers/mmc/wbsd.c | 15 ++++++++++----- include/linux/mmc/host.h | 3 ++- 13 files changed, 55 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index e28850dec9e..2ce50f38e3c 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -824,6 +824,7 @@ static int __init at91_mci_probe(struct platform_device *pdev) 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; diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 74e6ac0c7f7..b834be261ab 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -923,6 +923,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev) mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; mmc->max_blk_size = 2048; + mmc->max_blk_count = 512; mmc->ocr_avail = AU1XMMC_OCR; diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index 2107f8a8605..b060d4bfba2 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -958,9 +958,10 @@ static int imxmci_probe(struct platform_device *pdev) /* 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 9bda3fddad1..fb04bdd26c3 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -109,6 +109,9 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) 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; @@ -1605,10 +1608,11 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) */ 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_block.c b/drivers/mmc/mmc_block.c index 5a4eacac0bb..19ccfed8a54 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -242,10 +242,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) 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); diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c index 3e35a43819f..c27e42645cd 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, struct mmc_card *card, spinlock_t *lock 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/mmci.c b/drivers/mmc/mmci.c index 5d48e008189..5941dd951e8 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -524,21 +524,25 @@ static int mmci_probe(struct amba_device *dev, void *id) /* * 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); writel(0, host->base + MMCIMASK0); diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c index fa69a0dc596..1e96a2f6502 100644 --- a/drivers/mmc/omap.c +++ b/drivers/mmc/omap.c @@ -1100,8 +1100,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev) mmc->max_phys_segs = 32; mmc->max_hw_segs = 32; mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ - mmc->max_sectors = 256; /* NBLK max 11-bits, OMAP also limited by DMA */ - mmc->max_seg_size = mmc->max_sectors * 512; + 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 9fc9aed1a5e..9774fc68b61 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -455,6 +455,11 @@ static int pxamci_probe(struct platform_device *pdev) */ 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 155aafe69bf..99f1db92295 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1333,15 +1333,15 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) /* * 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_sectors * 512; + mmc->max_seg_size = mmc->max_req_size; /* * Maximum block size. This varies from controller to controller and @@ -1356,6 +1356,11 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) } mmc->max_blk_size = 512 << mmc->max_blk_size; + /* + * Maximum block count. + */ + mmc->max_blk_count = 65535; + /* * Init tasklets. */ diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index bdfad15371b..7e607b75f4b 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -885,10 +885,13 @@ static int tifm_sd_probe(struct tifm_dev *sock) mmc->f_max = 24000000; mmc->max_hw_segs = 1; mmc->max_phys_segs = 1; - mmc->max_sectors = 127; - //2k maximum hw block length - mmc->max_seg_size = mmc->max_sectors << 11; + // 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); diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 5711beecb4e..cf16e44c030 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -1343,16 +1343,15 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) 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_sectors * 512; + mmc->max_seg_size = mmc->max_req_size; /* * Maximum block size. We have 12 bits (= 4095) but have to subtract @@ -1360,6 +1359,12 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) */ 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_blk_count = mmc->max_req_size; + dev_set_drvdata(dev, mmc); return 0; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 2da0c918a8c..913e5752569 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -92,9 +92,10 @@ struct mmc_host { 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 */ -- cgit v1.2.3 From 5ba593a97206fb96dc0e63f209e6ade86452844f Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 21 Nov 2006 17:45:37 +0100 Subject: 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 --- drivers/mmc/wbsd.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index cf16e44c030..c1dd6ad8dab 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 @@ -909,6 +909,45 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) * transfered. */ 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. */ -- cgit v1.2.3 From ae06eaf9abb1fd00e413753786e13406eda5819a Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 7 Jan 2007 16:59:06 +0100 Subject: 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 --- drivers/mmc/mmc.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index fb04bdd26c3..5046a166134 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1092,7 +1092,8 @@ static void mmc_process_ext_csds(struct mmc_host *host) 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; } @@ -1108,7 +1109,6 @@ static void mmc_process_ext_csds(struct mmc_host *host) 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; } @@ -1289,7 +1289,9 @@ static void mmc_read_switch_caps(struct mmc_host *host) 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; } @@ -1321,12 +1323,8 @@ static void mmc_read_switch_caps(struct mmc_host *host) 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)); -- cgit v1.2.3 From fac8899129a0490020a0734cc84c1a94ac72c7e1 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 27 Jan 2007 13:18:26 +0100 Subject: mmc: change wbsd mailing list The wbsd-devel list has been shut down. Refer people to LKML instead. Signed-off-by: Pierre Ossman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0ad8803a0c7..3375ed1a9c0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3647,7 +3647,7 @@ S: Maintained W83L51xD SD/MMC CARD INTERFACE DRIVER P: Pierre Ossman M: drzeus-wbsd@drzeus.cx -L: wbsd-devel@list.drzeus.cx +L: linux-kernel@vger.kernel.org W: http://projects.drzeus.cx/wbsd S: Maintained -- cgit v1.2.3 From 397411e67ff473c36161d93e4c7ac6dc53e23503 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 30 Jan 2007 07:48:04 +0100 Subject: mmc: Proper unclaim in mmc_block Make sure we release the claim on the host even on failure. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc_block.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 19ccfed8a54..05ba8ace70e 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -379,9 +379,10 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) spin_unlock_irq(&md->lock); } +flush_queue: + mmc_card_release_host(card); -flush_queue: spin_lock_irq(&md->lock); while (ret) { ret = end_that_request_chunk(req, 0, -- cgit v1.2.3 From df1c4b7bf7f3b3a48d78c6e5c2fc5b9a1c01b821 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 30 Jan 2007 07:55:15 +0100 Subject: mmc: handle pci_enable_device() return value in sdhci Make sure we report back any errors from pci_enable_device(). Signed-off-by: Pierre Ossman --- drivers/mmc/sdhci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 99f1db92295..b57393c7f9b 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1125,7 +1125,9 @@ static int sdhci_resume (struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_enable_device(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; for (i = 0;i < chip->num_slots;i++) { if (!chip->hosts[i]) -- cgit v1.2.3 From 4a0ddbd25ad4e03a0a1657f5cb2259c9a35fe9e6 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 31 Jan 2007 18:20:48 +0100 Subject: mmc: wbsd: replace kmap with page_address Since we actively avoid highmem, calling kmap_atomic() instead of page_address() is effectively only obfuscation. Signed-off-by: Pierre Ossman --- drivers/mmc/wbsd.c | 40 ++++++++++------------------------------ drivers/mmc/wbsd.h | 1 - 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index c1dd6ad8dab..a44d8777ab9 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -272,16 +272,9 @@ static inline int wbsd_next_sg(struct wbsd_host *host) return host->num_sg; } -static inline char *wbsd_kmap_sg(struct wbsd_host *host) +static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) { - host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) + - host->cur_sg->offset; - return host->mapped_sg; -} - -static inline void wbsd_kunmap_sg(struct wbsd_host *host) -{ - kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ); + return page_address(host->cur_sg->page) + host->cur_sg->offset; } static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) @@ -302,12 +295,11 @@ static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) * we do not transfer too much. */ for (i = 0; i < len; i++) { - sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset; + sgbuf = page_address(sg[i].page) + sg[i].offset; if (size < sg[i].length) memcpy(dmabuf, sgbuf, size); else memcpy(dmabuf, sgbuf, sg[i].length); - kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ); dmabuf += sg[i].length; if (size < sg[i].length) @@ -347,7 +339,7 @@ static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) * we do not transfer too much. */ for (i = 0; i < len; i++) { - sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset; + sgbuf = page_address(sg[i].page) + sg[i].offset; if (size < sg[i].length) memcpy(sgbuf, dmabuf, size); else @@ -497,7 +489,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) if (data->bytes_xfered == host->size) return; - buffer = wbsd_kmap_sg(host) + host->offset; + buffer = wbsd_sg_to_buffer(host) + host->offset; /* * Drain the fifo. This has a tendency to loop longer @@ -526,17 +518,13 @@ static void wbsd_empty_fifo(struct wbsd_host *host) /* * Transfer done? */ - if (data->bytes_xfered == host->size) { - wbsd_kunmap_sg(host); + if (data->bytes_xfered == host->size) return; - } /* * End of scatter list entry? */ if (host->remain == 0) { - wbsd_kunmap_sg(host); - /* * Get next entry. Check if last. */ @@ -554,13 +542,11 @@ static void wbsd_empty_fifo(struct wbsd_host *host) return; } - buffer = wbsd_kmap_sg(host); + buffer = wbsd_sg_to_buffer(host); } } } - wbsd_kunmap_sg(host); - /* * This is a very dirty hack to solve a * hardware problem. The chip doesn't trigger @@ -583,7 +569,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) if (data->bytes_xfered == host->size) return; - buffer = wbsd_kmap_sg(host) + host->offset; + buffer = wbsd_sg_to_buffer(host) + host->offset; /* * Fill the fifo. This has a tendency to loop longer @@ -612,17 +598,13 @@ static void wbsd_fill_fifo(struct wbsd_host *host) /* * Transfer done? */ - if (data->bytes_xfered == host->size) { - wbsd_kunmap_sg(host); + if (data->bytes_xfered == host->size) return; - } /* * End of scatter list entry? */ if (host->remain == 0) { - wbsd_kunmap_sg(host); - /* * Get next entry. Check if last. */ @@ -640,13 +622,11 @@ static void wbsd_fill_fifo(struct wbsd_host *host) return; } - buffer = wbsd_kmap_sg(host); + buffer = wbsd_sg_to_buffer(host); } } } - wbsd_kunmap_sg(host); - /* * The controller stops sending interrupts for * 'FIFO empty' under certain conditions. So we diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h index 6072993f01e..d06718b0e2a 100644 --- a/drivers/mmc/wbsd.h +++ b/drivers/mmc/wbsd.h @@ -154,7 +154,6 @@ struct wbsd_host struct scatterlist* cur_sg; /* Current SG entry */ unsigned int num_sg; /* Number of entries left */ - void* mapped_sg; /* vaddr of mapped sg */ unsigned int offset; /* Offset into current entry */ unsigned int remain; /* Data left in curren entry */ -- cgit v1.2.3 From 2a22b14edfdf1dce303ec48bb934a6a2edb278b5 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 2 Feb 2007 18:27:42 +0100 Subject: mmc: sdhci: replace kmap with page_address Since we actively avoid highmem, calling kmap_atomic() instead of page_address() is effectively only obfuscation. Signed-off-by: Pierre Ossman --- drivers/mmc/sdhci.c | 24 ++++++------------------ drivers/mmc/sdhci.h | 1 - 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index b57393c7f9b..24803538570 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -206,15 +206,9 @@ static void sdhci_deactivate_led(struct sdhci_host *host) * * \*****************************************************************************/ -static inline char* sdhci_kmap_sg(struct sdhci_host* host) +static inline char* sdhci_sg_to_buffer(struct sdhci_host* host) { - host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ); - return host->mapped_sg + host->cur_sg->offset; -} - -static inline void sdhci_kunmap_sg(struct sdhci_host* host) -{ - kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ); + return page_address(host->cur_sg->page) + host->cur_sg->offset; } static inline int sdhci_next_sg(struct sdhci_host* host) @@ -249,7 +243,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host) chunk_remain = 0; data = 0; - buffer = sdhci_kmap_sg(host) + host->offset; + buffer = sdhci_sg_to_buffer(host) + host->offset; while (blksize) { if (chunk_remain == 0) { @@ -273,16 +267,13 @@ static void sdhci_read_block_pio(struct sdhci_host *host) } if (host->remain == 0) { - sdhci_kunmap_sg(host); if (sdhci_next_sg(host) == 0) { BUG_ON(blksize != 0); return; } - buffer = sdhci_kmap_sg(host); + buffer = sdhci_sg_to_buffer(host); } } - - sdhci_kunmap_sg(host); } static void sdhci_write_block_pio(struct sdhci_host *host) @@ -299,7 +290,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host) data = 0; bytes = 0; - buffer = sdhci_kmap_sg(host) + host->offset; + buffer = sdhci_sg_to_buffer(host) + host->offset; while (blksize) { size = min(host->size, host->remain); @@ -323,16 +314,13 @@ static void sdhci_write_block_pio(struct sdhci_host *host) } if (host->remain == 0) { - sdhci_kunmap_sg(host); if (sdhci_next_sg(host) == 0) { BUG_ON(blksize != 0); return; } - buffer = sdhci_kmap_sg(host); + buffer = sdhci_sg_to_buffer(host); } } - - sdhci_kunmap_sg(host); } static void sdhci_transfer_pio(struct sdhci_host *host) diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index bc6bf7e7757..e324f0a623d 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -183,7 +183,6 @@ struct sdhci_host { struct mmc_data *data; /* Current data request */ struct scatterlist *cur_sg; /* We're working on this */ - char *mapped_sg; /* This is where it's mapped */ int num_sg; /* Entries left */ int offset; /* Offset into current sg */ int remain; /* Bytes left in current */ -- cgit v1.2.3 From c70840e819acdbab96b8cdf71d27cb68c6567efa Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 2 Feb 2007 22:41:41 +0100 Subject: mmc: sdhci: fix voltage ocr Some bad if-clauses caused the driver to just report the highest supported voltage, not all. Signed-off-by: Pierre Ossman --- drivers/mmc/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 24803538570..4bf1fea5e2c 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1291,9 +1291,9 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc->ocr_avail = 0; if (caps & SDHCI_CAN_VDD_330) mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; - else if (caps & SDHCI_CAN_VDD_300) + if (caps & SDHCI_CAN_VDD_300) mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; - else if (caps & SDHCI_CAN_VDD_180) + if (caps & SDHCI_CAN_VDD_180) mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; if ((host->max_clk > 25000000) && !(caps & SDHCI_CAN_DO_HISPD)) { -- cgit v1.2.3 From f9d429a2e579ed7c51c49a81265f7e7d2c59c197 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 3 Feb 2007 13:36:41 +0100 Subject: mmc: tifm: replace kmap with page_address Since we actively avoid highmem, calling kmap_atomic() instead of page_address() is effectively only obfuscation. Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 7e607b75f4b..e65f8a0a934 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -107,14 +107,9 @@ struct tifm_sd { }; -static char* tifm_sd_kmap_atomic(struct mmc_data *data) +static char* tifm_sd_data_buffer(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); + return page_address(data->sg->page) + data->sg->offset; } static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, @@ -127,18 +122,17 @@ static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, if (host_status & TIFM_MMCSD_BRS) { /* in non-dma rx mode BRS fires when fifo is still not empty */ if (no_dma && (cmd->data->flags & MMC_DATA_READ)) { - buffer = tifm_sd_kmap_atomic(host->req->data); + buffer = tifm_sd_data_buffer(host->req->data); while (host->buffer_size > host->buffer_pos) { t_val = readl(sock->addr + SOCK_MMCSD_DATA); 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 (no_dma) { - buffer = tifm_sd_kmap_atomic(host->req->data); + buffer = tifm_sd_data_buffer(host->req->data); if ((cmd->data->flags & MMC_DATA_READ) && (host_status & TIFM_MMCSD_AF)) { for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { @@ -163,7 +157,6 @@ static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, } } } - tifm_sd_kunmap_atomic(buffer, host->req->data); } return 0; } -- cgit v1.2.3