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