Skip to content

Commit f12e39d

Browse files
ahunter6storulf
authored andcommitted
mmc: sdhci: Add CQE support
Add an interrupt hook and helper functions for enabling, disabling and delivering interrupts to a CQE. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Tested-by: Ludovic Desroches <ludovic.desroches@microchip.com>
1 parent f5c1ab8 commit f12e39d

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

drivers/mmc/host/sdhci.c

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ static void sdhci_init(struct sdhci_host *host, int soft)
252252

253253
sdhci_set_default_irqs(host);
254254

255+
host->cqe_on = false;
256+
255257
if (soft) {
256258
/* force clock reconfiguration */
257259
host->clock = 0;
@@ -2672,13 +2674,19 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
26722674
}
26732675

26742676
do {
2677+
DBG("IRQ status 0x%08x\n", intmask);
2678+
2679+
if (host->ops->irq) {
2680+
intmask = host->ops->irq(host, intmask);
2681+
if (!intmask)
2682+
goto cont;
2683+
}
2684+
26752685
/* Clear selected interrupts. */
26762686
mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
26772687
SDHCI_INT_BUS_POWER);
26782688
sdhci_writel(host, mask, SDHCI_INT_STATUS);
26792689

2680-
DBG("IRQ status 0x%08x\n", intmask);
2681-
26822690
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
26832691
u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
26842692
SDHCI_CARD_PRESENT;
@@ -2738,7 +2746,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
27382746
unexpected |= intmask;
27392747
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
27402748
}
2741-
2749+
cont:
27422750
if (result == IRQ_NONE)
27432751
result = IRQ_HANDLED;
27442752

@@ -2965,6 +2973,119 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
29652973

29662974
#endif /* CONFIG_PM */
29672975

