diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-03-15 16:46:34 -0400 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-03-24 06:00:11 -0400 |
commit | d7646f7632549124fe70fec8af834c7c1246f365 (patch) | |
tree | d69cf32f089d84f7a1d7813f7c8dd8b980170b25 /drivers/pcmcia/cs.c | |
parent | e7176a37d436a214f6a7727ea7986c654cbee8f0 (diff) |
pcmcia: use dev_pm_ops for class pcmcia_socket_class
Instead of requiring PCMCIA socket drivers to call various functions
during their (bus) resume and suspend functions, register an own
dev_pm_ops for this class. This fixes several suspend/resume bugs
seen on db1xxx-ss, and probably on some other socket drivers, too.
With regard to the asymmetry with only _noirq suspend, but split up
resume, please see bug 14334 and commit 9905d1b411946fb3 .
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers/pcmcia/cs.c')
-rw-r--r-- | drivers/pcmcia/cs.c | 124 |
1 files changed, 63 insertions, 61 deletions
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index e679e708db63..75ed866e6953 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c | |||
@@ -76,65 +76,6 @@ DECLARE_RWSEM(pcmcia_socket_list_rwsem); | |||
76 | EXPORT_SYMBOL(pcmcia_socket_list_rwsem); | 76 | EXPORT_SYMBOL(pcmcia_socket_list_rwsem); |
77 | 77 | ||
78 | 78 | ||
79 | /* | ||
80 | * Low-level PCMCIA socket drivers need to register with the PCCard | ||
81 | * core using pcmcia_register_socket. | ||
82 | * | ||
83 | * socket drivers are expected to use the following callbacks in their | ||
84 | * .drv struct: | ||
85 | * - pcmcia_socket_dev_suspend | ||
86 | * - pcmcia_socket_dev_resume | ||
87 | * These functions check for the appropriate struct pcmcia_soket arrays, | ||
88 | * and pass them to the low-level functions pcmcia_{suspend,resume}_socket | ||
89 | */ | ||
90 | static int socket_early_resume(struct pcmcia_socket *skt); | ||
91 | static int socket_late_resume(struct pcmcia_socket *skt); | ||
92 | static int socket_resume(struct pcmcia_socket *skt); | ||
93 | static int socket_suspend(struct pcmcia_socket *skt); | ||
94 | |||
95 | static void pcmcia_socket_dev_run(struct device *dev, | ||
96 | int (*cb)(struct pcmcia_socket *)) | ||
97 | { | ||
98 | struct pcmcia_socket *socket; | ||
99 | |||
100 | down_read(&pcmcia_socket_list_rwsem); | ||
101 | list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { | ||
102 | if (socket->dev.parent != dev) | ||
103 | continue; | ||
104 | mutex_lock(&socket->skt_mutex); | ||
105 | cb(socket); | ||
106 | mutex_unlock(&socket->skt_mutex); | ||
107 | } | ||
108 | up_read(&pcmcia_socket_list_rwsem); | ||
109 | } | ||
110 | |||
111 | int pcmcia_socket_dev_suspend(struct device *dev) | ||
112 | { | ||
113 | pcmcia_socket_dev_run(dev, socket_suspend); | ||
114 | return 0; | ||
115 | } | ||
116 | EXPORT_SYMBOL(pcmcia_socket_dev_suspend); | ||
117 | |||
118 | void pcmcia_socket_dev_early_resume(struct device *dev) | ||
119 | { | ||
120 | pcmcia_socket_dev_run(dev, socket_early_resume); | ||
121 | } | ||
122 | EXPORT_SYMBOL(pcmcia_socket_dev_early_resume); | ||
123 | |||
124 | void pcmcia_socket_dev_late_resume(struct device *dev) | ||
125 | { | ||
126 | pcmcia_socket_dev_run(dev, socket_late_resume); | ||
127 | } | ||
128 | EXPORT_SYMBOL(pcmcia_socket_dev_late_resume); | ||
129 | |||
130 | int pcmcia_socket_dev_resume(struct device *dev) | ||
131 | { | ||
132 | pcmcia_socket_dev_run(dev, socket_resume); | ||
133 | return 0; | ||
134 | } | ||
135 | EXPORT_SYMBOL(pcmcia_socket_dev_resume); | ||
136 | |||
137 | |||
138 | struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt) | 79 | struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt) |
139 | { | 80 | { |
140 | struct device *dev = get_device(&skt->dev); | 81 | struct device *dev = get_device(&skt->dev); |
@@ -578,12 +519,18 @@ static int socket_early_resume(struct pcmcia_socket *skt) | |||
578 | 519 | ||
579 | static int socket_late_resume(struct pcmcia_socket *skt) | 520 | static int socket_late_resume(struct pcmcia_socket *skt) |
580 | { | 521 | { |
522 | int ret; | ||
523 | |||
581 | mutex_lock(&skt->ops_mutex); | 524 | mutex_lock(&skt->ops_mutex); |
582 | skt->state &= ~SOCKET_SUSPEND; | 525 | skt->state &= ~SOCKET_SUSPEND; |
583 | mutex_unlock(&skt->ops_mutex); | 526 | mutex_unlock(&skt->ops_mutex); |
584 | 527 | ||
585 | if (!(skt->state & SOCKET_PRESENT)) | 528 | if (!(skt->state & SOCKET_PRESENT)) { |
586 | return socket_insert(skt); | 529 | ret = socket_insert(skt); |
530 | if (ret == -ENODEV) | ||
531 | ret = 0; | ||
532 | return ret; | ||
533 | } | ||
587 | 534 | ||
588 | if (skt->resume_status) { | 535 | if (skt->resume_status) { |
589 | socket_shutdown(skt); | 536 | socket_shutdown(skt); |
@@ -919,11 +866,66 @@ static void pcmcia_release_socket_class(struct class *data) | |||
919 | } | 866 | } |
920 | 867 | ||
921 | 868 | ||
869 | #ifdef CONFIG_PM | ||
870 | |||
871 | static int __pcmcia_pm_op(struct device *dev, | ||
872 | int (*callback) (struct pcmcia_socket *skt)) | ||
873 | { | ||
874 | struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); | ||
875 | int ret; | ||
876 | |||
877 | mutex_lock(&s->skt_mutex); | ||
878 | ret = callback(s); | ||
879 | mutex_unlock(&s->skt_mutex); | ||
880 | |||
881 | return ret; | ||
882 | } | ||
883 | |||
884 | static int pcmcia_socket_dev_suspend_noirq(struct device *dev) | ||
885 | { | ||
886 | return __pcmcia_pm_op(dev, socket_suspend); | ||
887 | } | ||
888 | |||
889 | static int pcmcia_socket_dev_resume_noirq(struct device *dev) | ||
890 | { | ||
891 | return __pcmcia_pm_op(dev, socket_early_resume); | ||
892 | } | ||
893 | |||
894 | static int pcmcia_socket_dev_resume(struct device *dev) | ||
895 | { | ||
896 | return __pcmcia_pm_op(dev, socket_late_resume); | ||
897 | } | ||
898 | |||
899 | static const struct dev_pm_ops pcmcia_socket_pm_ops = { | ||
900 | /* dev_resume may be called with IRQs enabled */ | ||
901 | SET_SYSTEM_SLEEP_PM_OPS(NULL, | ||
902 | pcmcia_socket_dev_resume) | ||
903 | |||
904 | /* late suspend must be called with IRQs disabled */ | ||
905 | .suspend_noirq = pcmcia_socket_dev_suspend_noirq, | ||
906 | .freeze_noirq = pcmcia_socket_dev_suspend_noirq, | ||
907 | .poweroff_noirq = pcmcia_socket_dev_suspend_noirq, | ||
908 | |||
909 | /* early resume must be called with IRQs disabled */ | ||
910 | .resume_noirq = pcmcia_socket_dev_resume_noirq, | ||
911 | .thaw_noirq = pcmcia_socket_dev_resume_noirq, | ||
912 | .restore_noirq = pcmcia_socket_dev_resume_noirq, | ||
913 | }; | ||
914 | |||
915 | #define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops) | ||
916 | |||
917 | #else /* CONFIG_PM */ | ||
918 | |||
919 | #define PCMCIA_SOCKET_CLASS_PM_OPS NULL | ||
920 | |||
921 | #endif /* CONFIG_PM */ | ||
922 | |||
922 | struct class pcmcia_socket_class = { | 923 | struct class pcmcia_socket_class = { |
923 | .name = "pcmcia_socket", | 924 | .name = "pcmcia_socket", |
924 | .dev_uevent = pcmcia_socket_uevent, | 925 | .dev_uevent = pcmcia_socket_uevent, |
925 | .dev_release = pcmcia_release_socket, | 926 | .dev_release = pcmcia_release_socket, |
926 | .class_release = pcmcia_release_socket_class, | 927 | .class_release = pcmcia_release_socket_class, |
928 | .pm = PCMCIA_SOCKET_CLASS_PM_OPS, | ||
927 | }; | 929 | }; |
928 | EXPORT_SYMBOL(pcmcia_socket_class); | 930 | EXPORT_SYMBOL(pcmcia_socket_class); |
929 | 931 | ||