diff options
Diffstat (limited to 'drivers/mfd/twl4030-irq.c')
-rw-r--r-- | drivers/mfd/twl4030-irq.c | 342 |
1 files changed, 131 insertions, 211 deletions
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 8a7ee3139b86..f062c8cc6c38 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <linux/init.h> | 30 | #include <linux/init.h> |
31 | #include <linux/interrupt.h> | 31 | #include <linux/interrupt.h> |
32 | #include <linux/irq.h> | 32 | #include <linux/irq.h> |
33 | #include <linux/kthread.h> | ||
34 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
35 | 34 | ||
36 | #include <linux/i2c/twl.h> | 35 | #include <linux/i2c/twl.h> |
@@ -278,59 +277,6 @@ static const struct sih sih_modules_twl5031[8] = { | |||
278 | 277 | ||
279 | static unsigned twl4030_irq_base; | 278 | static unsigned twl4030_irq_base; |
280 | 279 | ||
281 | static struct completion irq_event; | ||
282 | |||
283 | /* | ||
284 | * This thread processes interrupts reported by the Primary Interrupt Handler. | ||
285 | */ | ||
286 | static int twl4030_irq_thread(void *data) | ||
287 | { | ||
288 | long irq = (long)data; | ||
289 | static unsigned i2c_errors; | ||
290 | static const unsigned max_i2c_errors = 100; | ||
291 | |||
292 | |||
293 | current->flags |= PF_NOFREEZE; | ||
294 | |||
295 | while (!kthread_should_stop()) { | ||
296 | int ret; | ||
297 | int module_irq; | ||
298 | u8 pih_isr; | ||
299 | |||
300 | /* Wait for IRQ, then read PIH irq status (also blocking) */ | ||
301 | wait_for_completion_interruptible(&irq_event); | ||
302 | |||
303 | ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr, | ||
304 | REG_PIH_ISR_P1); | ||
305 | if (ret) { | ||
306 | pr_warning("twl4030: I2C error %d reading PIH ISR\n", | ||
307 | ret); | ||
308 | if (++i2c_errors >= max_i2c_errors) { | ||
309 | printk(KERN_ERR "Maximum I2C error count" | ||
310 | " exceeded. Terminating %s.\n", | ||
311 | __func__); | ||
312 | break; | ||
313 | } | ||
314 | complete(&irq_event); | ||
315 | continue; | ||
316 | } | ||
317 | |||
318 | /* these handlers deal with the relevant SIH irq status */ | ||
319 | local_irq_disable(); | ||
320 | for (module_irq = twl4030_irq_base; | ||
321 | pih_isr; | ||
322 | pih_isr >>= 1, module_irq++) { | ||
323 | if (pih_isr & 0x1) | ||
324 | generic_handle_irq(module_irq); | ||
325 | } | ||
326 | local_irq_enable(); | ||
327 | |||
328 | enable_irq(irq); | ||
329 | } | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | /* | 280 | /* |
335 | * handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt. | 281 | * handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt. |
336 | * This is a chained interrupt, so there is no desc->action method for it. | 282 | * This is a chained interrupt, so there is no desc->action method for it. |
@@ -342,9 +288,25 @@ static int twl4030_irq_thread(void *data) | |||
342 | */ | 288 | */ |
343 | static irqreturn_t handle_twl4030_pih(int irq, void *devid) | 289 | static irqreturn_t handle_twl4030_pih(int irq, void *devid) |
344 | { | 290 | { |
345 | /* Acknowledge, clear *AND* mask the interrupt... */ | 291 | int module_irq; |
346 | disable_irq_nosync(irq); | 292 | irqreturn_t ret; |
347 | complete(devid); | 293 | u8 pih_isr; |
294 | |||
295 | ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr, | ||
296 | REG_PIH_ISR_P1); | ||
297 | if (ret) { | ||
298 | pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret); | ||
299 | return IRQ_NONE; | ||
300 | } | ||
301 | |||
302 | /* these handlers deal with the relevant SIH irq status */ | ||
303 | for (module_irq = twl4030_irq_base; | ||
304 | pih_isr; | ||
305 | pih_isr >>= 1, module_irq++) { | ||
306 | if (pih_isr & 0x1) | ||
307 | handle_nested_irq(module_irq); | ||
308 | } | ||
309 | |||
348 | return IRQ_HANDLED; | 310 | return IRQ_HANDLED; |
349 | } | 311 | } |
350 | /*----------------------------------------------------------------------*/ | 312 | /*----------------------------------------------------------------------*/ |
@@ -460,113 +422,17 @@ static inline void activate_irq(int irq) | |||
460 | 422 | ||
461 | /*----------------------------------------------------------------------*/ | 423 | /*----------------------------------------------------------------------*/ |
462 | 424 | ||
463 | static DEFINE_SPINLOCK(sih_agent_lock); | ||
464 | |||
465 | static struct workqueue_struct *wq; | ||
466 | |||
467 | struct sih_agent { | 425 | struct sih_agent { |
468 | int irq_base; | 426 | int irq_base; |
469 | const struct sih *sih; | 427 | const struct sih *sih; |
470 | 428 | ||
471 | u32 imr; | 429 | u32 imr; |
472 | bool imr_change_pending; | 430 | bool imr_change_pending; |
473 | struct work_struct mask_work; | ||
474 | |||
475 | u32 edge_change; | ||
476 | struct work_struct edge_work; | ||
477 | }; | ||
478 | |||
479 | static void twl4030_sih_do_mask(struct work_struct *work) | ||
480 | { | ||
481 | struct sih_agent *agent; | ||
482 | const struct sih *sih; | ||
483 | union { | ||
484 | u8 bytes[4]; | ||
485 | u32 word; | ||
486 | } imr; | ||
487 | int status; | ||
488 | 431 | ||
489 | agent = container_of(work, struct sih_agent, mask_work); | ||
490 | |||
491 | /* see what work we have */ | ||
492 | spin_lock_irq(&sih_agent_lock); | ||
493 | if (agent->imr_change_pending) { | ||
494 | sih = agent->sih; | ||
495 | /* byte[0] gets overwritten as we write ... */ | ||
496 | imr.word = cpu_to_le32(agent->imr << 8); | ||
497 | agent->imr_change_pending = false; | ||
498 | } else | ||
499 | sih = NULL; | ||
500 | spin_unlock_irq(&sih_agent_lock); | ||
501 | if (!sih) | ||
502 | return; | ||
503 | |||
504 | /* write the whole mask ... simpler than subsetting it */ | ||
505 | status = twl_i2c_write(sih->module, imr.bytes, | ||
506 | sih->mask[irq_line].imr_offset, sih->bytes_ixr); | ||
507 | if (status) | ||
508 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
509 | "write", status); | ||
510 | } | ||
511 | |||
512 | static void twl4030_sih_do_edge(struct work_struct *work) | ||
513 | { | ||
514 | struct sih_agent *agent; | ||
515 | const struct sih *sih; | ||
516 | u8 bytes[6]; | ||
517 | u32 edge_change; | 432 | u32 edge_change; |
518 | int status; | ||
519 | |||
520 | agent = container_of(work, struct sih_agent, edge_work); | ||
521 | |||
522 | /* see what work we have */ | ||
523 | spin_lock_irq(&sih_agent_lock); | ||
524 | edge_change = agent->edge_change; | ||
525 | agent->edge_change = 0; | ||
526 | sih = edge_change ? agent->sih : NULL; | ||
527 | spin_unlock_irq(&sih_agent_lock); | ||
528 | if (!sih) | ||
529 | return; | ||
530 | |||
531 | /* Read, reserving first byte for write scratch. Yes, this | ||
532 | * could be cached for some speedup ... but be careful about | ||
533 | * any processor on the other IRQ line, EDR registers are | ||
534 | * shared. | ||
535 | */ | ||
536 | status = twl_i2c_read(sih->module, bytes + 1, | ||
537 | sih->edr_offset, sih->bytes_edr); | ||
538 | if (status) { | ||
539 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
540 | "read", status); | ||
541 | return; | ||
542 | } | ||
543 | |||
544 | /* Modify only the bits we know must change */ | ||
545 | while (edge_change) { | ||
546 | int i = fls(edge_change) - 1; | ||
547 | struct irq_data *idata = irq_get_irq_data(i + agent->irq_base); | ||
548 | int byte = 1 + (i >> 2); | ||
549 | int off = (i & 0x3) * 2; | ||
550 | unsigned int type; | ||
551 | |||
552 | bytes[byte] &= ~(0x03 << off); | ||
553 | 433 | ||
554 | type = irqd_get_trigger_type(idata); | 434 | struct mutex irq_lock; |
555 | if (type & IRQ_TYPE_EDGE_RISING) | 435 | }; |
556 | bytes[byte] |= BIT(off + 1); | ||
557 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
558 | bytes[byte] |= BIT(off + 0); | ||
559 | |||
560 | edge_change &= ~BIT(i); | ||
561 | } | ||
562 | |||
563 | /* Write */ | ||
564 | status = twl_i2c_write(sih->module, bytes, | ||
565 | sih->edr_offset, sih->bytes_edr); | ||
566 | if (status) | ||
567 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
568 | "write", status); | ||
569 | } | ||
570 | 436 | ||
571 | /*----------------------------------------------------------------------*/ | 437 | /*----------------------------------------------------------------------*/ |
572 | 438 | ||
@@ -579,50 +445,125 @@ static void twl4030_sih_do_edge(struct work_struct *work) | |||
579 | 445 | ||
580 | static void twl4030_sih_mask(struct irq_data *data) | 446 | static void twl4030_sih_mask(struct irq_data *data) |
581 | { | 447 | { |
582 | struct sih_agent *sih = irq_data_get_irq_chip_data(data); | 448 | struct sih_agent *agent = irq_data_get_irq_chip_data(data); |
583 | unsigned long flags; | 449 | |
584 | 450 | agent->imr |= BIT(data->irq - agent->irq_base); | |
585 | spin_lock_irqsave(&sih_agent_lock, flags); | 451 | agent->imr_change_pending = true; |
586 | sih->imr |= BIT(data->irq - sih->irq_base); | ||
587 | sih->imr_change_pending = true; | ||
588 | queue_work(wq, &sih->mask_work); | ||
589 | spin_unlock_irqrestore(&sih_agent_lock, flags); | ||
590 | } | 452 | } |
591 | 453 | ||
592 | static void twl4030_sih_unmask(struct irq_data *data) | 454 | static void twl4030_sih_unmask(struct irq_data *data) |
593 | { | 455 | { |
594 | struct sih_agent *sih = irq_data_get_irq_chip_data(data); | 456 | struct sih_agent *agent = irq_data_get_irq_chip_data(data); |
595 | unsigned long flags; | 457 | |
596 | 458 | agent->imr &= ~BIT(data->irq - agent->irq_base); | |
597 | spin_lock_irqsave(&sih_agent_lock, flags); | 459 | agent->imr_change_pending = true; |
598 | sih->imr &= ~BIT(data->irq - sih->irq_base); | ||
599 | sih->imr_change_pending = true; | ||
600 | queue_work(wq, &sih->mask_work); | ||
601 | spin_unlock_irqrestore(&sih_agent_lock, flags); | ||
602 | } | 460 | } |
603 | 461 | ||
604 | static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) | 462 | static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) |
605 | { | 463 | { |
606 | struct sih_agent *sih = irq_data_get_irq_chip_data(data); | 464 | struct sih_agent *agent = irq_data_get_irq_chip_data(data); |
607 | unsigned long flags; | ||
608 | 465 | ||
609 | if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) | 466 | if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) |
610 | return -EINVAL; | 467 | return -EINVAL; |
611 | 468 | ||
612 | spin_lock_irqsave(&sih_agent_lock, flags); | 469 | if (irqd_get_trigger_type(data) != trigger) |
613 | if (irqd_get_trigger_type(data) != trigger) { | 470 | agent->edge_change |= BIT(data->irq - agent->irq_base); |
614 | sih->edge_change |= BIT(data->irq - sih->irq_base); | 471 | |
615 | queue_work(wq, &sih->edge_work); | ||
616 | } | ||
617 | spin_unlock_irqrestore(&sih_agent_lock, flags); | ||
618 | return 0; | 472 | return 0; |
619 | } | 473 | } |
620 | 474 | ||
475 | static void twl4030_sih_bus_lock(struct irq_data *data) | ||
476 | { | ||
477 | struct sih_agent *agent = irq_data_get_irq_chip_data(data); | ||
478 | |||
479 | mutex_lock(&agent->irq_lock); | ||
480 | } | ||
481 | |||
482 | static void twl4030_sih_bus_sync_unlock(struct irq_data *data) | ||
483 | { | ||
484 | struct sih_agent *agent = irq_data_get_irq_chip_data(data); | ||
485 | const struct sih *sih = agent->sih; | ||
486 | int status; | ||
487 | |||
488 | if (agent->imr_change_pending) { | ||
489 | union { | ||
490 | u32 word; | ||
491 | u8 bytes[4]; | ||
492 | } imr; | ||
493 | |||
494 | /* byte[0] gets overwriten as we write ... */ | ||
495 | imr.word = cpu_to_le32(agent->imr << 8); | ||
496 | agent->imr_change_pending = false; | ||
497 | |||
498 | /* write the whole mask ... simpler than subsetting it */ | ||
499 | status = twl_i2c_write(sih->module, imr.bytes, | ||
500 | sih->mask[irq_line].imr_offset, | ||
501 | sih->bytes_ixr); | ||
502 | if (status) | ||
503 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
504 | "write", status); | ||
505 | } | ||
506 | |||
507 | if (agent->edge_change) { | ||
508 | u32 edge_change; | ||
509 | u8 bytes[6]; | ||
510 | |||
511 | edge_change = agent->edge_change; | ||
512 | agent->edge_change = 0; | ||
513 | |||
514 | /* | ||
515 | * Read, reserving first byte for write scratch. Yes, this | ||
516 | * could be cached for some speedup ... but be careful about | ||
517 | * any processor on the other IRQ line, EDR registers are | ||
518 | * shared. | ||
519 | */ | ||
520 | status = twl_i2c_read(sih->module, bytes + 1, | ||
521 | sih->edr_offset, sih->bytes_edr); | ||
522 | if (status) { | ||
523 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
524 | "read", status); | ||
525 | return; | ||
526 | } | ||
527 | |||
528 | /* Modify only the bits we know must change */ | ||
529 | while (edge_change) { | ||
530 | int i = fls(edge_change) - 1; | ||
531 | struct irq_data *idata; | ||
532 | int byte = 1 + (i >> 2); | ||
533 | int off = (i & 0x3) * 2; | ||
534 | unsigned int type; | ||
535 | |||
536 | idata = irq_get_irq_data(i + agent->irq_base); | ||
537 | |||
538 | bytes[byte] &= ~(0x03 << off); | ||
539 | |||
540 | type = irqd_get_trigger_type(idata); | ||
541 | if (type & IRQ_TYPE_EDGE_RISING) | ||
542 | bytes[byte] |= BIT(off + 1); | ||
543 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
544 | bytes[byte] |= BIT(off + 0); | ||
545 | |||
546 | edge_change &= ~BIT(i); | ||
547 | } | ||
548 | |||
549 | /* Write */ | ||
550 | status = twl_i2c_write(sih->module, bytes, | ||
551 | sih->edr_offset, sih->bytes_edr); | ||
552 | if (status) | ||
553 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
554 | "write", status); | ||
555 | } | ||
556 | |||
557 | mutex_unlock(&agent->irq_lock); | ||
558 | } | ||
559 | |||
621 | static struct irq_chip twl4030_sih_irq_chip = { | 560 | static struct irq_chip twl4030_sih_irq_chip = { |
622 | .name = "twl4030", | 561 | .name = "twl4030", |
623 | .irq_mask = twl4030_sih_mask, | 562 | .irq_mask = twl4030_sih_mask, |
624 | .irq_unmask = twl4030_sih_unmask, | 563 | .irq_unmask = twl4030_sih_unmask, |
625 | .irq_set_type = twl4030_sih_set_type, | 564 | .irq_set_type = twl4030_sih_set_type, |
565 | .irq_bus_lock = twl4030_sih_bus_lock, | ||
566 | .irq_bus_sync_unlock = twl4030_sih_bus_sync_unlock, | ||
626 | }; | 567 | }; |
627 | 568 | ||
628 | /*----------------------------------------------------------------------*/ | 569 | /*----------------------------------------------------------------------*/ |
@@ -655,9 +596,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc) | |||
655 | int isr; | 596 | int isr; |
656 | 597 | ||
657 | /* reading ISR acks the IRQs, using clear-on-read mode */ | 598 | /* reading ISR acks the IRQs, using clear-on-read mode */ |
658 | local_irq_enable(); | ||
659 | isr = sih_read_isr(sih); | 599 | isr = sih_read_isr(sih); |
660 | local_irq_disable(); | ||
661 | 600 | ||
662 | if (isr < 0) { | 601 | if (isr < 0) { |
663 | pr_err("twl4030: %s SIH, read ISR error %d\n", | 602 | pr_err("twl4030: %s SIH, read ISR error %d\n", |
@@ -672,7 +611,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc) | |||
672 | isr &= ~BIT(irq); | 611 | isr &= ~BIT(irq); |
673 | 612 | ||
674 | if (irq < sih->bits) | 613 | if (irq < sih->bits) |
675 | generic_handle_irq(agent->irq_base + irq); | 614 | handle_nested_irq(agent->irq_base + irq); |
676 | else | 615 | else |
677 | pr_err("twl4030: %s SIH, invalid ISR bit %d\n", | 616 | pr_err("twl4030: %s SIH, invalid ISR bit %d\n", |
678 | sih->name, irq); | 617 | sih->name, irq); |
@@ -718,15 +657,14 @@ int twl4030_sih_setup(int module) | |||
718 | agent->irq_base = irq_base; | 657 | agent->irq_base = irq_base; |
719 | agent->sih = sih; | 658 | agent->sih = sih; |
720 | agent->imr = ~0; | 659 | agent->imr = ~0; |
721 | INIT_WORK(&agent->mask_work, twl4030_sih_do_mask); | 660 | mutex_init(&agent->irq_lock); |
722 | INIT_WORK(&agent->edge_work, twl4030_sih_do_edge); | ||
723 | 661 | ||
724 | for (i = 0; i < sih->bits; i++) { | 662 | for (i = 0; i < sih->bits; i++) { |
725 | irq = irq_base + i; | 663 | irq = irq_base + i; |
726 | 664 | ||
665 | irq_set_chip_data(irq, agent); | ||
727 | irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip, | 666 | irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip, |
728 | handle_edge_irq); | 667 | handle_edge_irq); |
729 | irq_set_chip_data(irq, agent); | ||
730 | activate_irq(irq); | 668 | activate_irq(irq); |
731 | } | 669 | } |
732 | 670 | ||
@@ -758,7 +696,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
758 | 696 | ||
759 | int status; | 697 | int status; |
760 | int i; | 698 | int i; |
761 | struct task_struct *task; | ||
762 | 699 | ||
763 | /* | 700 | /* |
764 | * Mask and clear all TWL4030 interrupts since initially we do | 701 | * Mask and clear all TWL4030 interrupts since initially we do |
@@ -768,12 +705,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
768 | if (status < 0) | 705 | if (status < 0) |
769 | return status; | 706 | return status; |
770 | 707 | ||
771 | wq = create_singlethread_workqueue("twl4030-irqchip"); | ||
772 | if (!wq) { | ||
773 | pr_err("twl4030: workqueue FAIL\n"); | ||
774 | return -ESRCH; | ||
775 | } | ||
776 | |||
777 | twl4030_irq_base = irq_base; | 708 | twl4030_irq_base = irq_base; |
778 | 709 | ||
779 | /* install an irq handler for each of the SIH modules; | 710 | /* install an irq handler for each of the SIH modules; |
@@ -787,6 +718,7 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
787 | for (i = irq_base; i < irq_end; i++) { | 718 | for (i = irq_base; i < irq_end; i++) { |
788 | irq_set_chip_and_handler(i, &twl4030_irq_chip, | 719 | irq_set_chip_and_handler(i, &twl4030_irq_chip, |
789 | handle_simple_irq); | 720 | handle_simple_irq); |
721 | irq_set_nested_thread(i, 1); | ||
790 | activate_irq(i); | 722 | activate_irq(i); |
791 | } | 723 | } |
792 | twl4030_irq_next = i; | 724 | twl4030_irq_next = i; |
@@ -801,34 +733,22 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
801 | } | 733 | } |
802 | 734 | ||
803 | /* install an irq handler to demultiplex the TWL4030 interrupt */ | 735 | /* install an irq handler to demultiplex the TWL4030 interrupt */ |
804 | 736 | status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih, 0, | |
805 | 737 | "TWL4030-PIH", NULL); | |
806 | init_completion(&irq_event); | ||
807 | |||
808 | status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED, | ||
809 | "TWL4030-PIH", &irq_event); | ||
810 | if (status < 0) { | 738 | if (status < 0) { |
811 | pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); | 739 | pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); |
812 | goto fail_rqirq; | 740 | goto fail_rqirq; |
813 | } | 741 | } |
814 | 742 | ||
815 | task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num, | ||
816 | "twl4030-irq"); | ||
817 | if (IS_ERR(task)) { | ||
818 | pr_err("twl4030: could not create irq %d thread!\n", irq_num); | ||
819 | status = PTR_ERR(task); | ||
820 | goto fail_kthread; | ||
821 | } | ||
822 | return status; | 743 | return status; |
823 | fail_kthread: | ||
824 | free_irq(irq_num, &irq_event); | ||
825 | fail_rqirq: | 744 | fail_rqirq: |
826 | /* clean up twl4030_sih_setup */ | 745 | /* clean up twl4030_sih_setup */ |
827 | fail: | 746 | fail: |
828 | for (i = irq_base; i < irq_end; i++) | 747 | for (i = irq_base; i < irq_end; i++) { |
748 | irq_set_nested_thread(i, 0); | ||
829 | irq_set_chip_and_handler(i, NULL, NULL); | 749 | irq_set_chip_and_handler(i, NULL, NULL); |
830 | destroy_workqueue(wq); | 750 | } |
831 | wq = NULL; | 751 | |
832 | return status; | 752 | return status; |
833 | } | 753 | } |
834 | 754 | ||