X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fmisc%2Ftifm_7xx1.c;h=2d1b3df95c5bfbb138f5d0c509fe33345dc2a7b4;hb=27345a5109f36779187311f77e30bcf596571c29;hp=e5655fef42d7ce1c575df1c7e17e2b60fa866dca;hpb=3540af8ffddcdbc7573451ac0b5cd57a2eaf8af5;p=linux-2.6 diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index e5655fef42..2d1b3df95c 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -11,7 +11,6 @@ #include #include -#include #define DRIVER_NAME "tifm_7xx1" #define DRIVER_VERSION "0.8" @@ -22,6 +21,11 @@ #define TIFM_IRQ_FIFOMASK(x) ((x) << 16) #define TIFM_IRQ_SETALL 0xffffffff +static void tifm_7xx1_dummy_eject(struct tifm_adapter *fm, + struct tifm_dev *sock) +{ +} + static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) { unsigned long flags; @@ -36,8 +40,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) { struct tifm_adapter *fm = dev_id; struct tifm_dev *sock; - unsigned int irq_status; - unsigned int cnt; + unsigned int irq_status, cnt; spin_lock(&fm->lock); irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); @@ -75,52 +78,57 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, - int is_x2) +static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr) { unsigned int s_state; int cnt; writel(0x0e00, sock_addr + SOCK_CONTROL); - for (cnt = 0; cnt < 100; cnt++) { + for (cnt = 16; cnt <= 256; cnt <<= 1) { if (!(TIFM_SOCK_STATE_POWERED & readl(sock_addr + SOCK_PRESENT_STATE))) break; - msleep(10); + + msleep(cnt); } s_state = readl(sock_addr + SOCK_PRESENT_STATE); if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) return 0; - if (is_x2) { - writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); - } else { - // SmartMedia cards need extra 40 msec - if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1) - msleep(40); - writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, - sock_addr + SOCK_CONTROL); - msleep(10); - writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED, - sock_addr + SOCK_CONTROL); - } + writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, + sock_addr + SOCK_CONTROL); + + /* xd needs some extra time before power on */ + if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) + == TIFM_TYPE_XD) + msleep(40); - for (cnt = 0; cnt < 100; cnt++) { + writel((s_state & TIFM_CTRL_POWER_MASK) | 0x0c00, + sock_addr + SOCK_CONTROL); + /* wait for power to stabilize */ + msleep(20); + for (cnt = 16; cnt <= 256; cnt <<= 1) { if ((TIFM_SOCK_STATE_POWERED & readl(sock_addr + SOCK_PRESENT_STATE))) break; - msleep(10); + + msleep(cnt); } - if (!is_x2) - writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), - sock_addr + SOCK_CONTROL); + writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), + sock_addr + SOCK_CONTROL); return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7; } +inline static void tifm_7xx1_sock_power_off(char __iomem *sock_addr) +{ + writel((~TIFM_CTRL_POWER_MASK) & readl(sock_addr + SOCK_CONTROL), + sock_addr + SOCK_CONTROL); +} + inline static char __iomem * tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num) { @@ -131,18 +139,17 @@ static void tifm_7xx1_switch_media(struct work_struct *work) { struct tifm_adapter *fm = container_of(work, struct tifm_adapter, media_switcher); + struct tifm_dev *sock; + char __iomem *sock_addr; unsigned long flags; unsigned char media_id; - char *card_name = "xx"; - int cnt; - struct tifm_dev *sock; - unsigned int socket_change_set; + unsigned int socket_change_set, cnt; spin_lock_irqsave(&fm->lock, flags); socket_change_set = fm->socket_change_set; fm->socket_change_set = 0; - dev_dbg(fm->dev, "checking media set %x\n", + dev_dbg(fm->cdev.dev, "checking media set %x\n", socket_change_set); if (!socket_change_set) { @@ -150,69 +157,46 @@ static void tifm_7xx1_switch_media(struct work_struct *work) return; } - for (cnt = 0; cnt < fm->num_sockets; cnt++) { - if (!(socket_change_set & (1 << cnt))) - continue; - sock = fm->sockets[cnt]; - if (sock) { - printk(KERN_INFO DRIVER_NAME - ": demand removing card from socket %d\n", - cnt); - fm->sockets[cnt] = NULL; - spin_unlock_irqrestore(&fm->lock, flags); - device_unregister(&sock->dev); - spin_lock_irqsave(&fm->lock, flags); - writel(0x0e00, - tifm_7xx1_sock_addr(fm->addr, cnt) - + SOCK_CONTROL); - } - + for (cnt = 0; cnt < fm->num_sockets; cnt++) { + if (!(socket_change_set & (1 << cnt))) + continue; + sock = fm->sockets[cnt]; + if (sock) { + printk(KERN_INFO + "%s : demand removing card from socket %u:%u\n", + fm->cdev.class_id, fm->id, cnt); + fm->sockets[cnt] = NULL; + sock_addr = sock->addr; spin_unlock_irqrestore(&fm->lock, flags); - media_id = tifm_7xx1_toggle_sock_power( - tifm_7xx1_sock_addr(fm->addr, cnt), - fm->num_sockets == 2); - if (media_id) { - sock = tifm_alloc_device(fm); - if (sock) { - sock->addr = tifm_7xx1_sock_addr(fm->addr, - cnt); - sock->type = media_id; - sock->socket_id = cnt; - switch (media_id) { - case 1: - card_name = "xd"; - break; - case 2: - card_name = "ms"; - break; - case 3: - card_name = "sd"; - break; - default: - tifm_free_device(&sock->dev); - spin_lock_irqsave(&fm->lock, flags); - continue; - } - snprintf(sock->dev.bus_id, BUS_ID_SIZE, - "tifm_%s%u:%u", card_name, - fm->id, cnt); - printk(KERN_INFO DRIVER_NAME - ": %s card detected in socket %d\n", - card_name, cnt); - if (!device_register(&sock->dev)) { - spin_lock_irqsave(&fm->lock, flags); - if (!fm->sockets[cnt]) { - fm->sockets[cnt] = sock; - sock = NULL; - } - spin_unlock_irqrestore(&fm->lock, flags); - } - if (sock) - tifm_free_device(&sock->dev); - } + device_unregister(&sock->dev); + spin_lock_irqsave(&fm->lock, flags); + tifm_7xx1_sock_power_off(sock_addr); + writel(0x0e00, sock_addr + SOCK_CONTROL); + } + + spin_unlock_irqrestore(&fm->lock, flags); + + media_id = tifm_7xx1_toggle_sock_power( + tifm_7xx1_sock_addr(fm->addr, cnt)); + + // tifm_alloc_device will check if media_id is valid + sock = tifm_alloc_device(fm, cnt, media_id); + if (sock) { + sock->addr = tifm_7xx1_sock_addr(fm->addr, cnt); + + if (!device_register(&sock->dev)) { spin_lock_irqsave(&fm->lock, flags); + if (!fm->sockets[cnt]) { + fm->sockets[cnt] = sock; + sock = NULL; + } + spin_unlock_irqrestore(&fm->lock, flags); } + if (sock) + tifm_free_device(&sock->dev); } + spin_lock_irqsave(&fm->lock, flags); + } writel(TIFM_IRQ_FIFOMASK(socket_change_set) | TIFM_IRQ_CARDMASK(socket_change_set), @@ -230,8 +214,16 @@ static void tifm_7xx1_switch_media(struct work_struct *work) static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) { + struct tifm_adapter *fm = pci_get_drvdata(dev); + int cnt; + dev_dbg(&dev->dev, "suspending host\n"); + for (cnt = 0; cnt < fm->num_sockets; cnt++) { + if (fm->sockets[cnt]) + tifm_7xx1_sock_power_off(fm->sockets[cnt]->addr); + } + pci_save_state(dev); pci_enable_wake(dev, pci_choose_state(dev, state), 0); pci_disable_device(dev); @@ -242,7 +234,8 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) static int tifm_7xx1_resume(struct pci_dev *dev) { struct tifm_adapter *fm = pci_get_drvdata(dev); - int cnt, rc; + int rc; + unsigned int good_sockets = 0, bad_sockets = 0; unsigned long flags; unsigned char new_ids[fm->num_sockets]; DECLARE_COMPLETION_ONSTACK(finish_resume); @@ -256,47 +249,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev) dev_dbg(&dev->dev, "resuming host\n"); - for (cnt = 0; cnt < fm->num_sockets; cnt++) - new_ids[cnt] = tifm_7xx1_toggle_sock_power( - tifm_7xx1_sock_addr(fm->addr, cnt), - fm->num_sockets == 2); + for (rc = 0; rc < fm->num_sockets; rc++) + new_ids[rc] = tifm_7xx1_toggle_sock_power( + tifm_7xx1_sock_addr(fm->addr, rc)); spin_lock_irqsave(&fm->lock, flags); - fm->socket_change_set = 0; - for (cnt = 0; cnt < fm->num_sockets; cnt++) { - if (fm->sockets[cnt]) { - if (fm->sockets[cnt]->type == new_ids[cnt]) - fm->socket_change_set |= 1 << cnt; - - fm->sockets[cnt]->type = new_ids[cnt]; + for (rc = 0; rc < fm->num_sockets; rc++) { + if (fm->sockets[rc]) { + if (fm->sockets[rc]->type == new_ids[rc]) + good_sockets |= 1 << rc; + else + bad_sockets |= 1 << rc; } } writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - if (!fm->socket_change_set) { - spin_unlock_irqrestore(&fm->lock, flags); - return 0; - } else { - fm->socket_change_set = 0; + dev_dbg(&dev->dev, "change sets on resume: good %x, bad %x\n", + good_sockets, bad_sockets); + + fm->socket_change_set = 0; + if (good_sockets) { fm->finish_me = &finish_resume; spin_unlock_irqrestore(&fm->lock, flags); + rc = wait_for_completion_timeout(&finish_resume, HZ); + dev_dbg(&dev->dev, "wait returned %d\n", rc); + writel(TIFM_IRQ_FIFOMASK(good_sockets) + | TIFM_IRQ_CARDMASK(good_sockets), + fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel(TIFM_IRQ_FIFOMASK(good_sockets) + | TIFM_IRQ_CARDMASK(good_sockets), + fm->addr + FM_SET_INTERRUPT_ENABLE); + spin_lock_irqsave(&fm->lock, flags); + fm->finish_me = NULL; + fm->socket_change_set ^= good_sockets & fm->socket_change_set; } - wait_for_completion_timeout(&finish_resume, HZ); + fm->socket_change_set |= bad_sockets; + if (fm->socket_change_set) + tifm_queue_work(&fm->media_switcher); - spin_lock_irqsave(&fm->lock, flags); - fm->finish_me = NULL; - writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) - | TIFM_IRQ_CARDMASK(fm->socket_change_set), - fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) - | TIFM_IRQ_CARDMASK(fm->socket_change_set), - fm->addr + FM_SET_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&fm->lock, flags); writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); - fm->socket_change_set = 0; - spin_unlock_irqrestore(&fm->lock, flags); return 0; } @@ -332,20 +327,13 @@ static int tifm_7xx1_probe(struct pci_dev *dev, pci_intx(dev, 1); - fm = tifm_alloc_adapter(); + fm = tifm_alloc_adapter(dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM + ? 4 : 2, &dev->dev); if (!fm) { rc = -ENOMEM; goto err_out_int; } - fm->dev = &dev->dev; - fm->num_sockets = (dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM) - ? 4 : 2; - fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets, - GFP_KERNEL); - if (!fm->sockets) - goto err_out_free; - INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media); fm->eject = tifm_7xx1_eject; pci_set_drvdata(dev, fm); @@ -363,10 +351,8 @@ static int tifm_7xx1_probe(struct pci_dev *dev, if (rc) goto err_out_irq; - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - return 0; err_out_irq: @@ -388,18 +374,18 @@ err_out: static void tifm_7xx1_remove(struct pci_dev *dev) { struct tifm_adapter *fm = pci_get_drvdata(dev); - unsigned long flags; + int cnt; + fm->eject = tifm_7xx1_dummy_eject; writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); mmiowb(); free_irq(dev->irq, fm); - spin_lock_irqsave(&fm->lock, flags); - fm->socket_change_set = (1 << fm->num_sockets) - 1; - spin_unlock_irqrestore(&fm->lock, flags); - tifm_remove_adapter(fm); + for (cnt = 0; cnt < fm->num_sockets; cnt++) + tifm_7xx1_sock_power_off(tifm_7xx1_sock_addr(fm->addr, cnt)); + pci_set_drvdata(dev, NULL); iounmap(fm->addr);