2976+
/*****************************************************************************\
2977+
* *
2978+
* Command Queue Engine (CQE) helpers *
2979+
* *
2980+
\*****************************************************************************/
2981+
2982+
void sdhci_cqe_enable(struct mmc_host *mmc)
2983+
{
2984+
struct sdhci_host *host = mmc_priv(mmc);
2985+
unsigned long flags;
2986+
u8 ctrl;
2987+
2988+
spin_lock_irqsave(&host->lock, flags);
2989+
2990+
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
2991+
ctrl &= ~SDHCI_CTRL_DMA_MASK;
2992+
if (host->flags & SDHCI_USE_64_BIT_DMA)
2993+
ctrl |= SDHCI_CTRL_ADMA64;
2994+
else
2995+
ctrl |= SDHCI_CTRL_ADMA32;
2996+
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
2997+
2998+
sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 512),
2999+
SDHCI_BLOCK_SIZE);
3000+
3001+
/* Set maximum timeout */
3002+
sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL);
3003+
3004+
host->ier = host->cqe_ier;
3005+
3006+
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
3007+
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
3008+
3009+
host->cqe_on = true;
3010+
3011+
pr_debug("%s: sdhci: CQE on, IRQ mask %#x, IRQ status %#x\n",
3012+
mmc_hostname(mmc), host->ier,
3013+
sdhci_readl(host, SDHCI_INT_STATUS));
3014+
3015+
mmiowb();
3016+
spin_unlock_irqrestore(&host->lock, flags);
3017+
}
3018+
EXPORT_SYMBOL_GPL(sdhci_cqe_enable);
3019+
3020+
void sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
3021+
{
3022+
struct sdhci_host *host = mmc_priv(mmc);
3023+
unsigned long flags;
3024+
3025+
spin_lock_irqsave(&host->lock, flags);
3026+
3027+
sdhci_set_default_irqs(host);
3028+
3029+
host->cqe_on = false;
3030+
3031+
if (recovery) {
3032+
sdhci_do_reset(host, SDHCI_RESET_CMD);
3033+
sdhci_do_reset(host, SDHCI_RESET_DATA);
3034+
}
3035+
3036+
pr_debug("%s: sdhci: CQE off, IRQ mask %#x, IRQ status %#x\n",
3037+
mmc_hostname(mmc), host->ier,
3038+
sdhci_readl(host, SDHCI_INT_STATUS));
3039+
3040+
mmiowb();
3041+
spin_unlock_irqrestore(&host->lock, flags);
3042+
}
3043+
EXPORT_SYMBOL_GPL(sdhci_cqe_disable);
3044+
3045+
bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
3046+
int *data_error)
3047+
{
3048+
u32 mask;
3049+
3050+
if (!host->cqe_on)
3051+
return false;
3052+
3053+
if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC))
3054+
*cmd_error = -EILSEQ;
3055+
else if (intmask & SDHCI_INT_TIMEOUT)
3056+
*cmd_error = -ETIMEDOUT;
3057+
else
3058+
*cmd_error = 0;
3059+
3060+
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC))
3061+
*data_error = -EILSEQ;
3062+
else if (intmask & SDHCI_INT_DATA_TIMEOUT)
3063+
*data_error = -ETIMEDOUT;
3064+
else if (intmask & SDHCI_INT_ADMA_ERROR)
3065+
*data_error = -EIO;
3066+
else
3067+
*data_error = 0;
3068+
3069+
/* Clear selected interrupts. */
3070+
mask = intmask & host->cqe_ier;
3071+
sdhci_writel(host, mask, SDHCI_INT_STATUS);
3072+
3073+
if (intmask & SDHCI_INT_BUS_POWER)
3074+
pr_err("%s: Card is consuming too much power!\n",
3075+
mmc_hostname(host->mmc));
3076+
3077+
intmask &= ~(host->cqe_ier | SDHCI_INT_ERROR);
3078+
if (intmask) {
3079+
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
3080+
pr_err("%s: CQE: Unexpected interrupt 0x%08x.\n",
3081+
mmc_hostname(host->mmc), intmask);
3082+
sdhci_dumpregs(host);
3083+
}
3084+
3085+
return true;
3086+
}
3087+
EXPORT_SYMBOL_GPL(sdhci_cqe_irq);
3088+
29683089
/*****************************************************************************\
29693090
* *
29703091
* Device allocation/registration *
@@ -2990,6 +3111,9 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
29903111

29913112
host->flags = SDHCI_SIGNALING_330;
29923113

3114+
host->cqe_ier = SDHCI_CQE_INT_MASK;
3115+
host->cqe_err_ier = SDHCI_CQE_INT_ERR_MASK;
3116+
29933117
return host;
29943118
}
29953119

drivers/mmc/host/sdhci.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
#define SDHCI_INT_CARD_REMOVE 0x00000080
135135
#define SDHCI_INT_CARD_INT 0x00000100
136136
#define SDHCI_INT_RETUNE 0x00001000
137+
#define SDHCI_INT_CQE 0x00004000
137138
#define SDHCI_INT_ERROR 0x00008000
138139
#define SDHCI_INT_TIMEOUT 0x00010000
139140
#define SDHCI_INT_CRC 0x00020000
@@ -158,6 +159,13 @@
158159
SDHCI_INT_BLK_GAP)
159160
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
160161

162+
#define SDHCI_CQE_INT_ERR_MASK ( \
163+
SDHCI_INT_ADMA_ERROR | SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | \
164+
SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | \
165+
SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)
166+
167+
#define SDHCI_CQE_INT_MASK (SDHCI_CQE_INT_ERR_MASK | SDHCI_INT_CQE)
168+
161169
#define SDHCI_ACMD12_ERR 0x3C
162170

163171
#define SDHCI_HOST_CONTROL2 0x3E
@@ -518,6 +526,10 @@ struct sdhci_host {
518526
/* cached registers */
519527
u32 ier;
520528

529+
bool cqe_on; /* CQE is operating */
530+
u32 cqe_ier; /* CQE interrupt mask */
531+
u32 cqe_err_ier; /* CQE error interrupt mask */
532+
521533
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
522534
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
523535

@@ -544,6 +556,8 @@ struct sdhci_ops {
544556
void (*set_power)(struct sdhci_host *host, unsigned char mode,
545557
unsigned short vdd);
546558

559+
u32 (*irq)(struct sdhci_host *host, u32 intmask);
560+
547561
int (*enable_dma)(struct sdhci_host *host);
548562
unsigned int (*get_max_clock)(struct sdhci_host *host);
549563
unsigned int (*get_min_clock)(struct sdhci_host *host);
@@ -697,6 +711,11 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host);
697711
int sdhci_runtime_resume_host(struct sdhci_host *host);
698712
#endif
699713

714+
void sdhci_cqe_enable(struct mmc_host *mmc);
715+
void sdhci_cqe_disable(struct mmc_host *mmc, bool recovery);
716+
bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
717+
int *data_error);
718+
700719
void sdhci_dumpregs(struct sdhci_host *host);
701720

702721
#endif /* __SDHCI_HW_H */

0 commit comments

Comments
 (0)