diff options
author | Russell King - ARM Linux <linux@arm.linux.org.uk> | 2009-03-26 17:45:05 -0400 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2009-11-09 02:30:09 -0500 |
commit | 097e296d6175881eba7244de7222de61e9569911 (patch) | |
tree | 7202f0cc53dd48fd9e2ec14cd5751311d1f5d2d3 /drivers | |
parent | 0f767de6a26a07f7d58394512b6f6c96322f047f (diff) |
PCMCIA: soc_common: provide single socket add/remove functionality
Factor out the functionality for adding and removing a single
socket, thereby allowing SoCs to individually register each
socket. The advantage of this approach is that SoCs can then
extend soc_pcmcia_socket as they wish.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pcmcia/soc_common.c | 230 | ||||
-rw-r--r-- | drivers/pcmcia/soc_common.h | 3 |
2 files changed, 125 insertions, 108 deletions
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index a2d448f20b0f..9bfa74a6d8ff 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c | |||
@@ -574,7 +574,7 @@ void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, | |||
574 | EXPORT_SYMBOL(soc_pcmcia_enable_irqs); | 574 | EXPORT_SYMBOL(soc_pcmcia_enable_irqs); |
575 | 575 | ||
576 | 576 | ||
577 | LIST_HEAD(soc_pcmcia_sockets); | 577 | static LIST_HEAD(soc_pcmcia_sockets); |
578 | static DEFINE_MUTEX(soc_pcmcia_sockets_lock); | 578 | static DEFINE_MUTEX(soc_pcmcia_sockets_lock); |
579 | 579 | ||
580 | #ifdef CONFIG_CPU_FREQ | 580 | #ifdef CONFIG_CPU_FREQ |
@@ -619,158 +619,174 @@ module_exit(soc_pcmcia_cpufreq_unregister); | |||
619 | 619 | ||
620 | #endif | 620 | #endif |
621 | 621 | ||
622 | int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, | 622 | void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) |
623 | struct skt_dev_info *sinfo) | ||
624 | { | 623 | { |
625 | struct soc_pcmcia_socket *skt; | ||
626 | int ret, i; | ||
627 | |||
628 | mutex_lock(&soc_pcmcia_sockets_lock); | 624 | mutex_lock(&soc_pcmcia_sockets_lock); |
625 | del_timer_sync(&skt->poll_timer); | ||
629 | 626 | ||
630 | /* | 627 | pcmcia_unregister_socket(&skt->socket); |
631 | * Initialise the per-socket structure. | ||
632 | */ | ||
633 | for (i = 0; i < sinfo->nskt; i++) { | ||
634 | skt = &sinfo->skt[i]; | ||
635 | 628 | ||
636 | skt->socket.ops = &soc_common_pcmcia_operations; | 629 | flush_scheduled_work(); |
637 | skt->socket.owner = ops->owner; | ||
638 | skt->socket.dev.parent = dev; | ||
639 | 630 | ||
640 | init_timer(&skt->poll_timer); | 631 | skt->ops->hw_shutdown(skt); |
641 | skt->poll_timer.function = soc_common_pcmcia_poll_event; | ||
642 | skt->poll_timer.data = (unsigned long)skt; | ||
643 | skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD; | ||
644 | 632 | ||
645 | skt->dev = dev; | 633 | soc_common_pcmcia_config_skt(skt, &dead_socket); |
646 | skt->ops = ops; | ||
647 | 634 | ||
648 | ret = request_resource(&iomem_resource, &skt->res_skt); | 635 | list_del(&skt->node); |
649 | if (ret) | 636 | mutex_unlock(&soc_pcmcia_sockets_lock); |
650 | goto out_err_1; | ||
651 | 637 | ||
652 | ret = request_resource(&skt->res_skt, &skt->res_io); | 638 | iounmap(skt->virt_io); |
653 | if (ret) | 639 | skt->virt_io = NULL; |
654 | goto out_err_2; | 640 | release_resource(&skt->res_attr); |
641 | release_resource(&skt->res_mem); | ||
642 | release_resource(&skt->res_io); | ||
643 | release_resource(&skt->res_skt); | ||
644 | } | ||
645 | EXPORT_SYMBOL(soc_pcmcia_remove_one); | ||
655 | 646 | ||
656 | ret = request_resource(&skt->res_skt, &skt->res_mem); | 647 | int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) |
657 | if (ret) | 648 | { |
658 | goto out_err_3; | 649 | int ret; |
659 | 650 | ||
660 | ret = request_resource(&skt->res_skt, &skt->res_attr); | 651 | init_timer(&skt->poll_timer); |
661 | if (ret) | 652 | skt->poll_timer.function = soc_common_pcmcia_poll_event; |
662 | goto out_err_4; | 653 | skt->poll_timer.data = (unsigned long)skt; |
654 | skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD; | ||
663 | 655 | ||
664 | skt->virt_io = ioremap(skt->res_io.start, 0x10000); | 656 | ret = request_resource(&iomem_resource, &skt->res_skt); |
665 | if (skt->virt_io == NULL) { | 657 | if (ret) |
666 | ret = -ENOMEM; | 658 | goto out_err_1; |
667 | goto out_err_5; | ||
668 | } | ||
669 | 659 | ||
670 | list_add(&skt->node, &soc_pcmcia_sockets); | 660 | ret = request_resource(&skt->res_skt, &skt->res_io); |
661 | if (ret) | ||
662 | goto out_err_2; | ||
671 | 663 | ||
672 | /* | 664 | ret = request_resource(&skt->res_skt, &skt->res_mem); |
673 | * We initialize default socket timing here, because | 665 | if (ret) |
674 | * we are not guaranteed to see a SetIOMap operation at | 666 | goto out_err_3; |
675 | * runtime. | ||
676 | */ | ||
677 | ops->set_timing(skt); | ||
678 | 667 | ||
679 | ret = ops->hw_init(skt); | 668 | ret = request_resource(&skt->res_skt, &skt->res_attr); |
680 | if (ret) | 669 | if (ret) |
681 | goto out_err_6; | 670 | goto out_err_4; |
682 | 671 | ||
683 | skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; | 672 | skt->virt_io = ioremap(skt->res_io.start, 0x10000); |
684 | skt->socket.resource_ops = &pccard_static_ops; | 673 | if (skt->virt_io == NULL) { |
685 | skt->socket.irq_mask = 0; | 674 | ret = -ENOMEM; |
686 | skt->socket.map_size = PAGE_SIZE; | 675 | goto out_err_5; |
687 | skt->socket.pci_irq = skt->irq; | 676 | } |
688 | skt->socket.io_offset = (unsigned long)skt->virt_io; | ||
689 | 677 | ||
690 | skt->status = soc_common_pcmcia_skt_state(skt); | 678 | mutex_lock(&soc_pcmcia_sockets_lock); |
691 | 679 | ||
692 | ret = pcmcia_register_socket(&skt->socket); | 680 | list_add(&skt->node, &soc_pcmcia_sockets); |
693 | if (ret) | ||
694 | goto out_err_7; | ||
695 | 681 | ||
696 | WARN_ON(skt->socket.sock != i); | 682 | /* |
683 | * We initialize default socket timing here, because | ||
684 | * we are not guaranteed to see a SetIOMap operation at | ||
685 | * runtime. | ||
686 | */ | ||
687 | skt->ops->set_timing(skt); | ||
697 | 688 | ||
698 | add_timer(&skt->poll_timer); | 689 | ret = skt->ops->hw_init(skt); |
690 | if (ret) | ||
691 | goto out_err_6; | ||
699 | 692 | ||
700 | ret = device_create_file(&skt->socket.dev, &dev_attr_status); | 693 | skt->socket.ops = &soc_common_pcmcia_operations; |
701 | if (ret) | 694 | skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; |
702 | goto out_err_8; | 695 | skt->socket.resource_ops = &pccard_static_ops; |
703 | } | 696 | skt->socket.irq_mask = 0; |
697 | skt->socket.map_size = PAGE_SIZE; | ||
698 | skt->socket.pci_irq = skt->irq; | ||
699 | skt->socket.io_offset = (unsigned long)skt->virt_io; | ||
704 | 700 | ||
705 | dev_set_drvdata(dev, sinfo); | 701 | skt->status = soc_common_pcmcia_skt_state(skt); |
706 | ret = 0; | ||
707 | goto out; | ||
708 | 702 | ||
709 | do { | 703 | ret = pcmcia_register_socket(&skt->socket); |
710 | skt = &sinfo->skt[i]; | 704 | if (ret) |
705 | goto out_err_7; | ||
706 | |||
707 | add_timer(&skt->poll_timer); | ||
708 | |||
709 | mutex_unlock(&soc_pcmcia_sockets_lock); | ||
710 | |||
711 | ret = device_create_file(&skt->socket.dev, &dev_attr_status); | ||
712 | if (ret) | ||
713 | goto out_err_8; | ||
714 | |||
715 | return ret; | ||
711 | 716 | ||
712 | device_remove_file(&skt->socket.dev, &dev_attr_status); | ||
713 | out_err_8: | 717 | out_err_8: |
714 | del_timer_sync(&skt->poll_timer); | 718 | mutex_lock(&soc_pcmcia_sockets_lock); |
715 | pcmcia_unregister_socket(&skt->socket); | 719 | del_timer_sync(&skt->poll_timer); |
720 | pcmcia_unregister_socket(&skt->socket); | ||
716 | 721 | ||
717 | out_err_7: | 722 | out_err_7: |
718 | flush_scheduled_work(); | 723 | flush_scheduled_work(); |
719 | 724 | ||
720 | ops->hw_shutdown(skt); | 725 | skt->ops->hw_shutdown(skt); |
721 | out_err_6: | 726 | out_err_6: |
722 | list_del(&skt->node); | 727 | list_del(&skt->node); |
723 | iounmap(skt->virt_io); | 728 | mutex_unlock(&soc_pcmcia_sockets_lock); |
729 | iounmap(skt->virt_io); | ||
724 | out_err_5: | 730 | out_err_5: |
725 | release_resource(&skt->res_attr); | 731 | release_resource(&skt->res_attr); |
726 | out_err_4: | 732 | out_err_4: |
727 | release_resource(&skt->res_mem); | 733 | release_resource(&skt->res_mem); |
728 | out_err_3: | 734 | out_err_3: |
729 | release_resource(&skt->res_io); | 735 | release_resource(&skt->res_io); |
730 | out_err_2: | 736 | out_err_2: |
731 | release_resource(&skt->res_skt); | 737 | release_resource(&skt->res_skt); |
732 | out_err_1: | 738 | out_err_1: |
733 | i--; | ||
734 | } while (i > 0); | ||
735 | 739 | ||
736 | kfree(sinfo); | ||
737 | |||
738 | out: | ||
739 | mutex_unlock(&soc_pcmcia_sockets_lock); | ||
740 | return ret; | 740 | return ret; |
741 | } | 741 | } |
742 | EXPORT_SYMBOL(soc_common_drv_pcmcia_probe); | 742 | EXPORT_SYMBOL(soc_pcmcia_add_one); |
743 | 743 | ||
744 | int soc_common_drv_pcmcia_remove(struct device *dev) | 744 | int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, |
745 | struct skt_dev_info *sinfo) | ||
745 | { | 746 | { |
746 | struct skt_dev_info *sinfo = dev_get_drvdata(dev); | 747 | struct soc_pcmcia_socket *skt; |
747 | int i; | 748 | int ret, i; |
748 | 749 | ||
749 | dev_set_drvdata(dev, NULL); | 750 | /* |
751 | * Initialise the per-socket structure. | ||
752 | */ | ||
753 | for (i = ret = 0; i < sinfo->nskt; i++) { | ||
754 | skt = &sinfo->skt[i]; | ||
750 | 755 | ||
751 | mutex_lock(&soc_pcmcia_sockets_lock); | 756 | skt->socket.owner = ops->owner; |
752 | for (i = 0; i < sinfo->nskt; i++) { | 757 | skt->socket.dev.parent = dev; |
753 | struct soc_pcmcia_socket *skt = &sinfo->skt[i]; | ||
754 | 758 | ||
755 | del_timer_sync(&skt->poll_timer); | 759 | skt->dev = dev; |
760 | skt->ops = ops; | ||
756 | 761 | ||
757 | pcmcia_unregister_socket(&skt->socket); | 762 | ret = soc_pcmcia_add_one(skt); |
763 | if (ret) | ||
764 | break; | ||
758 | 765 | ||
759 | flush_scheduled_work(); | 766 | WARN_ON(skt->socket.sock != i); |
767 | } | ||
760 | 768 | ||
761 | skt->ops->hw_shutdown(skt); | 769 | if (ret) { |
770 | while (--i >= 0) | ||
771 | soc_pcmcia_remove_one(&sinfo->skt[i]); | ||
772 | kfree(sinfo); | ||
773 | } else { | ||
774 | dev_set_drvdata(dev, sinfo); | ||
775 | } | ||
762 | 776 | ||
763 | soc_common_pcmcia_config_skt(skt, &dead_socket); | 777 | return ret; |
778 | } | ||
779 | EXPORT_SYMBOL(soc_common_drv_pcmcia_probe); | ||
764 | 780 | ||
765 | list_del(&skt->node); | 781 | int soc_common_drv_pcmcia_remove(struct device *dev) |
766 | iounmap(skt->virt_io); | 782 | { |
767 | skt->virt_io = NULL; | 783 | struct skt_dev_info *sinfo = dev_get_drvdata(dev); |
768 | release_resource(&skt->res_attr); | 784 | int i; |
769 | release_resource(&skt->res_mem); | 785 | |
770 | release_resource(&skt->res_io); | 786 | dev_set_drvdata(dev, NULL); |
771 | release_resource(&skt->res_skt); | 787 | |
772 | } | 788 | for (i = 0; i < sinfo->nskt; i++) |
773 | mutex_unlock(&soc_pcmcia_sockets_lock); | 789 | soc_pcmcia_remove_one(&sinfo->skt[i]); |
774 | 790 | ||
775 | kfree(sinfo); | 791 | kfree(sinfo); |
776 | 792 | ||
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index 290e143839ee..51c72bae018e 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h | |||
@@ -135,7 +135,8 @@ extern void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_ | |||
135 | extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *); | 135 | extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *); |
136 | 136 | ||
137 | 137 | ||
138 | extern struct list_head soc_pcmcia_sockets; | 138 | void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt); |
139 | int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt); | ||
139 | 140 | ||
140 | extern int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, struct skt_dev_info *sinfo); | 141 | extern int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, struct skt_dev_info *sinfo); |
141 | extern int soc_common_drv_pcmcia_remove(struct device *dev); | 142 | extern int soc_common_drv_pcmcia_remove(struct device *dev); |