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