Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: mmci: refactor ST Micro busy detection

The ST Micro-specific busy detection was made after the assumption
that only this variant supports busy detection. So when doing busy
detection, the host immediately tries to use some ST-specific
register bits.

Since the qualcomm variant also supports some busy detection
schemes, encapsulate the variant flags better in the variant struct
and prepare to add more variants by just providing some bitmasks
to the logic.

Put the entire busy detection logic within an if()-clause in the
mmci_cmd_irq() function so the code is only executed when busy
detection is enabled, and so that it is kept in (almost) one
place, and add comments describing what is going on so the
code can be understood.

Tested on the Ux500 by introducing some prints in the busy
detection path and noticing how the IRQ is enabled, used and
disabled successfully.

Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Linus Walleij and committed by
Ulf Hansson
49adc0ca 5db3eee7

+85 -30
+84 -29
drivers/mmc/host/mmci.c
··· 71 71 * @f_max: maximum clk frequency supported by the controller. 72 72 * @signal_direction: input/out direction of bus signals can be indicated 73 73 * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock 74 - * @busy_detect: true if busy detection on dat0 is supported 74 + * @busy_detect: true if the variant supports busy detection on DAT0. 75 + * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM 76 + * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register 77 + * indicating that the card is busy 78 + * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for 79 + * getting busy end detection interrupts 75 80 * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply 76 81 * @explicit_mclk_control: enable explicit mclk control in driver. 77 82 * @qcom_fifo: enables qcom specific fifo pio read logic. ··· 103 98 bool signal_direction; 104 99 bool pwrreg_clkgate; 105 100 bool busy_detect; 101 + u32 busy_dpsm_flag; 102 + u32 busy_detect_flag; 103 + u32 busy_detect_mask; 106 104 bool pwrreg_nopower; 107 105 bool explicit_mclk_control; 108 106 bool qcom_fifo; ··· 186 178 .signal_direction = true, 187 179 .pwrreg_clkgate = true, 188 180 .busy_detect = true, 181 + .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE, 182 + .busy_detect_flag = MCI_ST_CARDBUSY, 183 + .busy_detect_mask = MCI_ST_BUSYENDMASK, 189 184 .pwrreg_nopower = true, 190 185 }; 191 186 ··· 210 199 .signal_direction = true, 211 200 .pwrreg_clkgate = true, 212 201 .busy_detect = true, 202 + .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE, 203 + .busy_detect_flag = MCI_ST_CARDBUSY, 204 + .busy_detect_mask = MCI_ST_BUSYENDMASK, 213 205 .pwrreg_nopower = true, 214 206 }; 215 207 ··· 234 220 .qcom_dml = true, 235 221 }; 236 222 223 + /* Busy detection for the ST Micro variant */ 237 224 static int mmci_card_busy(struct mmc_host *mmc) 238 225 { 239 226 struct mmci_host *host = mmc_priv(mmc); ··· 242 227 int busy = 0; 243 228 244 229 spin_lock_irqsave(&host->lock, flags); 245 - if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY) 230 + if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag) 246 231 busy = 1; 247 232 spin_unlock_irqrestore(&host->lock, flags); 248 233 ··· 309 294 */ 310 295 static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl) 311 296 { 312 - /* Keep ST Micro busy mode if enabled */ 313 - datactrl |= host->datactrl_reg & MCI_DPSM_ST_BUSYMODE; 297 + /* Keep busy mode in DPSM if enabled */ 298 + datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag; 314 299 315 300 if (host->datactrl_reg != datactrl) { 316 301 host->datactrl_reg = datactrl; ··· 988 973 unsigned int status) 989 974 { 990 975 void __iomem *base = host->base; 991 - bool sbc, busy_resp; 976 + bool sbc; 992 977 993 978 if (!cmd) 994 979 return; 995 980 996 981 sbc = (cmd == host->mrq->sbc); 997 - busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY); 998 982 999 - if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT| 1000 - MCI_CMDSENT|MCI_CMDRESPEND))) 983 + /* 984 + * We need to be one of these interrupts to be considered worth 985 + * handling. Note that we tag on any latent IRQs postponed 986 + * due to waiting for busy status. 987 + */ 988 + if (!((status|host->busy_status) & 989 + (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND))) 1001 990 return; 1002 991 1003 - /* Check if we need to wait for busy completion. */ 1004 - if (host->busy_status && (status & MCI_ST_CARDBUSY)) 1005 - return; 992 + /* 993 + * ST Micro variant: handle busy detection. 994 + */ 995 + if (host->variant->busy_detect) { 996 + bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY); 1006 997 1007 - /* Enable busy completion if needed and supported. */ 1008 - if (!host->busy_status && busy_resp && 1009 - !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && 1010 - (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) { 1011 - writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND, 1012 - base + MMCIMASK0); 1013 - host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND); 1014 - return; 1015 - } 998 + /* We are busy with a command, return */ 999 + if (host->busy_status && 1000 + (status & host->variant->busy_detect_flag)) 1001 + return; 1016 1002 1017 - /* At busy completion, mask the IRQ and complete the request. */ 1018 - if (host->busy_status) { 1019 - writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND, 1020 - base + MMCIMASK0); 1021 - host->busy_status = 0; 1003 + /* 1004 + * We were not busy, but we now got a busy response on 1005 + * something that was not an error, and we double-check 1006 + * that the special busy status bit is still set before 1007 + * proceeding. 1008 + */ 1009 + if (!host->busy_status && busy_resp && 1010 + !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && 1011 + (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { 1012 + /* Unmask the busy IRQ */ 1013 + writel(readl(base + MMCIMASK0) | 1014 + host->variant->busy_detect_mask, 1015 + base + MMCIMASK0); 1016 + /* 1017 + * Now cache the last response status code (until 1018 + * the busy bit goes low), and return. 1019 + */ 1020 + host->busy_status = 1021 + status & (MCI_CMDSENT|MCI_CMDRESPEND); 1022 + return; 1023 + } 1024 + 1025 + /* 1026 + * At this point we are not busy with a command, we have 1027 + * not received a new busy request, mask the busy IRQ and 1028 + * fall through to process the IRQ. 1029 + */ 1030 + if (host->busy_status) { 1031 + writel(readl(base + MMCIMASK0) & 1032 + ~host->variant->busy_detect_mask, 1033 + base + MMCIMASK0); 1034 + host->busy_status = 0; 1035 + } 1022 1036 } 1023 1037 1024 1038 host->cmd = NULL; ··· 1301 1257 mmci_data_irq(host, host->data, status); 1302 1258 } 1303 1259 1304 - /* Don't poll for busy completion in irq context. */ 1305 - if (host->busy_status) 1306 - status &= ~MCI_ST_CARDBUSY; 1260 + /* 1261 + * Don't poll for busy completion in irq context. 1262 + */ 1263 + if (host->variant->busy_detect && host->busy_status) 1264 + status &= ~host->variant->busy_detect_flag; 1307 1265 1308 1266 ret = 1; 1309 1267 } while (status); ··· 1658 1612 /* We support these capabilities. */ 1659 1613 mmc->caps |= MMC_CAP_CMD23; 1660 1614 1615 + /* 1616 + * Enable busy detection. 1617 + */ 1661 1618 if (variant->busy_detect) { 1662 1619 mmci_ops.card_busy = mmci_card_busy; 1663 - mmci_write_datactrlreg(host, MCI_DPSM_ST_BUSYMODE); 1620 + /* 1621 + * Not all variants have a flag to enable busy detection 1622 + * in the DPSM, but if they do, set it here. 1623 + */ 1624 + if (variant->busy_dpsm_flag) 1625 + mmci_write_datactrlreg(host, 1626 + host->variant->busy_dpsm_flag); 1664 1627 mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; 1665 1628 mmc->max_busy_timeout = 0; 1666 1629 }
+1 -1
drivers/mmc/host/mmci.h
··· 174 174 /* Extended status bits for the ST Micro variants */ 175 175 #define MCI_ST_SDIOITMASK (1 << 22) 176 176 #define MCI_ST_CEATAENDMASK (1 << 23) 177 - #define MCI_ST_BUSYEND (1 << 24) 177 + #define MCI_ST_BUSYENDMASK (1 << 24) 178 178 179 179 #define MMCIMASK1 0x040 180 180 #define MMCIFIFOCNT 0x048