diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-05 00:44:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-05 00:44:34 -0400 |
commit | 62ea6d80211ecc88ef516927ecebf64cb505be3f (patch) | |
tree | 1920de8cd3671aedcc912afb8e5ddb2a7c674b05 /drivers/misc | |
parent | fa24aa561a3cf91cf25b5d4066470b08a2d24206 (diff) | |
parent | d3af5abe9a809becbe4b413144b607844560d445 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (46 commits)
mmc-omap: Clean up omap set_ios and make MMC_POWER_ON work
mmc-omap: Fix omap to use MMC_POWER_ON
mmc-omap: add missing '\n'
mmc: make tifm_sd_set_dma_data() static
mmc: remove old card states
mmc: support unsafe resume of cards
mmc: separate out reading EXT_CSD
mmc: break apart switch function
MMC: Fix handling of low-voltage cards
MMC: Consolidate voltage definitions
mmc: add bus handler
wbsd: check for data opcode earlier
mmc: Separate out protocol ops
mmc: Move core functions to subdir
mmc: deprecate mmc bus topology
mmc: remove card upon suspend
mmc: allow suspended block driver to be removed
mmc: Flush pending detects on host removal
mmc: Move host and card drivers to subdirs
mmc: Move queue functions to mmc_block
...
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/tifm_7xx1.c | 332 | ||||
-rw-r--r-- | drivers/misc/tifm_core.c | 305 |
2 files changed, 323 insertions, 314 deletions
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index bc60e2fc3c2c..1ba6c085419a 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c | |||
@@ -11,10 +11,20 @@ | |||
11 | 11 | ||
12 | #include <linux/tifm.h> | 12 | #include <linux/tifm.h> |
13 | #include <linux/dma-mapping.h> | 13 | #include <linux/dma-mapping.h> |
14 | #include <linux/freezer.h> | ||
15 | 14 | ||
16 | #define DRIVER_NAME "tifm_7xx1" | 15 | #define DRIVER_NAME "tifm_7xx1" |
17 | #define DRIVER_VERSION "0.7" | 16 | #define DRIVER_VERSION "0.8" |
17 | |||
18 | #define TIFM_IRQ_ENABLE 0x80000000 | ||
19 | #define TIFM_IRQ_SOCKMASK(x) (x) | ||
20 | #define TIFM_IRQ_CARDMASK(x) ((x) << 8) | ||
21 | #define TIFM_IRQ_FIFOMASK(x) ((x) << 16) | ||
22 | #define TIFM_IRQ_SETALL 0xffffffff | ||
23 | |||
24 | static void tifm_7xx1_dummy_eject(struct tifm_adapter *fm, | ||
25 | struct tifm_dev *sock) | ||
26 | { | ||
27 | } | ||
18 | 28 | ||
19 | static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) | 29 | static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) |
20 | { | 30 | { |
@@ -22,7 +32,7 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) | |||
22 | 32 | ||
23 | spin_lock_irqsave(&fm->lock, flags); | 33 | spin_lock_irqsave(&fm->lock, flags); |
24 | fm->socket_change_set |= 1 << sock->socket_id; | 34 | fm->socket_change_set |= 1 << sock->socket_id; |
25 | wake_up_all(&fm->change_set_notify); | 35 | tifm_queue_work(&fm->media_switcher); |
26 | spin_unlock_irqrestore(&fm->lock, flags); | 36 | spin_unlock_irqrestore(&fm->lock, flags); |
27 | } | 37 | } |
28 | 38 | ||
@@ -30,8 +40,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) | |||
30 | { | 40 | { |
31 | struct tifm_adapter *fm = dev_id; | 41 | struct tifm_adapter *fm = dev_id; |
32 | struct tifm_dev *sock; | 42 | struct tifm_dev *sock; |
33 | unsigned int irq_status; | 43 | unsigned int irq_status, cnt; |
34 | unsigned int sock_irq_status, cnt; | ||
35 | 44 | ||
36 | spin_lock(&fm->lock); | 45 | spin_lock(&fm->lock); |
37 | irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); | 46 | irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); |
@@ -45,12 +54,12 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) | |||
45 | 54 | ||
46 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { | 55 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { |
47 | sock = fm->sockets[cnt]; | 56 | sock = fm->sockets[cnt]; |
48 | sock_irq_status = (irq_status >> cnt) | 57 | if (sock) { |
49 | & (TIFM_IRQ_FIFOMASK(1) | 58 | if ((irq_status >> cnt) & TIFM_IRQ_FIFOMASK(1)) |
50 | | TIFM_IRQ_CARDMASK(1)); | 59 | sock->data_event(sock); |
51 | 60 | if ((irq_status >> cnt) & TIFM_IRQ_CARDMASK(1)) | |
52 | if (sock && sock_irq_status) | 61 | sock->card_event(sock); |
53 | sock->signal_irq(sock, sock_irq_status); | 62 | } |
54 | } | 63 | } |
55 | 64 | ||
56 | fm->socket_change_set |= irq_status | 65 | fm->socket_change_set |= irq_status |
@@ -58,57 +67,57 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) | |||
58 | } | 67 | } |
59 | writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); | 68 | writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); |
60 | 69 | ||
61 | if (!fm->socket_change_set) | 70 | if (fm->finish_me) |
71 | complete_all(fm->finish_me); | ||
72 | else if (!fm->socket_change_set) | ||
62 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); | 73 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); |
63 | else | 74 | else |
64 | wake_up_all(&fm->change_set_notify); | 75 | tifm_queue_work(&fm->media_switcher); |
65 | 76 | ||
66 | spin_unlock(&fm->lock); | 77 | spin_unlock(&fm->lock); |
67 | return IRQ_HANDLED; | 78 | return IRQ_HANDLED; |
68 | } | 79 | } |
69 | 80 | ||
70 | static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, | 81 | static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr) |
71 | int is_x2) | ||
72 | { | 82 | { |
73 | unsigned int s_state; | 83 | unsigned int s_state; |
74 | int cnt; | 84 | int cnt; |
75 | 85 | ||
76 | writel(0x0e00, sock_addr + SOCK_CONTROL); | 86 | writel(0x0e00, sock_addr + SOCK_CONTROL); |
77 | 87 | ||
78 | for (cnt = 0; cnt < 100; cnt++) { | 88 | for (cnt = 16; cnt <= 256; cnt <<= 1) { |
79 | if (!(TIFM_SOCK_STATE_POWERED | 89 | if (!(TIFM_SOCK_STATE_POWERED |
80 | & readl(sock_addr + SOCK_PRESENT_STATE))) | 90 | & readl(sock_addr + SOCK_PRESENT_STATE))) |
81 | break; | 91 | break; |
82 | msleep(10); | 92 | |
93 | msleep(cnt); | ||
83 | } | 94 | } |
84 | 95 | ||
85 | s_state = readl(sock_addr + SOCK_PRESENT_STATE); | 96 | s_state = readl(sock_addr + SOCK_PRESENT_STATE); |
86 | if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) | 97 | if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) |
87 | return FM_NULL; | 98 | return 0; |
88 | |||
89 | if (is_x2) { | ||
90 | writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); | ||
91 | } else { | ||
92 | // SmartMedia cards need extra 40 msec | ||
93 | if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1) | ||
94 | msleep(40); | ||
95 | writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, | ||
96 | sock_addr + SOCK_CONTROL); | ||
97 | msleep(10); | ||
98 | writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED, | ||
99 | sock_addr + SOCK_CONTROL); | ||
100 | } | ||
101 | 99 | ||
102 | for (cnt = 0; cnt < 100; cnt++) { | 100 | writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, |
101 | sock_addr + SOCK_CONTROL); | ||
102 | |||
103 | /* xd needs some extra time before power on */ | ||
104 | if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) | ||
105 | == TIFM_TYPE_XD) | ||
106 | msleep(40); | ||
107 | |||
108 | writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); | ||
109 | /* wait for power to stabilize */ | ||
110 | msleep(20); | ||
111 | for (cnt = 16; cnt <= 256; cnt <<= 1) { | ||
103 | if ((TIFM_SOCK_STATE_POWERED | 112 | if ((TIFM_SOCK_STATE_POWERED |
104 | & readl(sock_addr + SOCK_PRESENT_STATE))) | 113 | & readl(sock_addr + SOCK_PRESENT_STATE))) |
105 | break; | 114 | break; |
106 | msleep(10); | 115 | |
116 | msleep(cnt); | ||
107 | } | 117 | } |
108 | 118 | ||
109 | if (!is_x2) | 119 | writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), |
110 | writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), | 120 | sock_addr + SOCK_CONTROL); |
111 | sock_addr + SOCK_CONTROL); | ||
112 | 121 | ||
113 | return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7; | 122 | return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7; |
114 | } | 123 | } |
@@ -119,127 +128,77 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num) | |||
119 | return base_addr + ((sock_num + 1) << 10); | 128 | return base_addr + ((sock_num + 1) << 10); |
120 | } | 129 | } |
121 | 130 | ||
122 | static int tifm_7xx1_switch_media(void *data) | 131 | static void tifm_7xx1_switch_media(struct work_struct *work) |
123 | { | 132 | { |
124 | struct tifm_adapter *fm = data; | 133 | struct tifm_adapter *fm = container_of(work, struct tifm_adapter, |
125 | unsigned long flags; | 134 | media_switcher); |
126 | tifm_media_id media_id; | ||
127 | char *card_name = "xx"; | ||
128 | int cnt, rc; | ||
129 | struct tifm_dev *sock; | 135 | struct tifm_dev *sock; |
130 | unsigned int socket_change_set; | 136 | unsigned long flags; |
131 | 137 | unsigned char media_id; | |
132 | while (1) { | 138 | unsigned int socket_change_set, cnt; |
133 | rc = wait_event_interruptible(fm->change_set_notify, | ||
134 | fm->socket_change_set); | ||
135 | if (rc == -ERESTARTSYS) | ||
136 | try_to_freeze(); | ||
137 | 139 | ||
138 | spin_lock_irqsave(&fm->lock, flags); | 140 | spin_lock_irqsave(&fm->lock, flags); |
139 | socket_change_set = fm->socket_change_set; | 141 | socket_change_set = fm->socket_change_set; |
140 | fm->socket_change_set = 0; | 142 | fm->socket_change_set = 0; |
141 | 143 | ||
142 | dev_dbg(fm->dev, "checking media set %x\n", | 144 | dev_dbg(fm->cdev.dev, "checking media set %x\n", |
143 | socket_change_set); | 145 | socket_change_set); |
144 | 146 | ||
145 | if (kthread_should_stop()) | 147 | if (!socket_change_set) { |
146 | socket_change_set = (1 << fm->num_sockets) - 1; | ||
147 | spin_unlock_irqrestore(&fm->lock, flags); | 148 | spin_unlock_irqrestore(&fm->lock, flags); |
149 | return; | ||
150 | } | ||
148 | 151 | ||
149 | if (!socket_change_set) | 152 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { |
153 | if (!(socket_change_set & (1 << cnt))) | ||
150 | continue; | 154 | continue; |
151 | 155 | sock = fm->sockets[cnt]; | |
152 | spin_lock_irqsave(&fm->lock, flags); | 156 | if (sock) { |
153 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { | 157 | printk(KERN_INFO |
154 | if (!(socket_change_set & (1 << cnt))) | 158 | "%s : demand removing card from socket %u:%u\n", |
155 | continue; | 159 | fm->cdev.class_id, fm->id, cnt); |
156 | sock = fm->sockets[cnt]; | 160 | fm->sockets[cnt] = NULL; |
157 | if (sock) { | ||
158 | printk(KERN_INFO DRIVER_NAME | ||
159 | ": demand removing card from socket %d\n", | ||
160 | cnt); | ||
161 | fm->sockets[cnt] = NULL; | ||
162 | spin_unlock_irqrestore(&fm->lock, flags); | ||
163 | device_unregister(&sock->dev); | ||
164 | spin_lock_irqsave(&fm->lock, flags); | ||
165 | writel(0x0e00, | ||
166 | tifm_7xx1_sock_addr(fm->addr, cnt) | ||
167 | + SOCK_CONTROL); | ||
168 | } | ||
169 | if (kthread_should_stop()) | ||
170 | continue; | ||
171 | |||
172 | spin_unlock_irqrestore(&fm->lock, flags); | 161 | spin_unlock_irqrestore(&fm->lock, flags); |
173 | media_id = tifm_7xx1_toggle_sock_power( | 162 | device_unregister(&sock->dev); |
174 | tifm_7xx1_sock_addr(fm->addr, cnt), | 163 | spin_lock_irqsave(&fm->lock, flags); |
175 | fm->num_sockets == 2); | 164 | writel(0x0e00, tifm_7xx1_sock_addr(fm->addr, cnt) |
176 | if (media_id) { | 165 | + SOCK_CONTROL); |
177 | sock = tifm_alloc_device(fm); | ||
178 | if (sock) { | ||
179 | sock->addr = tifm_7xx1_sock_addr(fm->addr, | ||
180 | cnt); | ||
181 | sock->media_id = media_id; | ||
182 | sock->socket_id = cnt; | ||
183 | switch (media_id) { | ||
184 | case 1: | ||
185 | card_name = "xd"; | ||
186 | break; | ||
187 | case 2: | ||
188 | card_name = "ms"; | ||
189 | break; | ||
190 | case 3: | ||
191 | card_name = "sd"; | ||
192 | break; | ||
193 | default: | ||
194 | tifm_free_device(&sock->dev); | ||
195 | spin_lock_irqsave(&fm->lock, flags); | ||
196 | continue; | ||
197 | } | ||
198 | snprintf(sock->dev.bus_id, BUS_ID_SIZE, | ||
199 | "tifm_%s%u:%u", card_name, | ||
200 | fm->id, cnt); | ||
201 | printk(KERN_INFO DRIVER_NAME | ||
202 | ": %s card detected in socket %d\n", | ||
203 | card_name, cnt); | ||
204 | if (!device_register(&sock->dev)) { | ||
205 | spin_lock_irqsave(&fm->lock, flags); | ||
206 | if (!fm->sockets[cnt]) { | ||
207 | fm->sockets[cnt] = sock; | ||
208 | sock = NULL; | ||
209 | } | ||
210 | spin_unlock_irqrestore(&fm->lock, flags); | ||
211 | } | ||
212 | if (sock) | ||
213 | tifm_free_device(&sock->dev); | ||
214 | } | ||
215 | spin_lock_irqsave(&fm->lock, flags); | ||
216 | } | ||
217 | } | 166 | } |
218 | 167 | ||
219 | if (!kthread_should_stop()) { | 168 | spin_unlock_irqrestore(&fm->lock, flags); |
220 | writel(TIFM_IRQ_FIFOMASK(socket_change_set) | 169 | |
221 | | TIFM_IRQ_CARDMASK(socket_change_set), | 170 | media_id = tifm_7xx1_toggle_sock_power( |
222 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | 171 | tifm_7xx1_sock_addr(fm->addr, cnt)); |
223 | writel(TIFM_IRQ_FIFOMASK(socket_change_set) | 172 | |
224 | | TIFM_IRQ_CARDMASK(socket_change_set), | 173 | // tifm_alloc_device will check if media_id is valid |
225 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 174 | sock = tifm_alloc_device(fm, cnt, media_id); |
226 | writel(TIFM_IRQ_ENABLE, | 175 | if (sock) { |
227 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 176 | sock->addr = tifm_7xx1_sock_addr(fm->addr, cnt); |
228 | spin_unlock_irqrestore(&fm->lock, flags); | 177 | |
229 | } else { | 178 | if (!device_register(&sock->dev)) { |
230 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { | 179 | spin_lock_irqsave(&fm->lock, flags); |
231 | if (fm->sockets[cnt]) | 180 | if (!fm->sockets[cnt]) { |
232 | fm->socket_change_set |= 1 << cnt; | 181 | fm->sockets[cnt] = sock; |
233 | } | 182 | sock = NULL; |
234 | if (!fm->socket_change_set) { | 183 | } |
235 | spin_unlock_irqrestore(&fm->lock, flags); | ||
236 | return 0; | ||
237 | } else { | ||
238 | spin_unlock_irqrestore(&fm->lock, flags); | 184 | spin_unlock_irqrestore(&fm->lock, flags); |
239 | } | 185 | } |
186 | if (sock) | ||
187 | tifm_free_device(&sock->dev); | ||
240 | } | 188 | } |
189 | spin_lock_irqsave(&fm->lock, flags); | ||
241 | } | 190 | } |
242 | return 0; | 191 | |
192 | writel(TIFM_IRQ_FIFOMASK(socket_change_set) | ||
193 | | TIFM_IRQ_CARDMASK(socket_change_set), | ||
194 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
195 | |||
196 | writel(TIFM_IRQ_FIFOMASK(socket_change_set) | ||
197 | | TIFM_IRQ_CARDMASK(socket_change_set), | ||
198 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
199 | |||
200 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
201 | spin_unlock_irqrestore(&fm->lock, flags); | ||
243 | } | 202 | } |
244 | 203 | ||
245 | #ifdef CONFIG_PM | 204 | #ifdef CONFIG_PM |
@@ -258,9 +217,11 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) | |||
258 | static int tifm_7xx1_resume(struct pci_dev *dev) | 217 | static int tifm_7xx1_resume(struct pci_dev *dev) |
259 | { | 218 | { |
260 | struct tifm_adapter *fm = pci_get_drvdata(dev); | 219 | struct tifm_adapter *fm = pci_get_drvdata(dev); |
261 | int cnt, rc; | 220 | int rc; |
221 | unsigned int good_sockets = 0, bad_sockets = 0; | ||
262 | unsigned long flags; | 222 | unsigned long flags; |
263 | tifm_media_id new_ids[fm->num_sockets]; | 223 | unsigned char new_ids[fm->num_sockets]; |
224 | DECLARE_COMPLETION_ONSTACK(finish_resume); | ||
264 | 225 | ||
265 | pci_set_power_state(dev, PCI_D0); | 226 | pci_set_power_state(dev, PCI_D0); |
266 | pci_restore_state(dev); | 227 | pci_restore_state(dev); |
@@ -271,45 +232,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev) | |||
271 | 232 | ||
272 | dev_dbg(&dev->dev, "resuming host\n"); | 233 | dev_dbg(&dev->dev, "resuming host\n"); |
273 | 234 | ||
274 | for (cnt = 0; cnt < fm->num_sockets; cnt++) | 235 | for (rc = 0; rc < fm->num_sockets; rc++) |
275 | new_ids[cnt] = tifm_7xx1_toggle_sock_power( | 236 | new_ids[rc] = tifm_7xx1_toggle_sock_power( |
276 | tifm_7xx1_sock_addr(fm->addr, cnt), | 237 | tifm_7xx1_sock_addr(fm->addr, rc)); |
277 | fm->num_sockets == 2); | ||
278 | spin_lock_irqsave(&fm->lock, flags); | 238 | spin_lock_irqsave(&fm->lock, flags); |
279 | fm->socket_change_set = 0; | 239 | for (rc = 0; rc < fm->num_sockets; rc++) { |
280 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { | 240 | if (fm->sockets[rc]) { |
281 | if (fm->sockets[cnt]) { | 241 | if (fm->sockets[rc]->type == new_ids[rc]) |
282 | if (fm->sockets[cnt]->media_id == new_ids[cnt]) | 242 | good_sockets |= 1 << rc; |
283 | fm->socket_change_set |= 1 << cnt; | 243 | else |
284 | 244 | bad_sockets |= 1 << rc; | |
285 | fm->sockets[cnt]->media_id = new_ids[cnt]; | ||
286 | } | 245 | } |
287 | } | 246 | } |
288 | 247 | ||
289 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), | 248 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), |
290 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 249 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
291 | if (!fm->socket_change_set) { | 250 | dev_dbg(&dev->dev, "change sets on resume: good %x, bad %x\n", |
292 | spin_unlock_irqrestore(&fm->lock, flags); | 251 | good_sockets, bad_sockets); |
293 | return 0; | 252 | |
294 | } else { | 253 | fm->socket_change_set = 0; |
295 | fm->socket_change_set = 0; | 254 | if (good_sockets) { |
255 | fm->finish_me = &finish_resume; | ||
296 | spin_unlock_irqrestore(&fm->lock, flags); | 256 | spin_unlock_irqrestore(&fm->lock, flags); |
257 | rc = wait_for_completion_timeout(&finish_resume, HZ); | ||
258 | dev_dbg(&dev->dev, "wait returned %d\n", rc); | ||
259 | writel(TIFM_IRQ_FIFOMASK(good_sockets) | ||
260 | | TIFM_IRQ_CARDMASK(good_sockets), | ||
261 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
262 | writel(TIFM_IRQ_FIFOMASK(good_sockets) | ||
263 | | TIFM_IRQ_CARDMASK(good_sockets), | ||
264 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
265 | spin_lock_irqsave(&fm->lock, flags); | ||
266 | fm->finish_me = NULL; | ||
267 | fm->socket_change_set ^= good_sockets & fm->socket_change_set; | ||
297 | } | 268 | } |
298 | 269 | ||
299 | wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ); | 270 | fm->socket_change_set |= bad_sockets; |
271 | if (fm->socket_change_set) | ||
272 | tifm_queue_work(&fm->media_switcher); | ||
300 | 273 | ||
301 | spin_lock_irqsave(&fm->lock, flags); | 274 | spin_unlock_irqrestore(&fm->lock, flags); |
302 | writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) | ||
303 | | TIFM_IRQ_CARDMASK(fm->socket_change_set), | ||
304 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
305 | writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) | ||
306 | | TIFM_IRQ_CARDMASK(fm->socket_change_set), | ||
307 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
308 | writel(TIFM_IRQ_ENABLE, | 275 | writel(TIFM_IRQ_ENABLE, |
309 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 276 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
310 | fm->socket_change_set = 0; | ||
311 | 277 | ||
312 | spin_unlock_irqrestore(&fm->lock, flags); | ||
313 | return 0; | 278 | return 0; |
314 | } | 279 | } |
315 | 280 | ||
@@ -345,20 +310,14 @@ static int tifm_7xx1_probe(struct pci_dev *dev, | |||
345 | 310 | ||
346 | pci_intx(dev, 1); | 311 | pci_intx(dev, 1); |
347 | 312 | ||
348 | fm = tifm_alloc_adapter(); | 313 | fm = tifm_alloc_adapter(dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM |
314 | ? 4 : 2, &dev->dev); | ||
349 | if (!fm) { | 315 | if (!fm) { |
350 | rc = -ENOMEM; | 316 | rc = -ENOMEM; |
351 | goto err_out_int; | 317 | goto err_out_int; |
352 | } | 318 | } |
353 | 319 | ||
354 | fm->dev = &dev->dev; | 320 | INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media); |
355 | fm->num_sockets = (dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM) | ||
356 | ? 4 : 2; | ||
357 | fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets, | ||
358 | GFP_KERNEL); | ||
359 | if (!fm->sockets) | ||
360 | goto err_out_free; | ||
361 | |||
362 | fm->eject = tifm_7xx1_eject; | 321 | fm->eject = tifm_7xx1_eject; |
363 | pci_set_drvdata(dev, fm); | 322 | pci_set_drvdata(dev, fm); |
364 | 323 | ||
@@ -367,19 +326,16 @@ static int tifm_7xx1_probe(struct pci_dev *dev, | |||
367 | if (!fm->addr) | 326 | if (!fm->addr) |
368 | goto err_out_free; | 327 | goto err_out_free; |
369 | 328 | ||
370 | rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm); | 329 | rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm); |
371 | if (rc) | 330 | if (rc) |
372 | goto err_out_unmap; | 331 | goto err_out_unmap; |
373 | 332 | ||
374 | init_waitqueue_head(&fm->change_set_notify); | 333 | rc = tifm_add_adapter(fm); |
375 | rc = tifm_add_adapter(fm, tifm_7xx1_switch_media); | ||
376 | if (rc) | 334 | if (rc) |
377 | goto err_out_irq; | 335 | goto err_out_irq; |
378 | 336 | ||
379 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
380 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), | 337 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), |
381 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 338 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
382 | wake_up_process(fm->media_switcher); | ||
383 | return 0; | 339 | return 0; |
384 | 340 | ||
385 | err_out_irq: | 341 | err_out_irq: |
@@ -401,18 +357,12 @@ err_out: | |||
401 | static void tifm_7xx1_remove(struct pci_dev *dev) | 357 | static void tifm_7xx1_remove(struct pci_dev *dev) |
402 | { | 358 | { |
403 | struct tifm_adapter *fm = pci_get_drvdata(dev); | 359 | struct tifm_adapter *fm = pci_get_drvdata(dev); |
404 | unsigned long flags; | ||
405 | 360 | ||
361 | fm->eject = tifm_7xx1_dummy_eject; | ||
406 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | 362 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); |
407 | mmiowb(); | 363 | mmiowb(); |
408 | free_irq(dev->irq, fm); | 364 | free_irq(dev->irq, fm); |
409 | 365 | ||
410 | spin_lock_irqsave(&fm->lock, flags); | ||
411 | fm->socket_change_set = (1 << fm->num_sockets) - 1; | ||
412 | spin_unlock_irqrestore(&fm->lock, flags); | ||
413 | |||
414 | kthread_stop(fm->media_switcher); | ||
415 | |||
416 | tifm_remove_adapter(fm); | 366 | tifm_remove_adapter(fm); |
417 | 367 | ||
418 | pci_set_drvdata(dev, NULL); | 368 | pci_set_drvdata(dev, NULL); |
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 6b10ebe9d936..d195fb088f4a 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c | |||
@@ -14,71 +14,124 @@ | |||
14 | #include <linux/idr.h> | 14 | #include <linux/idr.h> |
15 | 15 | ||
16 | #define DRIVER_NAME "tifm_core" | 16 | #define DRIVER_NAME "tifm_core" |
17 | #define DRIVER_VERSION "0.7" | 17 | #define DRIVER_VERSION "0.8" |
18 | 18 | ||
19 | static struct workqueue_struct *workqueue; | ||
19 | static DEFINE_IDR(tifm_adapter_idr); | 20 | static DEFINE_IDR(tifm_adapter_idr); |
20 | static DEFINE_SPINLOCK(tifm_adapter_lock); | 21 | static DEFINE_SPINLOCK(tifm_adapter_lock); |
21 | 22 | ||
22 | static tifm_media_id *tifm_device_match(tifm_media_id *ids, | 23 | static const char *tifm_media_type_name(unsigned char type, unsigned char nt) |
23 | struct tifm_dev *dev) | ||
24 | { | 24 | { |
25 | while (*ids) { | 25 | const char *card_type_name[3][3] = { |
26 | if (dev->media_id == *ids) | 26 | { "SmartMedia/xD", "MemoryStick", "MMC/SD" }, |
27 | return ids; | 27 | { "XD", "MS", "SD"}, |
28 | ids++; | 28 | { "xd", "ms", "sd"} |
29 | } | 29 | }; |
30 | return NULL; | 30 | |
31 | if (nt > 2 || type < 1 || type > 3) | ||
32 | return NULL; | ||
33 | return card_type_name[nt][type - 1]; | ||
31 | } | 34 | } |
32 | 35 | ||
33 | static int tifm_match(struct device *dev, struct device_driver *drv) | 36 | static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id) |
34 | { | 37 | { |
35 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 38 | if (sock->type == id->type) |
36 | struct tifm_driver *fm_drv; | ||
37 | |||
38 | fm_drv = container_of(drv, struct tifm_driver, driver); | ||
39 | if (!fm_drv->id_table) | ||
40 | return -EINVAL; | ||
41 | if (tifm_device_match(fm_drv->id_table, fm_dev)) | ||
42 | return 1; | 39 | return 1; |
43 | return -ENODEV; | 40 | return 0; |
41 | } | ||
42 | |||
43 | static int tifm_bus_match(struct device *dev, struct device_driver *drv) | ||
44 | { | ||
45 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
46 | struct tifm_driver *fm_drv = container_of(drv, struct tifm_driver, | ||
47 | driver); | ||
48 | struct tifm_device_id *ids = fm_drv->id_table; | ||
49 | |||
50 | if (ids) { | ||
51 | while (ids->type) { | ||
52 | if (tifm_dev_match(sock, ids)) | ||
53 | return 1; | ||
54 | ++ids; | ||
55 | } | ||
56 | } | ||
57 | return 0; | ||
44 | } | 58 | } |
45 | 59 | ||
46 | static int tifm_uevent(struct device *dev, char **envp, int num_envp, | 60 | static int tifm_uevent(struct device *dev, char **envp, int num_envp, |
47 | char *buffer, int buffer_size) | 61 | char *buffer, int buffer_size) |
48 | { | 62 | { |
49 | struct tifm_dev *fm_dev; | 63 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
50 | int i = 0; | 64 | int i = 0; |
51 | int length = 0; | 65 | int length = 0; |
52 | const char *card_type_name[] = {"INV", "SM", "MS", "SD"}; | ||
53 | 66 | ||
54 | if (!dev || !(fm_dev = container_of(dev, struct tifm_dev, dev))) | ||
55 | return -ENODEV; | ||
56 | if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, | 67 | if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, |
57 | "TIFM_CARD_TYPE=%s", card_type_name[fm_dev->media_id])) | 68 | "TIFM_CARD_TYPE=%s", |
69 | tifm_media_type_name(sock->type, 1))) | ||
58 | return -ENOMEM; | 70 | return -ENOMEM; |
59 | 71 | ||
60 | return 0; | 72 | return 0; |
61 | } | 73 | } |
62 | 74 | ||
75 | static int tifm_device_probe(struct device *dev) | ||
76 | { | ||
77 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
78 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, | ||
79 | driver); | ||
80 | int rc = -ENODEV; | ||
81 | |||
82 | get_device(dev); | ||
83 | if (dev->driver && drv->probe) { | ||
84 | rc = drv->probe(sock); | ||
85 | if (!rc) | ||
86 | return 0; | ||
87 | } | ||
88 | put_device(dev); | ||
89 | return rc; | ||
90 | } | ||
91 | |||
92 | static void tifm_dummy_event(struct tifm_dev *sock) | ||
93 | { | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | static int tifm_device_remove(struct device *dev) | ||
98 | { | ||
99 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
100 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, | ||
101 | driver); | ||
102 | |||
103 | if (dev->driver && drv->remove) { | ||
104 | sock->card_event = tifm_dummy_event; | ||
105 | sock->data_event = tifm_dummy_event; | ||
106 | drv->remove(sock); | ||
107 | sock->dev.driver = NULL; | ||
108 | } | ||
109 | |||
110 | put_device(dev); | ||
111 | return 0; | ||
112 | } | ||
113 | |||
63 | #ifdef CONFIG_PM | 114 | #ifdef CONFIG_PM |
64 | 115 | ||
65 | static int tifm_device_suspend(struct device *dev, pm_message_t state) | 116 | static int tifm_device_suspend(struct device *dev, pm_message_t state) |
66 | { | 117 | { |
67 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 118 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
68 | struct tifm_driver *drv = fm_dev->drv; | 119 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, |
120 | driver); | ||
69 | 121 | ||
70 | if (drv && drv->suspend) | 122 | if (dev->driver && drv->suspend) |
71 | return drv->suspend(fm_dev, state); | 123 | return drv->suspend(sock, state); |
72 | return 0; | 124 | return 0; |
73 | } | 125 | } |
74 | 126 | ||
75 | static int tifm_device_resume(struct device *dev) | 127 | static int tifm_device_resume(struct device *dev) |
76 | { | 128 | { |
77 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 129 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
78 | struct tifm_driver *drv = fm_dev->drv; | 130 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, |
131 | driver); | ||
79 | 132 | ||
80 | if (drv && drv->resume) | 133 | if (dev->driver && drv->resume) |
81 | return drv->resume(fm_dev); | 134 | return drv->resume(sock); |
82 | return 0; | 135 | return 0; |
83 | } | 136 | } |
84 | 137 | ||
@@ -89,19 +142,33 @@ static int tifm_device_resume(struct device *dev) | |||
89 | 142 | ||
90 | #endif /* CONFIG_PM */ | 143 | #endif /* CONFIG_PM */ |
91 | 144 | ||
145 | static ssize_t type_show(struct device *dev, struct device_attribute *attr, | ||
146 | char *buf) | ||
147 | { | ||
148 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
149 | return sprintf(buf, "%x", sock->type); | ||
150 | } | ||
151 | |||
152 | static struct device_attribute tifm_dev_attrs[] = { | ||
153 | __ATTR(type, S_IRUGO, type_show, NULL), | ||
154 | __ATTR_NULL | ||
155 | }; | ||
156 | |||
92 | static struct bus_type tifm_bus_type = { | 157 | static struct bus_type tifm_bus_type = { |
93 | .name = "tifm", | 158 | .name = "tifm", |
94 | .match = tifm_match, | 159 | .dev_attrs = tifm_dev_attrs, |
95 | .uevent = tifm_uevent, | 160 | .match = tifm_bus_match, |
96 | .suspend = tifm_device_suspend, | 161 | .uevent = tifm_uevent, |
97 | .resume = tifm_device_resume | 162 | .probe = tifm_device_probe, |
163 | .remove = tifm_device_remove, | ||
164 | .suspend = tifm_device_suspend, | ||
165 | .resume = tifm_device_resume | ||
98 | }; | 166 | }; |
99 | 167 | ||
100 | static void tifm_free(struct class_device *cdev) | 168 | static void tifm_free(struct class_device *cdev) |
101 | { | 169 | { |
102 | struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); | 170 | struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); |
103 | 171 | ||
104 | kfree(fm->sockets); | ||
105 | kfree(fm); | 172 | kfree(fm); |
106 | } | 173 | } |
107 | 174 | ||
@@ -110,28 +177,25 @@ static struct class tifm_adapter_class = { | |||
110 | .release = tifm_free | 177 | .release = tifm_free |
111 | }; | 178 | }; |
112 | 179 | ||
113 | struct tifm_adapter *tifm_alloc_adapter(void) | 180 | struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets, |
181 | struct device *dev) | ||
114 | { | 182 | { |
115 | struct tifm_adapter *fm; | 183 | struct tifm_adapter *fm; |
116 | 184 | ||
117 | fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL); | 185 | fm = kzalloc(sizeof(struct tifm_adapter) |
186 | + sizeof(struct tifm_dev*) * num_sockets, GFP_KERNEL); | ||
118 | if (fm) { | 187 | if (fm) { |
119 | fm->cdev.class = &tifm_adapter_class; | 188 | fm->cdev.class = &tifm_adapter_class; |
120 | spin_lock_init(&fm->lock); | 189 | fm->cdev.dev = dev; |
121 | class_device_initialize(&fm->cdev); | 190 | class_device_initialize(&fm->cdev); |
191 | spin_lock_init(&fm->lock); | ||
192 | fm->num_sockets = num_sockets; | ||
122 | } | 193 | } |
123 | return fm; | 194 | return fm; |
124 | } | 195 | } |
125 | EXPORT_SYMBOL(tifm_alloc_adapter); | 196 | EXPORT_SYMBOL(tifm_alloc_adapter); |
126 | 197 | ||
127 | void tifm_free_adapter(struct tifm_adapter *fm) | 198 | int tifm_add_adapter(struct tifm_adapter *fm) |
128 | { | ||
129 | class_device_put(&fm->cdev); | ||
130 | } | ||
131 | EXPORT_SYMBOL(tifm_free_adapter); | ||
132 | |||
133 | int tifm_add_adapter(struct tifm_adapter *fm, | ||
134 | int (*mediathreadfn)(void *data)) | ||
135 | { | 199 | { |
136 | int rc; | 200 | int rc; |
137 | 201 | ||
@@ -141,59 +205,80 @@ int tifm_add_adapter(struct tifm_adapter *fm, | |||
141 | spin_lock(&tifm_adapter_lock); | 205 | spin_lock(&tifm_adapter_lock); |
142 | rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); | 206 | rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); |
143 | spin_unlock(&tifm_adapter_lock); | 207 | spin_unlock(&tifm_adapter_lock); |
144 | if (!rc) { | 208 | if (rc) |
145 | snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); | 209 | return rc; |
146 | fm->media_switcher = kthread_create(mediathreadfn, | ||
147 | fm, "tifm/%u", fm->id); | ||
148 | |||
149 | if (!IS_ERR(fm->media_switcher)) | ||
150 | return class_device_add(&fm->cdev); | ||
151 | 210 | ||
211 | snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); | ||
212 | rc = class_device_add(&fm->cdev); | ||
213 | if (rc) { | ||
152 | spin_lock(&tifm_adapter_lock); | 214 | spin_lock(&tifm_adapter_lock); |
153 | idr_remove(&tifm_adapter_idr, fm->id); | 215 | idr_remove(&tifm_adapter_idr, fm->id); |
154 | spin_unlock(&tifm_adapter_lock); | 216 | spin_unlock(&tifm_adapter_lock); |
155 | rc = -ENOMEM; | ||
156 | } | 217 | } |
218 | |||
157 | return rc; | 219 | return rc; |
158 | } | 220 | } |
159 | EXPORT_SYMBOL(tifm_add_adapter); | 221 | EXPORT_SYMBOL(tifm_add_adapter); |
160 | 222 | ||
161 | void tifm_remove_adapter(struct tifm_adapter *fm) | 223 | void tifm_remove_adapter(struct tifm_adapter *fm) |
162 | { | 224 | { |
163 | class_device_del(&fm->cdev); | 225 | unsigned int cnt; |
226 | |||
227 | flush_workqueue(workqueue); | ||
228 | for (cnt = 0; cnt < fm->num_sockets; ++cnt) { | ||
229 | if (fm->sockets[cnt]) | ||
230 | device_unregister(&fm->sockets[cnt]->dev); | ||
231 | } | ||
164 | 232 | ||
165 | spin_lock(&tifm_adapter_lock); | 233 | spin_lock(&tifm_adapter_lock); |
166 | idr_remove(&tifm_adapter_idr, fm->id); | 234 | idr_remove(&tifm_adapter_idr, fm->id); |
167 | spin_unlock(&tifm_adapter_lock); | 235 | spin_unlock(&tifm_adapter_lock); |
236 | class_device_del(&fm->cdev); | ||
168 | } | 237 | } |
169 | EXPORT_SYMBOL(tifm_remove_adapter); | 238 | EXPORT_SYMBOL(tifm_remove_adapter); |
170 | 239 | ||
171 | void tifm_free_device(struct device *dev) | 240 | void tifm_free_adapter(struct tifm_adapter *fm) |
172 | { | 241 | { |
173 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 242 | class_device_put(&fm->cdev); |
174 | kfree(fm_dev); | ||
175 | } | 243 | } |
176 | EXPORT_SYMBOL(tifm_free_device); | 244 | EXPORT_SYMBOL(tifm_free_adapter); |
177 | 245 | ||
178 | static void tifm_dummy_signal_irq(struct tifm_dev *sock, | 246 | void tifm_free_device(struct device *dev) |
179 | unsigned int sock_irq_status) | ||
180 | { | 247 | { |
181 | return; | 248 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
249 | kfree(sock); | ||
182 | } | 250 | } |
251 | EXPORT_SYMBOL(tifm_free_device); | ||
183 | 252 | ||
184 | struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) | 253 | struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id, |
254 | unsigned char type) | ||
185 | { | 255 | { |
186 | struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); | 256 | struct tifm_dev *sock = NULL; |
187 | 257 | ||
188 | if (dev) { | 258 | if (!tifm_media_type_name(type, 0)) |
189 | spin_lock_init(&dev->lock); | 259 | return sock; |
190 | 260 | ||
191 | dev->dev.parent = fm->dev; | 261 | sock = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); |
192 | dev->dev.bus = &tifm_bus_type; | 262 | if (sock) { |
193 | dev->dev.release = tifm_free_device; | 263 | spin_lock_init(&sock->lock); |
194 | dev->signal_irq = tifm_dummy_signal_irq; | 264 | sock->type = type; |
265 | sock->socket_id = id; | ||
266 | sock->card_event = tifm_dummy_event; | ||
267 | sock->data_event = tifm_dummy_event; | ||
268 | |||
269 | sock->dev.parent = fm->cdev.dev; | ||
270 | sock->dev.bus = &tifm_bus_type; | ||
271 | sock->dev.dma_mask = fm->cdev.dev->dma_mask; | ||
272 | sock->dev.release = tifm_free_device; | ||
273 | |||
274 | snprintf(sock->dev.bus_id, BUS_ID_SIZE, | ||
275 | "tifm_%s%u:%u", tifm_media_type_name(type, 2), | ||
276 | fm->id, id); | ||
277 | printk(KERN_INFO DRIVER_NAME | ||
278 | ": %s card detected in socket %u:%u\n", | ||
279 | tifm_media_type_name(type, 0), fm->id, id); | ||
195 | } | 280 | } |
196 | return dev; | 281 | return sock; |
197 | } | 282 | } |
198 | EXPORT_SYMBOL(tifm_alloc_device); | 283 | EXPORT_SYMBOL(tifm_alloc_device); |
199 | 284 | ||
@@ -218,54 +303,15 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, | |||
218 | } | 303 | } |
219 | EXPORT_SYMBOL(tifm_unmap_sg); | 304 | EXPORT_SYMBOL(tifm_unmap_sg); |
220 | 305 | ||
221 | static int tifm_device_probe(struct device *dev) | 306 | void tifm_queue_work(struct work_struct *work) |
222 | { | ||
223 | struct tifm_driver *drv; | ||
224 | struct tifm_dev *fm_dev; | ||
225 | int rc = 0; | ||
226 | const tifm_media_id *id; | ||
227 | |||
228 | drv = container_of(dev->driver, struct tifm_driver, driver); | ||
229 | fm_dev = container_of(dev, struct tifm_dev, dev); | ||
230 | get_device(dev); | ||
231 | if (!fm_dev->drv && drv->probe && drv->id_table) { | ||
232 | rc = -ENODEV; | ||
233 | id = tifm_device_match(drv->id_table, fm_dev); | ||
234 | if (id) | ||
235 | rc = drv->probe(fm_dev); | ||
236 | if (rc >= 0) { | ||
237 | rc = 0; | ||
238 | fm_dev->drv = drv; | ||
239 | } | ||
240 | } | ||
241 | if (rc) | ||
242 | put_device(dev); | ||
243 | return rc; | ||
244 | } | ||
245 | |||
246 | static int tifm_device_remove(struct device *dev) | ||
247 | { | 307 | { |
248 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 308 | queue_work(workqueue, work); |
249 | struct tifm_driver *drv = fm_dev->drv; | ||
250 | |||
251 | if (drv) { | ||
252 | fm_dev->signal_irq = tifm_dummy_signal_irq; | ||
253 | if (drv->remove) | ||
254 | drv->remove(fm_dev); | ||
255 | fm_dev->drv = NULL; | ||
256 | } | ||
257 | |||
258 | put_device(dev); | ||
259 | return 0; | ||
260 | } | 309 | } |
310 | EXPORT_SYMBOL(tifm_queue_work); | ||
261 | 311 | ||
262 | int tifm_register_driver(struct tifm_driver *drv) | 312 | int tifm_register_driver(struct tifm_driver *drv) |
263 | { | 313 | { |
264 | drv->driver.bus = &tifm_bus_type; | 314 | drv->driver.bus = &tifm_bus_type; |
265 | drv->driver.probe = tifm_device_probe; | ||
266 | drv->driver.remove = tifm_device_remove; | ||
267 | drv->driver.suspend = tifm_device_suspend; | ||
268 | drv->driver.resume = tifm_device_resume; | ||
269 | 315 | ||
270 | return driver_register(&drv->driver); | 316 | return driver_register(&drv->driver); |
271 | } | 317 | } |
@@ -279,13 +325,25 @@ EXPORT_SYMBOL(tifm_unregister_driver); | |||
279 | 325 | ||
280 | static int __init tifm_init(void) | 326 | static int __init tifm_init(void) |
281 | { | 327 | { |
282 | int rc = bus_register(&tifm_bus_type); | 328 | int rc; |
283 | 329 | ||
284 | if (!rc) { | 330 | workqueue = create_freezeable_workqueue("tifm"); |
285 | rc = class_register(&tifm_adapter_class); | 331 | if (!workqueue) |
286 | if (rc) | 332 | return -ENOMEM; |
287 | bus_unregister(&tifm_bus_type); | 333 | |
288 | } | 334 | rc = bus_register(&tifm_bus_type); |
335 | |||
336 | if (rc) | ||
337 | goto err_out_wq; | ||
338 | |||
339 | rc = class_register(&tifm_adapter_class); | ||
340 | if (!rc) | ||
341 | return 0; | ||
342 | |||
343 | bus_unregister(&tifm_bus_type); | ||
344 | |||
345 | err_out_wq: | ||
346 | destroy_workqueue(workqueue); | ||
289 | 347 | ||
290 | return rc; | 348 | return rc; |
291 | } | 349 | } |
@@ -294,6 +352,7 @@ static void __exit tifm_exit(void) | |||
294 | { | 352 | { |
295 | class_unregister(&tifm_adapter_class); | 353 | class_unregister(&tifm_adapter_class); |
296 | bus_unregister(&tifm_bus_type); | 354 | bus_unregister(&tifm_bus_type); |
355 | destroy_workqueue(workqueue); | ||
297 | } | 356 | } |
298 | 357 | ||
299 | subsys_initcall(tifm_init); | 358 | subsys_initcall(tifm_init); |