From c5f93cf19df633a8dbd7adf8130d604eec96e145 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 19 Feb 2007 11:34:43 +0100 Subject: [PATCH 1/4] ncpfs: make sure server connection survives a kill Use internal buffers instead of the ones supplied by the caller so that a caller can be interrupted without having to abort the entire ncp connection. Signed-off-by: Pierre Ossman Acked-by: Petr Vandrovec --- fs/ncpfs/inode.c | 16 ++++- fs/ncpfs/sock.c | 147 ++++++++++++++++++++++---------------- include/linux/ncp_fs_sb.h | 2 + 3 files changed, 103 insertions(+), 62 deletions(-) diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 14939ddf74f1..7285c94956c4 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -576,6 +576,12 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) server->packet = vmalloc(NCP_PACKET_SIZE); if (server->packet == NULL) goto out_nls; + server->txbuf = vmalloc(NCP_PACKET_SIZE); + if (server->txbuf == NULL) + goto out_packet; + server->rxbuf = vmalloc(NCP_PACKET_SIZE); + if (server->rxbuf == NULL) + goto out_txbuf; sock->sk->sk_data_ready = ncp_tcp_data_ready; sock->sk->sk_error_report = ncp_tcp_error_report; @@ -597,7 +603,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) error = ncp_connect(server); ncp_unlock_server(server); if (error < 0) - goto out_packet; + goto out_rxbuf; DPRINTK("ncp_fill_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb)); error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */ @@ -666,8 +672,12 @@ out_disconnect: ncp_lock_server(server); ncp_disconnect(server); ncp_unlock_server(server); -out_packet: +out_rxbuf: ncp_stop_tasks(server); + vfree(server->rxbuf); +out_txbuf: + vfree(server->txbuf); +out_packet: vfree(server->packet); out_nls: #ifdef CONFIG_NCPFS_NLS @@ -723,6 +733,8 @@ static void ncp_put_super(struct super_block *sb) kfree(server->priv.data); kfree(server->auth.object_name); + vfree(server->rxbuf); + vfree(server->txbuf); vfree(server->packet); sb->s_fs_info = NULL; kfree(server); diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index e496d8b65e92..e37df8d5fe70 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -55,10 +56,11 @@ static int _send(struct socket *sock, const void *buff, int len) struct ncp_request_reply { struct list_head req; wait_queue_head_t wq; - struct ncp_reply_header* reply_buf; + atomic_t refs; + unsigned char* reply_buf; size_t datalen; int result; - enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status; + enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE, RQ_ABANDONED } status; struct kvec* tx_ciov; size_t tx_totallen; size_t tx_iovlen; @@ -67,6 +69,32 @@ struct ncp_request_reply { u_int32_t sign[6]; }; +static inline struct ncp_request_reply* ncp_alloc_req(void) +{ + struct ncp_request_reply *req; + + req = kmalloc(sizeof(struct ncp_request_reply), GFP_KERNEL); + if (!req) + return NULL; + + init_waitqueue_head(&req->wq); + atomic_set(&req->refs, (1)); + req->status = RQ_IDLE; + + return req; +} + +static void ncp_req_get(struct ncp_request_reply *req) +{ + atomic_inc(&req->refs); +} + +static void ncp_req_put(struct ncp_request_reply *req) +{ + if (atomic_dec_and_test(&req->refs)) + kfree(req); +} + void ncp_tcp_data_ready(struct sock *sk, int len) { struct ncp_server *server = sk->sk_user_data; @@ -101,14 +129,17 @@ void ncpdgram_timeout_call(unsigned long v) schedule_work(&server->timeout_tq); } -static inline void ncp_finish_request(struct ncp_request_reply *req, int result) +static inline void ncp_finish_request(struct ncp_server *server, struct ncp_request_reply *req, int result) { req->result = result; + if (req->status != RQ_ABANDONED) + memcpy(req->reply_buf, server->rxbuf, req->datalen); req->status = RQ_DONE; wake_up_all(&req->wq); + ncp_req_put(req); } -static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) +static void __abort_ncp_connection(struct ncp_server *server) { struct ncp_request_reply *req; @@ -118,31 +149,19 @@ static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request req = list_entry(server->tx.requests.next, struct ncp_request_reply, req); list_del_init(&req->req); - if (req == aborted) { - ncp_finish_request(req, err); - } else { - ncp_finish_request(req, -EIO); - } + ncp_finish_request(server, req, -EIO); } req = server->rcv.creq; if (req) { server->rcv.creq = NULL; - if (req == aborted) { - ncp_finish_request(req, err); - } else { - ncp_finish_request(req, -EIO); - } + ncp_finish_request(server, req, -EIO); server->rcv.ptr = NULL; server->rcv.state = 0; } req = server->tx.creq; if (req) { server->tx.creq = NULL; - if (req == aborted) { - ncp_finish_request(req, err); - } else { - ncp_finish_request(req, -EIO); - } + ncp_finish_request(server, req, -EIO); } } @@ -160,10 +179,12 @@ static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_req break; case RQ_QUEUED: list_del_init(&req->req); - ncp_finish_request(req, err); + ncp_finish_request(server, req, err); break; case RQ_INPROGRESS: - __abort_ncp_connection(server, req, err); + req->status = RQ_ABANDONED; + break; + case RQ_ABANDONED: break; } } @@ -177,7 +198,7 @@ static inline void ncp_abort_request(struct ncp_server *server, struct ncp_reque static inline void __ncptcp_abort(struct ncp_server *server) { - __abort_ncp_connection(server, NULL, 0); + __abort_ncp_connection(server); } static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req) @@ -294,6 +315,11 @@ static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_r static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req) { + /* we copy the data so that we do not depend on the caller + staying alive */ + memcpy(server->txbuf, req->tx_iov[1].iov_base, req->tx_iov[1].iov_len); + req->tx_iov[1].iov_base = server->txbuf; + if (server->ncp_sock->type == SOCK_STREAM) ncptcp_start_request(server, req); else @@ -308,6 +334,7 @@ static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply * printk(KERN_ERR "ncpfs: tcp: Server died\n"); return -EIO; } + ncp_req_get(req); if (server->tx.creq || server->rcv.creq) { req->status = RQ_QUEUED; list_add_tail(&req->req, &server->tx.requests); @@ -409,7 +436,7 @@ void ncpdgram_rcv_proc(struct work_struct *work) server->timeout_last = NCP_MAX_RPC_TIMEOUT; mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT); } else if (reply.type == NCP_REPLY) { - result = _recv(sock, (void*)req->reply_buf, req->datalen, MSG_DONTWAIT); + result = _recv(sock, server->rxbuf, req->datalen, MSG_DONTWAIT); #ifdef CONFIG_NCPFS_PACKET_SIGNING if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { if (result < 8 + 8) { @@ -419,7 +446,7 @@ void ncpdgram_rcv_proc(struct work_struct *work) result -= 8; hdrl = sock->sk->sk_family == AF_INET ? 8 : 6; - if (sign_verify_reply(server, ((char*)req->reply_buf) + hdrl, result - hdrl, cpu_to_le32(result), ((char*)req->reply_buf) + result)) { + if (sign_verify_reply(server, server->rxbuf + hdrl, result - hdrl, cpu_to_le32(result), server->rxbuf + result)) { printk(KERN_INFO "ncpfs: Signature violation\n"); result = -EIO; } @@ -428,7 +455,7 @@ void ncpdgram_rcv_proc(struct work_struct *work) #endif del_timer(&server->timeout_tm); server->rcv.creq = NULL; - ncp_finish_request(req, result); + ncp_finish_request(server, req, result); __ncp_next_request(server); mutex_unlock(&server->rcv.creq_mutex); continue; @@ -478,12 +505,6 @@ void ncpdgram_timeout_proc(struct work_struct *work) mutex_unlock(&server->rcv.creq_mutex); } -static inline void ncp_init_req(struct ncp_request_reply* req) -{ - init_waitqueue_head(&req->wq); - req->status = RQ_IDLE; -} - static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) { int result; @@ -601,8 +622,8 @@ skipdata:; goto skipdata; } req->datalen = datalen - 8; - req->reply_buf->type = NCP_REPLY; - server->rcv.ptr = (unsigned char*)(req->reply_buf) + 2; + ((struct ncp_reply_header*)server->rxbuf)->type = NCP_REPLY; + server->rcv.ptr = server->rxbuf + 2; server->rcv.len = datalen - 10; server->rcv.state = 1; break; @@ -615,12 +636,12 @@ skipdata:; case 1: req = server->rcv.creq; if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) { - if (req->reply_buf->sequence != server->sequence) { + if (((struct ncp_reply_header*)server->rxbuf)->sequence != server->sequence) { printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n"); __ncp_abort_request(server, req, -EIO); return -EIO; } - if ((req->reply_buf->conn_low | (req->reply_buf->conn_high << 8)) != server->connection) { + if ((((struct ncp_reply_header*)server->rxbuf)->conn_low | (((struct ncp_reply_header*)server->rxbuf)->conn_high << 8)) != server->connection) { printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n"); __ncp_abort_request(server, req, -EIO); return -EIO; @@ -628,14 +649,14 @@ skipdata:; } #ifdef CONFIG_NCPFS_PACKET_SIGNING if (server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { - if (sign_verify_reply(server, (unsigned char*)(req->reply_buf) + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) { + if (sign_verify_reply(server, server->rxbuf + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) { printk(KERN_ERR "ncpfs: tcp: Signature violation\n"); __ncp_abort_request(server, req, -EIO); return -EIO; } } #endif - ncp_finish_request(req, req->datalen); + ncp_finish_request(server, req, req->datalen); nextreq:; __ncp_next_request(server); case 2: @@ -645,7 +666,7 @@ skipdata:; server->rcv.state = 0; break; case 3: - ncp_finish_request(server->rcv.creq, -EIO); + ncp_finish_request(server, server->rcv.creq, -EIO); goto nextreq; case 5: info_server(server, 0, server->unexpected_packet.data, server->unexpected_packet.len); @@ -675,28 +696,39 @@ void ncp_tcp_tx_proc(struct work_struct *work) } static int do_ncp_rpc_call(struct ncp_server *server, int size, - struct ncp_reply_header* reply_buf, int max_reply_size) + unsigned char* reply_buf, int max_reply_size) { int result; - struct ncp_request_reply req; + struct ncp_request_reply *req; - ncp_init_req(&req); - req.reply_buf = reply_buf; - req.datalen = max_reply_size; - req.tx_iov[1].iov_base = server->packet; - req.tx_iov[1].iov_len = size; - req.tx_iovlen = 1; - req.tx_totallen = size; - req.tx_type = *(u_int16_t*)server->packet; + req = ncp_alloc_req(); + if (!req) + return -ENOMEM; - result = ncp_add_request(server, &req); - if (result < 0) { - return result; + req->reply_buf = reply_buf; + req->datalen = max_reply_size; + req->tx_iov[1].iov_base = server->packet; + req->tx_iov[1].iov_len = size; + req->tx_iovlen = 1; + req->tx_totallen = size; + req->tx_type = *(u_int16_t*)server->packet; + + result = ncp_add_request(server, req); + if (result < 0) + goto out; + + if (wait_event_interruptible(req->wq, req->status == RQ_DONE)) { + ncp_abort_request(server, req, -EINTR); + result = -EINTR; + goto out; } - if (wait_event_interruptible(req.wq, req.status == RQ_DONE)) { - ncp_abort_request(server, &req, -EIO); - } - return req.result; + + result = req->result; + +out: + ncp_req_put(req); + + return result; } /* @@ -751,11 +783,6 @@ static int ncp_do_request(struct ncp_server *server, int size, DDPRINTK("do_ncp_rpc_call returned %d\n", result); - if (result < 0) { - /* There was a problem with I/O, so the connections is - * no longer usable. */ - ncp_invalidate_conn(server); - } return result; } diff --git a/include/linux/ncp_fs_sb.h b/include/linux/ncp_fs_sb.h index a503052138bd..6330fc76b00f 100644 --- a/include/linux/ncp_fs_sb.h +++ b/include/linux/ncp_fs_sb.h @@ -50,6 +50,8 @@ struct ncp_server { int packet_size; unsigned char *packet; /* Here we prepare requests and receive replies */ + unsigned char *txbuf; /* Storage for current request */ + unsigned char *rxbuf; /* Storage for reply to current request */ int lock; /* To prevent mismatch in protocols. */ struct mutex mutex; From cd9277c011a99769fa371521b460ed57f6d280b1 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 18 Feb 2007 12:07:47 +0100 Subject: [PATCH 2/4] mmc: require explicit support for high-speed The new high-speed timings are similar to each other and the old system, but not identical. And although things "just work" most of the time, sometimes it does not. So we need to start marking which hosts are known to fully comply with the new timings. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 87 +++++++++++++++++++++++----------------- drivers/mmc/sdhci.c | 25 +++++------- include/linux/mmc/host.h | 8 ++++ 3 files changed, 68 insertions(+), 52 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5046a1661342..4a73e8b2428d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -376,10 +376,11 @@ static inline void mmc_set_ios(struct mmc_host *host) { struct mmc_ios *ios = &host->ios; - pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", + pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u " + "width %u timing %u\n", mmc_hostname(host), ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, ios->vdd, - ios->bus_width); + ios->bus_width, ios->timing); host->ops->set_ios(host, ios); } @@ -809,6 +810,7 @@ static void mmc_power_up(struct mmc_host *host) host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_UP; host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); mmc_delay(1); @@ -828,6 +830,7 @@ static void mmc_power_off(struct mmc_host *host) host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_OFF; host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); } @@ -1112,46 +1115,50 @@ static void mmc_process_ext_csds(struct mmc_host *host) continue; } - /* Activate highspeed support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_HS_TIMING << 16) | - (1 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + if (host->caps & MMC_CAP_MMC_HIGHSPEED) { + /* Activate highspeed support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_HS_TIMING << 16) | + (1 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to mmc v4 " - "high-speed mode.\n", - mmc_hostname(card->host)); - continue; + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to mmc v4 " + "high-speed mode.\n", + mmc_hostname(card->host)); + continue; + } + + mmc_card_set_highspeed(card); + + host->ios.timing = MMC_TIMING_SD_HS; + mmc_set_ios(host); } - mmc_card_set_highspeed(card); - /* Check for host support for wide-bus modes. */ - if (!(host->caps & MMC_CAP_4_BIT_DATA)) { - continue; + if (host->caps & MMC_CAP_4_BIT_DATA) { + /* Activate 4-bit support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BUS_WIDTH << 16) | + (EXT_CSD_BUS_WIDTH_4 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to " + "mmc v4 4-bit bus mode.\n", + mmc_hostname(card->host)); + continue; + } + + host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(host); } - - /* Activate 4-bit support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_BUS_WIDTH << 16) | - (EXT_CSD_BUS_WIDTH_4 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to " - "mmc v4 4-bit bus mode.\n", - mmc_hostname(card->host)); - continue; - } - - host->ios.bus_width = MMC_BUS_WIDTH_4; } kfree(ext_csd); @@ -1241,6 +1248,9 @@ static void mmc_read_switch_caps(struct mmc_host *host) unsigned char *status; struct scatterlist sg; + if (!(host->caps & MMC_CAP_SD_HIGHSPEED)) + return; + status = kmalloc(64, GFP_KERNEL); if (!status) { printk(KERN_WARNING "%s: Unable to allocate buffer for " @@ -1332,6 +1342,9 @@ static void mmc_read_switch_caps(struct mmc_host *host) } mmc_card_set_highspeed(card); + + host->ios.timing = MMC_TIMING_SD_HS; + mmc_set_ios(host); } kfree(status); diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 7522f76b15ec..c52b167e8585 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -606,7 +606,6 @@ static void sdhci_finish_command(struct sdhci_host *host) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div; - u8 ctrl; u16 clk; unsigned long timeout; @@ -615,13 +614,6 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); - if (clock > 25000000) - ctrl |= SDHCI_CTRL_HISPD; - else - ctrl &= ~SDHCI_CTRL_HISPD; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); - if (clock == 0) goto out; @@ -761,10 +753,17 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_set_power(host, ios->vdd); ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + if (ios->bus_width == MMC_BUS_WIDTH_4) ctrl |= SDHCI_CTRL_4BITBUS; else ctrl &= ~SDHCI_CTRL_4BITBUS; + + if (ios->timing == MMC_TIMING_SD_HS) + ctrl |= SDHCI_CTRL_HISPD; + else + ctrl &= ~SDHCI_CTRL_HISPD; + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); mmiowb(); @@ -1274,6 +1273,9 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc->f_max = host->max_clk; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + if (caps & SDHCI_CAN_DO_HISPD) + mmc->caps |= MMC_CAP_SD_HIGHSPEED; + mmc->ocr_avail = 0; if (caps & SDHCI_CAN_VDD_330) mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; @@ -1282,13 +1284,6 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) 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)) { - printk(KERN_ERR "%s: Controller reports > 25 MHz base clock," - " but no high speed support.\n", - host->slot_descr); - mmc->f_max = 25000000; - } - if (mmc->ocr_avail == 0) { printk(KERN_ERR "%s: Hardware doesn't report any " "support voltages.\n", host->slot_descr); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 913e5752569f..bfcef8a1ad8b 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -62,6 +62,12 @@ struct mmc_ios { #define MMC_BUS_WIDTH_1 0 #define MMC_BUS_WIDTH_4 2 + + unsigned char timing; /* timing specification used */ + +#define MMC_TIMING_LEGACY 0 +#define MMC_TIMING_MMC_HS 1 +#define MMC_TIMING_SD_HS 2 }; struct mmc_host_ops { @@ -87,6 +93,8 @@ struct mmc_host { #define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ #define MMC_CAP_MULTIWRITE (1 << 1) /* Can accurately report bytes sent to card on error */ #define MMC_CAP_BYTEBLOCK (1 << 2) /* Can do non-log2 block sizes */ +#define MMC_CAP_MMC_HIGHSPEED (1 << 3) /* Can do MMC high-speed timing */ +#define MMC_CAP_SD_HIGHSPEED (1 << 4) /* Can do SD high-speed timing */ /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ From 62df67a523acd7a22d936bf946b1889dbd60ca98 Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Tue, 6 Mar 2007 13:30:13 +0100 Subject: [PATCH 3/4] sdhci: make isr tolerant of read errors The interrupt is shared with another device, which resumes earlier than the sdhci controller, and generates an interrupt. The sdhci interrupt handler runs, sees 0xffffffff in its own device's interrupt status, and tries to handle it.. The reason for the 0xffffffff is that the device is still suspended, and *all* regs are reading back 0xffffffff. Signed-off-by: Mark Lord Signed-off-by: Andrew Morton Signed-off-by: Pierre Ossman --- drivers/mmc/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index c52b167e8585..f3260ec61fe9 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -993,7 +993,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) intmask = readl(host->ioaddr + SDHCI_INT_STATUS); - if (!intmask) { + if (!intmask || intmask == 0xffffffff) { result = IRQ_NONE; goto out; } From a715dfc7b9ef15ed5b398b185bd84cc015ff37f6 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Mar 2007 13:38:49 +0100 Subject: [PATCH 4/4] sdhci: release irq during suspend Release the device's irq during sleep, as all well-behaved drivers should. Signed-off-by: Pierre Ossman --- drivers/mmc/sdhci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index f3260ec61fe9..d749f08601b8 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1079,6 +1079,13 @@ static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state) pci_save_state(pdev); pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); + + for (i = 0;i < chip->num_slots;i++) { + if (!chip->hosts[i]) + continue; + free_irq(chip->hosts[i]->irq, chip->hosts[i]); + } + pci_disable_device(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); @@ -1107,6 +1114,11 @@ static int sdhci_resume (struct pci_dev *pdev) continue; if (chip->hosts[i]->flags & SDHCI_USE_DMA) pci_set_master(pdev); + ret = request_irq(chip->hosts[i]->irq, sdhci_irq, + IRQF_SHARED, chip->hosts[i]->slot_descr, + chip->hosts[i]); + if (ret) + return ret; sdhci_init(chip->hosts[i]); mmiowb(); ret = mmc_resume_host(chip->hosts[i]->mmc);