Date: | 2010-07-12 22:49:53 (13 years 8 months ago) |
---|---|
Author: | Lars C. |
Commit: | ef5286907666baca5edc4f8c243db32a6691d9bb |
Message: | MMC: jz4740: Rework state handling Instead of sleeping inside the threaded irq handler there is now a state variable which determines in which action has to be executed next after an irq. |
Files: |
drivers/mmc/host/jz4740_mmc.c (16 diffs) |
Change Details
drivers/mmc/host/jz4740_mmc.c | ||
---|---|---|
24 | 24 | #include <linux/clk.h> |
25 | 25 | #include <linux/mmc/jz4740_mmc.h> |
26 | 26 | |
27 | #include <linux/completion.h> | |
28 | ||
29 | 27 | #include <linux/bitops.h> |
30 | 28 | #include <linux/gpio.h> |
31 | 29 | #include <asm/mach-jz4740/gpio.h> |
... | ... | |
105 | 103 | |
106 | 104 | #define JZ_MMC_CLK_RATE 24000000 |
107 | 105 | |
106 | enum jz4740_mmc_state { | |
107 | JZ4740_MMC_STATE_READ_RESPONSE, | |
108 | JZ4740_MMC_STATE_TRANSFER_DATA, | |
109 | JZ4740_MMC_STATE_SEND_STOP, | |
110 | JZ4740_MMC_STATE_DONE, | |
111 | }; | |
112 | ||
108 | 113 | struct jz4740_mmc_host { |
109 | 114 | struct mmc_host *mmc; |
110 | 115 | struct platform_device *pdev; |
... | ... | |
129 | 134 | spinlock_t lock; |
130 | 135 | |
131 | 136 | struct timer_list timeout_timer; |
132 | struct completion completion; | |
137 | struct sg_mapping_iter miter; | |
138 | enum jz4740_mmc_state state; | |
133 | 139 | }; |
134 | 140 | |
135 | 141 | static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, |
... | ... | |
161 | 167 | static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) |
162 | 168 | { |
163 | 169 | uint32_t status; |
170 | unsigned int timeout = 1000; | |
164 | 171 | |
165 | 172 | writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); |
166 | 173 | do { |
167 | 174 | status = readl(host->base + JZ_REG_MMC_STATUS); |
168 | } while (status & JZ_MMC_STATUS_CLK_EN); | |
175 | } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); | |
169 | 176 | } |
170 | 177 | |
171 | 178 | static void jz4740_mmc_reset(struct jz4740_mmc_host *host) |
172 | 179 | { |
173 | 180 | uint32_t status; |
181 | unsigned int timeout = 1000; | |
174 | 182 | |
175 | 183 | writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); |
176 | 184 | udelay(10); |
177 | 185 | do { |
178 | 186 | status = readl(host->base + JZ_REG_MMC_STATUS); |
179 | } while (status & JZ_MMC_STATUS_IS_RESETTING); | |
187 | } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); | |
180 | 188 | } |
181 | 189 | |
182 | 190 | static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) |
... | ... | |
188 | 196 | |
189 | 197 | mmc_request_done(host->mmc, req); |
190 | 198 | } |
191 | static unsigned int jz4740_mmc_wait_irq(struct jz4740_mmc_host *host, | |
199 | static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, | |
192 | 200 | unsigned int irq) |
193 | 201 | { |
194 | 202 | unsigned int timeout = 1000; |
... | ... | |
199 | 207 | } while (!(status & irq) && --timeout); |
200 | 208 | |
201 | 209 | if (timeout == 0) { |
202 | INIT_COMPLETION(host->completion); | |
210 | set_bit(0, &host->waiting); | |
211 | mod_timer(&host->timeout_timer, jiffies + 5*HZ); | |
203 | 212 | jz4740_mmc_set_irq_enabled(host, irq, true); |
204 | timeout = wait_for_completion_timeout(&host->completion, HZ); | |
213 | return true; | |
205 | 214 | } |
206 | 215 | |
207 | return timeout; | |
216 | return false; | |
208 | 217 | } |
209 | 218 | |
210 | static void jz4740_mmc_write_data(struct jz4740_mmc_host *host, | |
219 | static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, | |
211 | 220 | struct mmc_data *data) |
212 | 221 | { |
213 | struct sg_mapping_iter miter; | |
214 | uint32_t *buf; | |
215 | 222 | int status; |
216 | unsigned int timeout; | |
223 | ||
224 | status = readl(host->base + JZ_REG_MMC_STATUS); | |
225 | if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { | |
226 | if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { | |
227 | host->req->cmd->error = -ETIMEDOUT; | |
228 | data->error = -ETIMEDOUT; | |
229 | } else { | |
230 | host->req->cmd->error = -EIO; | |
231 | data->error = -EIO; | |
232 | } | |
233 | } | |
234 | } | |
235 | ||
236 | static bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, | |
237 | struct mmc_data *data) | |
238 | { | |
239 | struct sg_mapping_iter *miter = &host->miter; | |
240 | uint32_t *buf; | |
241 | bool timeout; | |
217 | 242 | size_t i, j; |
218 | 243 | |
219 | sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_FROM_SG); | |
220 | while (sg_miter_next(&miter)) { | |
221 | buf = miter.addr; | |
222 | i = miter.length / 4; | |
223 | j = i >> 3; | |
244 | while (sg_miter_next(miter)) { | |
245 | buf = miter->addr; | |
246 | i = miter->length / 4; | |
247 | j = i / 8; | |
224 | 248 | i = i & 0x7; |
225 | 249 | while (j) { |
226 | timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); | |
227 | if (unlikely(timeout == 0)) { | |
228 | sg_miter_stop(&miter); | |
229 | goto err_timeout; | |
230 | } | |
250 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); | |
251 | if (unlikely(timeout)) | |
252 | goto poll_timeout; | |
231 | 253 | |
232 | 254 | writel(buf[0], host->base + JZ_REG_MMC_TXFIFO); |
233 | 255 | writel(buf[1], host->base + JZ_REG_MMC_TXFIFO); |
... | ... | |
241 | 263 | --j; |
242 | 264 | } |
243 | 265 | if (unlikely(i)) { |
244 | timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); | |
245 | if (unlikely(timeout == 0)) { | |
246 | sg_miter_stop(&miter); | |
247 | goto err_timeout; | |
248 | } | |
266 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); | |
267 | if (unlikely(timeout)) | |
268 | goto poll_timeout; | |
249 | 269 | |
250 | 270 | while (i) { |
251 | 271 | writel(*buf, host->base + JZ_REG_MMC_TXFIFO); |
... | ... | |
253 | 273 | --i; |
254 | 274 | } |
255 | 275 | } |
256 | data->bytes_xfered += miter.length; | |
276 | data->bytes_xfered += miter->length; | |
257 | 277 | } |
258 | sg_miter_stop(&miter); | |
278 | sg_miter_stop(miter); | |
259 | 279 | |
260 | status = readl(host->base + JZ_REG_MMC_STATUS); | |
261 | if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { | |
262 | if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { | |
263 | host->req->cmd->error = -ETIMEDOUT; | |
264 | data->error = -ETIMEDOUT; | |
265 | } else { | |
266 | host->req->cmd->error = -EIO; | |
267 | data->error = -EIO; | |
268 | } | |
269 | return; | |
270 | } | |
271 | ||
272 | timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); | |
273 | if (unlikely(timeout == 0)) | |
274 | goto err_timeout; | |
280 | return false; | |
275 | 281 | |
276 | writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); | |
282 | poll_timeout: | |
283 | miter->consumed = (void *)buf - miter->addr; | |
284 | data->bytes_xfered += miter->consumed; | |
285 | sg_miter_stop(miter); | |
277 | 286 | |
278 | return; | |
279 | ||
280 | err_timeout: | |
281 | host->req->cmd->error = -ETIMEDOUT; | |
282 | data->error = -ETIMEDOUT; | |
287 | return true; | |
283 | 288 | } |
284 | 289 | |
285 | static void jz4740_mmc_read_data(struct jz4740_mmc_host *host, | |
290 | static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, | |
286 | 291 | struct mmc_data *data) |
287 | 292 | { |
288 | struct sg_mapping_iter miter; | |
293 | struct sg_mapping_iter *miter = &host->miter; | |
289 | 294 | uint32_t *buf; |
290 | 295 | uint32_t d; |
291 | uint16_t status = 0; | |
296 | uint16_t status; | |
292 | 297 | size_t i, j; |
293 | 298 | unsigned int timeout; |
294 | 299 | |
295 | sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_TO_SG); | |
296 | while (sg_miter_next(&miter)) { | |
297 | buf = miter.addr; | |
298 | i = miter.length; | |
299 | j = i >> 5; | |
300 | while (sg_miter_next(miter)) { | |
301 | buf = miter->addr; | |
302 | i = miter->length; | |
303 | j = i / 32; | |
300 | 304 | i = i & 0x1f; |
301 | 305 | while (j) { |
302 | timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); | |
303 | if (unlikely(timeout == 0)) { | |
304 | sg_miter_stop(&miter); | |
305 | goto err_timeout; | |
306 | } | |
306 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); | |
307 | if (unlikely(timeout)) | |
308 | goto poll_timeout; | |
307 | 309 | |
308 | 310 | buf[0] = readl(host->base + JZ_REG_MMC_RXFIFO); |
309 | 311 | buf[1] = readl(host->base + JZ_REG_MMC_RXFIFO); |
... | ... | |
318 | 320 | --j; |
319 | 321 | } |
320 | 322 | |
321 | while (i >= 4) { | |
322 | timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); | |
323 | if (unlikely(timeout == 0)) { | |
324 | sg_miter_stop(&miter); | |
325 | goto err_timeout; | |
326 | } | |
323 | if (unlikely(i)) { | |
324 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); | |
325 | if (unlikely(timeout)) | |
326 | goto poll_timeout; | |
327 | 327 | |
328 | *buf++ = readl(host->base + JZ_REG_MMC_RXFIFO); | |
329 | i -= 4; | |
330 | } | |
331 | if (unlikely(i > 0)) { | |
332 | d = readl(host->base + JZ_REG_MMC_RXFIFO); | |
333 | memcpy(buf, &d, i); | |
328 | while (i >= 4) { | |
329 | *buf++ = readl(host->base + JZ_REG_MMC_RXFIFO); | |
330 | i -= 4; | |
331 | } | |
332 | if (unlikely(i > 0)) { | |
333 | d = readl(host->base + JZ_REG_MMC_RXFIFO); | |
334 | memcpy(buf, &d, i); | |
335 | } | |
334 | 336 | } |
335 | data->bytes_xfered += miter.length; | |
336 | ||
337 | /* This can go away once MIPS implements flush_kernel_dcache_page */ | |
338 | flush_dcache_page(miter.page); | |
339 | } | |
340 | sg_miter_stop(&miter); | |
337 | data->bytes_xfered += miter->length; | |
341 | 338 | |
342 | status = readl(host->base + JZ_REG_MMC_STATUS); | |
343 | if (status & JZ_MMC_STATUS_READ_ERROR_MASK) { | |
344 | if (status & JZ_MMC_STATUS_TIMEOUT_READ) { | |
345 | host->req->cmd->error = -ETIMEDOUT; | |
346 | data->error = -ETIMEDOUT; | |
347 | } else { | |
348 | host->req->cmd->error = -EIO; | |
349 | data->error = -EIO; | |
350 | } | |
351 | return; | |
339 | /* This can go away once MIPS implements | |
340 | * flush_kernel_dcache_page */ | |
341 | flush_dcache_page(miter->page); | |
352 | 342 | } |
343 | sg_miter_stop(miter); | |
353 | 344 | |
354 | 345 | /* For whatever reason there is sometime one word more in the fifo then |
355 | 346 | * requested */ |
356 | while ((status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) == 0 && --timeout) { | |
347 | timeout = 1000; | |
348 | status = readl(host->base + JZ_REG_MMC_STATUS); | |
349 | while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { | |
357 | 350 | d = readl(host->base + JZ_REG_MMC_RXFIFO); |
358 | 351 | status = readl(host->base + JZ_REG_MMC_STATUS); |
359 | 352 | } |
360 | return; | |
361 | 353 | |
362 | err_timeout: | |
363 | host->req->cmd->error = -ETIMEDOUT; | |
364 | data->error = -ETIMEDOUT; | |
354 | return false; | |
355 | ||
356 | poll_timeout: | |
357 | miter->consumed = (void *)buf - miter->addr; | |
358 | data->bytes_xfered += miter->consumed; | |
359 | sg_miter_stop(miter); | |
360 | ||
361 | return true; | |
365 | 362 | } |
366 | 363 | |
367 | 364 | static void jz4740_mmc_timeout(unsigned long data) |
... | ... | |
444 | 441 | jz4740_mmc_clock_enable(host, 1); |
445 | 442 | } |
446 | 443 | |
444 | static void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) | |
445 | { | |
446 | struct mmc_command *cmd = host->req->cmd; | |
447 | struct mmc_data *data = cmd->data; | |
448 | int direction; | |
449 | ||
450 | if (cmd->data->flags & MMC_DATA_READ) | |
451 | direction = SG_MITER_TO_SG; | |
452 | else | |
453 | direction = SG_MITER_FROM_SG; | |
454 | ||
455 | sg_miter_start(&host->miter, data->sg, data->sg_len, direction); | |
456 | } | |
457 | ||
447 | 458 | |
448 | 459 | static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) |
449 | 460 | { |
450 | 461 | struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; |
451 | 462 | struct mmc_command *cmd = host->req->cmd; |
452 | 463 | struct mmc_request *req = host->req; |
453 | unsigned int timeout; | |
464 | bool timedout = false; | |
454 | 465 | |
455 | 466 | if (cmd->error) |
456 | goto done; | |
467 | host->state = JZ4740_MMC_STATE_DONE; | |
457 | 468 | |
458 | if (cmd->flags & MMC_RSP_PRESENT) | |
459 | jz4740_mmc_read_response(host, cmd); | |
469 | switch (host->state) { | |
470 | case JZ4740_MMC_STATE_READ_RESPONSE: | |
471 | if (cmd->flags & MMC_RSP_PRESENT) | |
472 | jz4740_mmc_read_response(host, cmd); | |
460 | 473 | |
461 | if (cmd->data) { | |
474 | if (!cmd->data) | |
475 | break; | |
476 | ||
477 | jz_mmc_prepare_data_transfer(host); | |
478 | ||
479 | case JZ4740_MMC_STATE_TRANSFER_DATA: | |
462 | 480 | if (cmd->data->flags & MMC_DATA_READ) |
463 | jz4740_mmc_read_data(host, cmd->data); | |
481 | timedout = jz4740_mmc_read_data(host, cmd->data); | |
464 | 482 | else |
465 | jz4740_mmc_write_data(host, cmd->data); | |
466 | } | |
483 | timedout = jz4740_mmc_write_data(host, cmd->data); | |
467 | 484 | |
468 | if (req->stop) { | |
469 | jz4740_mmc_send_command(host, req->stop); | |
485 | if (unlikely(timedout)) { | |
486 | host->state = JZ4740_MMC_STATE_TRANSFER_DATA; | |
487 | break; | |
488 | } | |
470 | 489 | |
471 | timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_PRG_DONE); | |
472 | writew(JZ_MMC_IRQ_PRG_DONE, host->base + JZ_REG_MMC_IREG); | |
490 | jz4740_mmc_transfer_check_state(host, cmd->data); | |
473 | 491 | |
474 | if (unlikely(timeout == 0)) | |
475 | req->stop->error = -ETIMEDOUT; | |
476 | } | |
492 | timedout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); | |
493 | if (unlikely(timedout)) { | |
494 | host->state = JZ4740_MMC_STATE_SEND_STOP; | |
495 | break; | |
496 | } | |
497 | writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); | |
477 | 498 | |
499 | case JZ4740_MMC_STATE_SEND_STOP: | |
500 | if (!req->stop) | |
501 | break; | |
478 | 502 | |
479 | done: | |
480 | jz4740_mmc_request_done(host); | |
503 | jz4740_mmc_send_command(host, req->stop); | |
504 | ||
505 | timedout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_PRG_DONE); | |
506 | if (timedout) { | |
507 | host->state = JZ4740_MMC_STATE_DONE; | |
508 | break; | |
509 | } | |
510 | case JZ4740_MMC_STATE_DONE: | |
511 | break; | |
512 | } | |
513 | ||
514 | if (!timedout) | |
515 | jz4740_mmc_request_done(host); | |
481 | 516 | |
482 | 517 | return IRQ_HANDLED; |
483 | 518 | } |
... | ... | |
486 | 521 | { |
487 | 522 | struct jz4740_mmc_host *host = devid; |
488 | 523 | uint16_t irq_reg, status, tmp; |
489 | irqreturn_t ret = IRQ_HANDLED; | |
490 | 524 | |
491 | 525 | irq_reg = readw(host->base + JZ_REG_MMC_IREG); |
492 | 526 | |
... | ... | |
502 | 536 | if (irq_reg & JZ_MMC_IRQ_SDIO) { |
503 | 537 | writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG); |
504 | 538 | mmc_signal_sdio_irq(host->mmc); |
539 | irq_reg &= ~JZ_MMC_IRQ_SDIO; | |
505 | 540 | } |
506 | 541 | |
507 | if (!host->req || !host->cmd) | |
508 | goto handled; | |
509 | ||
510 | if (irq_reg & JZ_MMC_IRQ_END_CMD_RES) { | |
511 | ||
542 | if (host->req && host->cmd && irq_reg) { | |
512 | 543 | if (test_and_clear_bit(0, &host->waiting)) { |
513 | 544 | del_timer(&host->timeout_timer); |
514 | 545 | |
... | ... | |
526 | 557 | host->cmd->data->error = -EIO; |
527 | 558 | } |
528 | 559 | |
529 | ret = IRQ_WAKE_THREAD; | |
530 | } | |
560 | jz4740_mmc_set_irq_enabled(host, irq_reg, false); | |
561 | writew(irq_reg, host->base + JZ_REG_MMC_IREG); | |
531 | 562 | |
532 | jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); | |
533 | writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); | |
534 | } else { | |
535 | complete(&host->completion); | |
536 | jz4740_mmc_set_irq_enabled(host, irq_reg, false); | |
537 | writew(irq_reg, host->base + JZ_REG_MMC_IREG); | |
563 | return IRQ_WAKE_THREAD; | |
564 | } | |
538 | 565 | } |
539 | 566 | |
540 | handled: | |
541 | return ret; | |
567 | return IRQ_HANDLED; | |
542 | 568 | } |
543 | 569 | |
544 | 570 | static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) |
... | ... | |
571 | 597 | writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); |
572 | 598 | jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); |
573 | 599 | |
600 | host->state = JZ4740_MMC_STATE_READ_RESPONSE; | |
574 | 601 | set_bit(0, &host->waiting); |
575 | 602 | mod_timer(&host->timeout_timer, jiffies + 5*HZ); |
576 | 603 | jz4740_mmc_send_command(host, req->cmd); |
... | ... | |
770 | 797 | |
771 | 798 | host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
772 | 799 | if (!host->mem) { |
773 | ret = -ENXIO; | |
800 | ret = -ENOENT; | |
774 | 801 | dev_err(&pdev->dev, "Failed to get base platform memory\n"); |
775 | 802 | goto err_clk_put; |
776 | 803 | } |
... | ... | |
848 | 875 | jz4740_mmc_clock_disable(host); |
849 | 876 | setup_timer(&host->timeout_timer, jz4740_mmc_timeout, |
850 | 877 | (unsigned long)host); |
851 | init_completion(&host->completion); | |
878 | /* It is not that important when it times out, it just needs to timeout. */ | |
879 | set_timer_slack(&host->timeout_timer, HZ); | |
852 | 880 | |
853 | 881 | platform_set_drvdata(pdev, host); |
854 | 882 | ret = mmc_add_host(mmc); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9