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); |
