diff options
author | Ulf Samuelsson <ulf.samuelsson@atmel.com> | 2007-10-12 15:10:27 +0000 |
---|---|---|
committer | Ulf Samuelsson <ulf.samuelsson@atmel.com> | 2007-10-12 15:10:27 +0000 |
commit | 022b1041523e218a44b75bec783dfd2f0d5371df (patch) | |
tree | 71fe681a269954dcdff70692de990b9f3d84aea9 /target/device/Atmel/arch-avr32/kernel-patches-2.6.22.10/linux-2.6.22.10-502-atmel_mci-fix-two-subtle-but-deadly-races.patch | |
parent | 103832204dd8e5e48c17a4a2a0adee735235f5c3 (diff) | |
download | buildroot-novena-022b1041523e218a44b75bec783dfd2f0d5371df.tar.gz buildroot-novena-022b1041523e218a44b75bec783dfd2f0d5371df.zip |
Add 2.6.22.10 and 2.6.23 support
Diffstat (limited to 'target/device/Atmel/arch-avr32/kernel-patches-2.6.22.10/linux-2.6.22.10-502-atmel_mci-fix-two-subtle-but-deadly-races.patch')
-rw-r--r-- | target/device/Atmel/arch-avr32/kernel-patches-2.6.22.10/linux-2.6.22.10-502-atmel_mci-fix-two-subtle-but-deadly-races.patch | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/target/device/Atmel/arch-avr32/kernel-patches-2.6.22.10/linux-2.6.22.10-502-atmel_mci-fix-two-subtle-but-deadly-races.patch b/target/device/Atmel/arch-avr32/kernel-patches-2.6.22.10/linux-2.6.22.10-502-atmel_mci-fix-two-subtle-but-deadly-races.patch new file mode 100644 index 000000000..be99b8346 --- /dev/null +++ b/target/device/Atmel/arch-avr32/kernel-patches-2.6.22.10/linux-2.6.22.10-502-atmel_mci-fix-two-subtle-but-deadly-races.patch @@ -0,0 +1,438 @@ +From: Haavard Skinnemoen <hskinnemoen@atmel.com> +Subject: [PATCH 2/2] atmel_mci: Fix two subtle but deadly races + +This patch fixes two possible races in the atmel_mci driver, at least +one of which may cause card probing to fail. + +The first one may happen if a command fails and the next command is +queued before the controller is ready to accept a new one. Fix this by +not enabling error interrupts for commands and instead do any error +handling when the CMDRDY bit has been set. + +The second one may happen after a successful read data transfer where +then next command is queued after the DMA transfer is complete, but +before the whole data transfer from the card is complete (i.e. the +card is still sending CRC, for example.) Fix this by waiting for the +NOTBUSY bit to be set before considering the request to be done. This +will also ensure that we actually see any CRC failures before +completing the request. + +Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com> +--- + drivers/mmc/host/atmel-mci.c | 172 +++++++++++++----------------------------- + 1 files changed, 54 insertions(+), 118 deletions(-) + +diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c +index 1dc91b4..45323c9 100644 +--- a/drivers/mmc/host/atmel-mci.c ++++ b/drivers/mmc/host/atmel-mci.c +@@ -28,20 +28,15 @@ + + #define DRIVER_NAME "atmel_mci" + +-#define MCI_CMD_ERROR_FLAGS (MCI_BIT(RINDE) | MCI_BIT(RDIRE) | \ +- MCI_BIT(RCRCE) | MCI_BIT(RENDE) | \ +- MCI_BIT(RTOE)) + #define MCI_DATA_ERROR_FLAGS (MCI_BIT(DCRCE) | MCI_BIT(DTOE) | \ + MCI_BIT(OVRE) | MCI_BIT(UNRE)) + + enum { + EVENT_CMD_COMPLETE = 0, +- EVENT_CMD_ERROR, + EVENT_DATA_COMPLETE, + EVENT_DATA_ERROR, + EVENT_STOP_SENT, + EVENT_STOP_COMPLETE, +- EVENT_STOP_ERROR, + EVENT_DMA_ERROR, + EVENT_CARD_DETECT, + }; +@@ -61,13 +56,14 @@ struct atmel_mci { + struct mmc_command *cmd; + struct mmc_data *data; + ++ u32 cmd_status; ++ u32 data_status; ++ u32 stop_status; + u32 stop_cmdr; +- u32 stop_iflags; + + struct tasklet_struct tasklet; + unsigned long pending_events; + unsigned long completed_events; +- u32 error_status; + + int present; + int detect_pin; +@@ -99,8 +95,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + /* Test bit macros for completed events */ + #define mci_cmd_is_complete(host) \ + test_bit(EVENT_CMD_COMPLETE, &host->completed_events) +-#define mci_cmd_error_is_complete(host) \ +- test_bit(EVENT_CMD_ERROR, &host->completed_events) + #define mci_data_is_complete(host) \ + test_bit(EVENT_DATA_COMPLETE, &host->completed_events) + #define mci_data_error_is_complete(host) \ +@@ -109,8 +103,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + test_bit(EVENT_STOP_SENT, &host->completed_events) + #define mci_stop_is_complete(host) \ + test_bit(EVENT_STOP_COMPLETE, &host->completed_events) +-#define mci_stop_error_is_complete(host) \ +- test_bit(EVENT_STOP_ERROR, &host->completed_events) + #define mci_dma_error_is_complete(host) \ + test_bit(EVENT_DMA_ERROR, &host->completed_events) + #define mci_card_detect_is_complete(host) \ +@@ -119,8 +111,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + /* Test and clear bit macros for pending events */ + #define mci_clear_cmd_is_pending(host) \ + test_and_clear_bit(EVENT_CMD_COMPLETE, &host->pending_events) +-#define mci_clear_cmd_error_is_pending(host) \ +- test_and_clear_bit(EVENT_CMD_ERROR, &host->pending_events) + #define mci_clear_data_is_pending(host) \ + test_and_clear_bit(EVENT_DATA_COMPLETE, &host->pending_events) + #define mci_clear_data_error_is_pending(host) \ +@@ -129,8 +119,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + test_and_clear_bit(EVENT_STOP_SENT, &host->pending_events) + #define mci_clear_stop_is_pending(host) \ + test_and_clear_bit(EVENT_STOP_COMPLETE, &host->pending_events) +-#define mci_clear_stop_error_is_pending(host) \ +- test_and_clear_bit(EVENT_STOP_ERROR, &host->pending_events) + #define mci_clear_dma_error_is_pending(host) \ + test_and_clear_bit(EVENT_DMA_ERROR, &host->pending_events) + #define mci_clear_card_detect_is_pending(host) \ +@@ -139,8 +127,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + /* Test and set bit macros for completed events */ + #define mci_set_cmd_is_completed(host) \ + test_and_set_bit(EVENT_CMD_COMPLETE, &host->completed_events) +-#define mci_set_cmd_error_is_completed(host) \ +- test_and_set_bit(EVENT_CMD_ERROR, &host->completed_events) + #define mci_set_data_is_completed(host) \ + test_and_set_bit(EVENT_DATA_COMPLETE, &host->completed_events) + #define mci_set_data_error_is_completed(host) \ +@@ -149,8 +135,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + test_and_set_bit(EVENT_STOP_SENT, &host->completed_events) + #define mci_set_stop_is_completed(host) \ + test_and_set_bit(EVENT_STOP_COMPLETE, &host->completed_events) +-#define mci_set_stop_error_is_completed(host) \ +- test_and_set_bit(EVENT_STOP_ERROR, &host->completed_events) + #define mci_set_dma_error_is_completed(host) \ + test_and_set_bit(EVENT_DMA_ERROR, &host->completed_events) + #define mci_set_card_detect_is_completed(host) \ +@@ -159,8 +143,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + /* Set bit macros for completed events */ + #define mci_set_cmd_complete(host) \ + set_bit(EVENT_CMD_COMPLETE, &host->completed_events) +-#define mci_set_cmd_error_complete(host) \ +- set_bit(EVENT_CMD_ERROR, &host->completed_events) + #define mci_set_data_complete(host) \ + set_bit(EVENT_DATA_COMPLETE, &host->completed_events) + #define mci_set_data_error_complete(host) \ +@@ -169,8 +151,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + set_bit(EVENT_STOP_SENT, &host->completed_events) + #define mci_set_stop_complete(host) \ + set_bit(EVENT_STOP_COMPLETE, &host->completed_events) +-#define mci_set_stop_error_complete(host) \ +- set_bit(EVENT_STOP_ERROR, &host->completed_events) + #define mci_set_dma_error_complete(host) \ + set_bit(EVENT_DMA_ERROR, &host->completed_events) + #define mci_set_card_detect_complete(host) \ +@@ -179,8 +159,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + /* Set bit macros for pending events */ + #define mci_set_cmd_pending(host) \ + set_bit(EVENT_CMD_COMPLETE, &host->pending_events) +-#define mci_set_cmd_error_pending(host) \ +- set_bit(EVENT_CMD_ERROR, &host->pending_events) + #define mci_set_data_pending(host) \ + set_bit(EVENT_DATA_COMPLETE, &host->pending_events) + #define mci_set_data_error_pending(host) \ +@@ -189,8 +167,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + set_bit(EVENT_STOP_SENT, &host->pending_events) + #define mci_set_stop_pending(host) \ + set_bit(EVENT_STOP_COMPLETE, &host->pending_events) +-#define mci_set_stop_error_pending(host) \ +- set_bit(EVENT_STOP_ERROR, &host->pending_events) + #define mci_set_dma_error_pending(host) \ + set_bit(EVENT_DMA_ERROR, &host->pending_events) + #define mci_set_card_detect_pending(host) \ +@@ -199,8 +175,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + /* Clear bit macros for pending events */ + #define mci_clear_cmd_pending(host) \ + clear_bit(EVENT_CMD_COMPLETE, &host->pending_events) +-#define mci_clear_cmd_error_pending(host) \ +- clear_bit(EVENT_CMD_ERROR, &host->pending_events) + #define mci_clear_data_pending(host) \ + clear_bit(EVENT_DATA_COMPLETE, &host->pending_events) + #define mci_clear_data_error_pending(host) \ +@@ -209,8 +183,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + clear_bit(EVENT_STOP_SENT, &host->pending_events) + #define mci_clear_stop_pending(host) \ + clear_bit(EVENT_STOP_COMPLETE, &host->pending_events) +-#define mci_clear_stop_error_pending(host) \ +- clear_bit(EVENT_STOP_ERROR, &host->pending_events) + #define mci_clear_dma_error_pending(host) \ + clear_bit(EVENT_DMA_ERROR, &host->pending_events) + #define mci_clear_card_detect_pending(host) \ +@@ -471,20 +443,16 @@ static void atmci_set_timeout(struct atmel_mci *host, + } + + /* +- * Return mask with interrupt flags to be handled for this command. ++ * Return mask with command flags to be enabled for this command. + */ + static u32 atmci_prepare_command(struct mmc_host *mmc, +- struct mmc_command *cmd, +- u32 *cmd_flags) ++ struct mmc_command *cmd) + { + u32 cmdr; +- u32 iflags; + + cmd->error = MMC_ERR_NONE; + +- cmdr = 0; +- BUG_ON(MCI_BFEXT(CMDNB, cmdr) != 0); +- cmdr = MCI_BFINS(CMDNB, cmd->opcode, cmdr); ++ cmdr = MCI_BF(CMDNB, cmd->opcode); + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) +@@ -503,16 +471,11 @@ static u32 atmci_prepare_command(struct mmc_host *mmc, + if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdr |= MCI_BIT(OPDCMD); + +- iflags = MCI_BIT(CMDRDY) | MCI_CMD_ERROR_FLAGS; +- if (!(cmd->flags & MMC_RSP_CRC)) +- iflags &= ~MCI_BIT(RCRCE); +- + dev_dbg(&mmc->class_dev, + "cmd: op %02x arg %08x flags %08x, cmdflags %08lx\n", + cmd->opcode, cmd->arg, cmd->flags, (unsigned long)cmdr); + +- *cmd_flags = cmdr; +- return iflags; ++ return cmdr; + } + + static void atmci_start_command(struct atmel_mci *host, +@@ -596,13 +559,13 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) + host->pending_events = 0; + host->completed_events = 0; + +- iflags = atmci_prepare_command(mmc, mrq->cmd, &cmdflags); ++ iflags = MCI_BIT(CMDRDY); ++ cmdflags = atmci_prepare_command(mmc, mrq->cmd); + + if (mrq->stop) { +- BUG_ON(!data); ++ WARN_ON(!data); + +- host->stop_iflags = atmci_prepare_command(mmc, mrq->stop, +- &host->stop_cmdr); ++ host->stop_cmdr = atmci_prepare_command(mmc, mrq->stop); + host->stop_cmdr |= MCI_BF(TRCMD, MCI_TRCMD_STOP_TRANS); + if (!(data->flags & MMC_DATA_WRITE)) + host->stop_cmdr |= MCI_BIT(TRDIR); +@@ -716,7 +679,7 @@ static void send_stop_cmd(struct mmc_host *mmc, struct mmc_data *data, + struct atmel_mci *host = mmc_priv(mmc); + + atmci_start_command(host, data->stop, host->stop_cmdr | flags); +- mci_writel(host, IER, host->stop_iflags); ++ mci_writel(host, IER, MCI_BIT(CMDRDY)); + } + + static void atmci_data_complete(struct atmel_mci *host, struct mmc_data *data) +@@ -735,18 +698,30 @@ static void atmci_data_complete(struct atmel_mci *host, struct mmc_data *data) + atmci_request_end(host->mmc, data->mrq); + } + +-static void atmci_command_error(struct mmc_host *mmc, +- struct mmc_command *cmd, +- u32 status) ++static void atmci_command_complete(struct atmel_mci *host, ++ struct mmc_command *cmd, u32 status) + { +- dev_dbg(&mmc->class_dev, "command error: status=0x%08x\n", status); +- + if (status & MCI_BIT(RTOE)) + cmd->error = MMC_ERR_TIMEOUT; +- else if (status & MCI_BIT(RCRCE)) ++ else if ((cmd->flags & MMC_RSP_CRC) ++ && (status & MCI_BIT(RCRCE))) + cmd->error = MMC_ERR_BADCRC; +- else ++ else if (status & (MCI_BIT(RINDE) | MCI_BIT(RDIRE) | MCI_BIT(RENDE))) + cmd->error = MMC_ERR_FAILED; ++ ++ if (cmd->error != MMC_ERR_NONE) { ++ dev_dbg(&host->mmc->class_dev, ++ "command error: op=0x%x status=0x%08x\n", ++ cmd->opcode, status); ++ ++ if (cmd->data) { ++ dma_stop_request(host->dma.req.req.dmac, ++ host->dma.req.req.channel); ++ mci_writel(host, IDR, MCI_BIT(NOTBUSY) ++ | MCI_DATA_ERROR_FLAGS); ++ host->data = NULL; ++ } ++ } + } + + static void atmci_tasklet_func(unsigned long priv) +@@ -761,38 +736,16 @@ static void atmci_tasklet_func(unsigned long priv) + host->pending_events, host->completed_events, + mci_readl(host, IMR)); + +- if (mci_clear_cmd_error_is_pending(host)) { +- struct mmc_command *cmd; +- +- mci_set_cmd_error_complete(host); +- mci_clear_cmd_pending(host); +- cmd = host->mrq->cmd; +- +- if (cmd->data) { +- dma_stop_request(host->dma.req.req.dmac, +- host->dma.req.req.channel); +- host->data = NULL; +- } +- +- atmci_command_error(mmc, cmd, host->error_status); +- atmci_request_end(mmc, cmd->mrq); +- } +- if (mci_clear_stop_error_is_pending(host)) { +- mci_set_stop_error_complete(host); +- mci_clear_stop_pending(host); +- atmci_command_error(mmc, host->mrq->stop, +- host->error_status); +- if (!host->data) +- atmci_request_end(mmc, host->mrq); +- } + if (mci_clear_cmd_is_pending(host)) { + mci_set_cmd_complete(host); +- if (!mrq->data || mci_data_is_complete(host) ++ atmci_command_complete(host, mrq->cmd, host->cmd_status); ++ if (!host->data || mci_data_is_complete(host) + || mci_data_error_is_complete(host)) + atmci_request_end(mmc, mrq); + } + if (mci_clear_stop_is_pending(host)) { + mci_set_stop_complete(host); ++ atmci_command_complete(host, mrq->stop, host->stop_status); + if (mci_data_is_complete(host) + || mci_data_error_is_complete(host)) + atmci_request_end(mmc, mrq); +@@ -815,7 +768,7 @@ static void atmci_tasklet_func(unsigned long priv) + atmci_data_complete(host, data); + } + if (mci_clear_data_error_is_pending(host)) { +- u32 status = host->error_status; ++ u32 status = host->data_status; + + mci_set_data_error_complete(host); + mci_clear_data_pending(host); +@@ -858,10 +811,8 @@ static void atmci_tasklet_func(unsigned long priv) + + /* Clean up queue if present */ + if (mrq) { +- if (!mci_cmd_is_complete(host) +- && !mci_cmd_error_is_complete(host)) { ++ if (!mci_cmd_is_complete(host)) + mrq->cmd->error = MMC_ERR_TIMEOUT; +- } + if (mrq->data && !mci_data_is_complete(host) + && !mci_data_error_is_complete(host)) { + dma_stop_request(host->dma.req.req.dmac, +@@ -869,10 +820,8 @@ static void atmci_tasklet_func(unsigned long priv) + host->data->error = MMC_ERR_TIMEOUT; + atmci_data_complete(host, data); + } +- if (mrq->stop && !mci_stop_is_complete(host) +- && !mci_stop_error_is_complete(host)) { ++ if (mrq->stop && !mci_stop_is_complete(host)) + mrq->stop->error = MMC_ERR_TIMEOUT; +- } + + host->cmd = NULL; + atmci_request_end(mmc, mrq); +@@ -895,13 +844,16 @@ static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status) + cmd->resp[2] = mci_readl(host, RSPR); + cmd->resp[3] = mci_readl(host, RSPR); + +- mci_writel(host, IDR, MCI_BIT(CMDRDY) | MCI_CMD_ERROR_FLAGS); ++ mci_writel(host, IDR, MCI_BIT(CMDRDY)); + host->cmd = NULL; + +- if (mci_stop_sent_is_complete(host)) ++ if (mci_stop_sent_is_complete(host)) { ++ host->stop_status = status; + mci_set_stop_pending(host); +- else ++ } else { ++ host->cmd_status = status; + mci_set_cmd_pending(host); ++ } + + tasklet_schedule(&host->tasklet); + } +@@ -920,18 +872,16 @@ static void atmci_xfer_complete(struct dma_request *_req) + if (data->stop && !mci_set_stop_sent_is_completed(host)) + send_stop_cmd(host->mmc, data, 0); + +- if (data->flags & MMC_DATA_READ) { +- mci_writel(host, IDR, MCI_DATA_ERROR_FLAGS); +- mci_set_data_pending(host); +- tasklet_schedule(&host->tasklet); +- } else { +- /* +- * For the WRITE case, wait for NOTBUSY. This function +- * is called when everything has been written to the +- * controller, not when the card is done programming. +- */ +- mci_writel(host, IER, MCI_BIT(NOTBUSY)); +- } ++ /* ++ * Regardless of what the documentation says, we have to wait ++ * for NOTBUSY even after block read operations. ++ * ++ * When the DMA transfer is complete, the controller may still ++ * be reading the CRC from the card, i.e. the data transfer is ++ * still in progress and we haven't seen all the potential ++ * error bits yet. ++ */ ++ mci_writel(host, IER, MCI_BIT(NOTBUSY)); + } + + static void atmci_dma_error(struct dma_request *_req) +@@ -963,24 +913,10 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) + pending = status & mask; + + do { +- if (pending & MCI_CMD_ERROR_FLAGS) { +- mci_writel(host, IDR, (MCI_BIT(CMDRDY) +- | MCI_BIT(NOTBUSY) +- | MCI_CMD_ERROR_FLAGS +- | MCI_DATA_ERROR_FLAGS)); +- host->error_status = status; +- host->cmd = NULL; +- if (mci_stop_sent_is_complete(host)) +- mci_set_stop_error_pending(host); +- else +- mci_set_cmd_error_pending(host); +- tasklet_schedule(&host->tasklet); +- break; +- } + if (pending & MCI_DATA_ERROR_FLAGS) { + mci_writel(host, IDR, (MCI_BIT(NOTBUSY) + | MCI_DATA_ERROR_FLAGS)); +- host->error_status = status; ++ host->data_status = status; + mci_set_data_error_pending(host); + tasklet_schedule(&host->tasklet); + break; +-- +1.5.3.2 + +_______________________________________________ +Kernel mailing list +Kernel@avr32linux.org +http://duppen.flaskehals.net/cgi-bin/mailman/listinfo/kernel |