Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/mmc/core/sdio_irq.c
15109 views
1
/*
2
* linux/drivers/mmc/core/sdio_irq.c
3
*
4
* Author: Nicolas Pitre
5
* Created: June 18, 2007
6
* Copyright: MontaVista Software Inc.
7
*
8
* Copyright 2008 Pierre Ossman
9
*
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License as published by
12
* the Free Software Foundation; either version 2 of the License, or (at
13
* your option) any later version.
14
*/
15
16
#include <linux/kernel.h>
17
#include <linux/sched.h>
18
#include <linux/kthread.h>
19
#include <linux/wait.h>
20
#include <linux/delay.h>
21
22
#include <linux/mmc/core.h>
23
#include <linux/mmc/host.h>
24
#include <linux/mmc/card.h>
25
#include <linux/mmc/sdio.h>
26
#include <linux/mmc/sdio_func.h>
27
28
#include "sdio_ops.h"
29
30
static int process_sdio_pending_irqs(struct mmc_card *card)
31
{
32
int i, ret, count;
33
unsigned char pending;
34
struct sdio_func *func;
35
36
/*
37
* Optimization, if there is only 1 function interrupt registered
38
* call irq handler directly
39
*/
40
func = card->sdio_single_irq;
41
if (func) {
42
func->irq_handler(func);
43
return 1;
44
}
45
46
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
47
if (ret) {
48
printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
49
mmc_card_id(card), ret);
50
return ret;
51
}
52
53
count = 0;
54
for (i = 1; i <= 7; i++) {
55
if (pending & (1 << i)) {
56
func = card->sdio_func[i - 1];
57
if (!func) {
58
printk(KERN_WARNING "%s: pending IRQ for "
59
"non-existent function\n",
60
mmc_card_id(card));
61
ret = -EINVAL;
62
} else if (func->irq_handler) {
63
func->irq_handler(func);
64
count++;
65
} else {
66
printk(KERN_WARNING "%s: pending IRQ with no handler\n",
67
sdio_func_id(func));
68
ret = -EINVAL;
69
}
70
}
71
}
72
73
if (count)
74
return count;
75
76
return ret;
77
}
78
79
static int sdio_irq_thread(void *_host)
80
{
81
struct mmc_host *host = _host;
82
struct sched_param param = { .sched_priority = 1 };
83
unsigned long period, idle_period;
84
int ret;
85
86
sched_setscheduler(current, SCHED_FIFO, &param);
87
88
/*
89
* We want to allow for SDIO cards to work even on non SDIO
90
* aware hosts. One thing that non SDIO host cannot do is
91
* asynchronous notification of pending SDIO card interrupts
92
* hence we poll for them in that case.
93
*/
94
idle_period = msecs_to_jiffies(10);
95
period = (host->caps & MMC_CAP_SDIO_IRQ) ?
96
MAX_SCHEDULE_TIMEOUT : idle_period;
97
98
pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
99
mmc_hostname(host), period);
100
101
do {
102
/*
103
* We claim the host here on drivers behalf for a couple
104
* reasons:
105
*
106
* 1) it is already needed to retrieve the CCCR_INTx;
107
* 2) we want the driver(s) to clear the IRQ condition ASAP;
108
* 3) we need to control the abort condition locally.
109
*
110
* Just like traditional hard IRQ handlers, we expect SDIO
111
* IRQ handlers to be quick and to the point, so that the
112
* holding of the host lock does not cover too much work
113
* that doesn't require that lock to be held.
114
*/
115
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
116
if (ret)
117
break;
118
ret = process_sdio_pending_irqs(host->card);
119
mmc_release_host(host);
120
121
/*
122
* Give other threads a chance to run in the presence of
123
* errors.
124
*/
125
if (ret < 0) {
126
set_current_state(TASK_INTERRUPTIBLE);
127
if (!kthread_should_stop())
128
schedule_timeout(HZ);
129
set_current_state(TASK_RUNNING);
130
}
131
132
/*
133
* Adaptive polling frequency based on the assumption
134
* that an interrupt will be closely followed by more.
135
* This has a substantial benefit for network devices.
136
*/
137
if (!(host->caps & MMC_CAP_SDIO_IRQ)) {
138
if (ret > 0)
139
period /= 2;
140
else {
141
period++;
142
if (period > idle_period)
143
period = idle_period;
144
}
145
}
146
147
set_current_state(TASK_INTERRUPTIBLE);
148
if (host->caps & MMC_CAP_SDIO_IRQ)
149
host->ops->enable_sdio_irq(host, 1);
150
if (!kthread_should_stop())
151
schedule_timeout(period);
152
set_current_state(TASK_RUNNING);
153
} while (!kthread_should_stop());
154
155
if (host->caps & MMC_CAP_SDIO_IRQ)
156
host->ops->enable_sdio_irq(host, 0);
157
158
pr_debug("%s: IRQ thread exiting with code %d\n",
159
mmc_hostname(host), ret);
160
161
return ret;
162
}
163
164
static int sdio_card_irq_get(struct mmc_card *card)
165
{
166
struct mmc_host *host = card->host;
167
168
WARN_ON(!host->claimed);
169
170
if (!host->sdio_irqs++) {
171
atomic_set(&host->sdio_irq_thread_abort, 0);
172
host->sdio_irq_thread =
173
kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
174
mmc_hostname(host));
175
if (IS_ERR(host->sdio_irq_thread)) {
176
int err = PTR_ERR(host->sdio_irq_thread);
177
host->sdio_irqs--;
178
return err;
179
}
180
}
181
182
return 0;
183
}
184
185
static int sdio_card_irq_put(struct mmc_card *card)
186
{
187
struct mmc_host *host = card->host;
188
189
WARN_ON(!host->claimed);
190
BUG_ON(host->sdio_irqs < 1);
191
192
if (!--host->sdio_irqs) {
193
atomic_set(&host->sdio_irq_thread_abort, 1);
194
kthread_stop(host->sdio_irq_thread);
195
}
196
197
return 0;
198
}
199
200
/* If there is only 1 function registered set sdio_single_irq */
201
static void sdio_single_irq_set(struct mmc_card *card)
202
{
203
struct sdio_func *func;
204
int i;
205
206
card->sdio_single_irq = NULL;
207
if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&
208
card->host->sdio_irqs == 1)
209
for (i = 0; i < card->sdio_funcs; i++) {
210
func = card->sdio_func[i];
211
if (func && func->irq_handler) {
212
card->sdio_single_irq = func;
213
break;
214
}
215
}
216
}
217
218
/**
219
* sdio_claim_irq - claim the IRQ for a SDIO function
220
* @func: SDIO function
221
* @handler: IRQ handler callback
222
*
223
* Claim and activate the IRQ for the given SDIO function. The provided
224
* handler will be called when that IRQ is asserted. The host is always
225
* claimed already when the handler is called so the handler must not
226
* call sdio_claim_host() nor sdio_release_host().
227
*/
228
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
229
{
230
int ret;
231
unsigned char reg;
232
233
BUG_ON(!func);
234
BUG_ON(!func->card);
235
236
pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
237
238
if (func->irq_handler) {
239
pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
240
return -EBUSY;
241
}
242
243
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
244
if (ret)
245
return ret;
246
247
reg |= 1 << func->num;
248
249
reg |= 1; /* Master interrupt enable */
250
251
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
252
if (ret)
253
return ret;
254
255
func->irq_handler = handler;
256
ret = sdio_card_irq_get(func->card);
257
if (ret)
258
func->irq_handler = NULL;
259
sdio_single_irq_set(func->card);
260
261
return ret;
262
}
263
EXPORT_SYMBOL_GPL(sdio_claim_irq);
264
265
/**
266
* sdio_release_irq - release the IRQ for a SDIO function
267
* @func: SDIO function
268
*
269
* Disable and release the IRQ for the given SDIO function.
270
*/
271
int sdio_release_irq(struct sdio_func *func)
272
{
273
int ret;
274
unsigned char reg;
275
276
BUG_ON(!func);
277
BUG_ON(!func->card);
278
279
pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
280
281
if (func->irq_handler) {
282
func->irq_handler = NULL;
283
sdio_card_irq_put(func->card);
284
sdio_single_irq_set(func->card);
285
}
286
287
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
288
if (ret)
289
return ret;
290
291
reg &= ~(1 << func->num);
292
293
/* Disable master interrupt with the last function interrupt */
294
if (!(reg & 0xFE))
295
reg = 0;
296
297
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
298
if (ret)
299
return ret;
300
301
return 0;
302
}
303
EXPORT_SYMBOL_GPL(sdio_release_irq);
304
305
306