From 097e296d6175881eba7244de7222de61e9569911 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Thu, 26 Mar 2009 21:45:05 +0000 Subject: [PATCH] PCMCIA: soc_common: provide single socket add/remove functionality Factor out the functionality for adding and removing a single socket, thereby allowing SoCs to individually register each socket. The advantage of this approach is that SoCs can then extend soc_pcmcia_socket as they wish. Signed-off-by: Russell King Signed-off-by: Dominik Brodowski --- drivers/pcmcia/soc_common.c | 254 +++++++++++++++++++----------------- drivers/pcmcia/soc_common.h | 3 +- 2 files changed, 137 insertions(+), 120 deletions(-) diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index a2d448f20b0f..9bfa74a6d8ff 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -574,7 +574,7 @@ void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, EXPORT_SYMBOL(soc_pcmcia_enable_irqs); -LIST_HEAD(soc_pcmcia_sockets); +static LIST_HEAD(soc_pcmcia_sockets); static DEFINE_MUTEX(soc_pcmcia_sockets_lock); #ifdef CONFIG_CPU_FREQ @@ -619,124 +619,161 @@ module_exit(soc_pcmcia_cpufreq_unregister); #endif +void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) +{ + mutex_lock(&soc_pcmcia_sockets_lock); + del_timer_sync(&skt->poll_timer); + + pcmcia_unregister_socket(&skt->socket); + + flush_scheduled_work(); + + skt->ops->hw_shutdown(skt); + + soc_common_pcmcia_config_skt(skt, &dead_socket); + + list_del(&skt->node); + mutex_unlock(&soc_pcmcia_sockets_lock); + + iounmap(skt->virt_io); + skt->virt_io = NULL; + release_resource(&skt->res_attr); + release_resource(&skt->res_mem); + release_resource(&skt->res_io); + release_resource(&skt->res_skt); +} +EXPORT_SYMBOL(soc_pcmcia_remove_one); + +int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) +{ + int ret; + + init_timer(&skt->poll_timer); + skt->poll_timer.function = soc_common_pcmcia_poll_event; + skt->poll_timer.data = (unsigned long)skt; + skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD; + + ret = request_resource(&iomem_resource, &skt->res_skt); + if (ret) + goto out_err_1; + + ret = request_resource(&skt->res_skt, &skt->res_io); + if (ret) + goto out_err_2; + + ret = request_resource(&skt->res_skt, &skt->res_mem); + if (ret) + goto out_err_3; + + ret = request_resource(&skt->res_skt, &skt->res_attr); + if (ret) + goto out_err_4; + + skt->virt_io = ioremap(skt->res_io.start, 0x10000); + if (skt->virt_io == NULL) { + ret = -ENOMEM; + goto out_err_5; + } + + mutex_lock(&soc_pcmcia_sockets_lock); + + list_add(&skt->node, &soc_pcmcia_sockets); + + /* + * We initialize default socket timing here, because + * we are not guaranteed to see a SetIOMap operation at + * runtime. + */ + skt->ops->set_timing(skt); + + ret = skt->ops->hw_init(skt); + if (ret) + goto out_err_6; + + skt->socket.ops = &soc_common_pcmcia_operations; + skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; + skt->socket.resource_ops = &pccard_static_ops; + skt->socket.irq_mask = 0; + skt->socket.map_size = PAGE_SIZE; + skt->socket.pci_irq = skt->irq; + skt->socket.io_offset = (unsigned long)skt->virt_io; + + skt->status = soc_common_pcmcia_skt_state(skt); + + ret = pcmcia_register_socket(&skt->socket); + if (ret) + goto out_err_7; + + add_timer(&skt->poll_timer); + + mutex_unlock(&soc_pcmcia_sockets_lock); + + ret = device_create_file(&skt->socket.dev, &dev_attr_status); + if (ret) + goto out_err_8; + + return ret; + + out_err_8: + mutex_lock(&soc_pcmcia_sockets_lock); + del_timer_sync(&skt->poll_timer); + pcmcia_unregister_socket(&skt->socket); + + out_err_7: + flush_scheduled_work(); + + skt->ops->hw_shutdown(skt); + out_err_6: + list_del(&skt->node); + mutex_unlock(&soc_pcmcia_sockets_lock); + iounmap(skt->virt_io); + out_err_5: + release_resource(&skt->res_attr); + out_err_4: + release_resource(&skt->res_mem); + out_err_3: + release_resource(&skt->res_io); + out_err_2: + release_resource(&skt->res_skt); + out_err_1: + + return ret; +} +EXPORT_SYMBOL(soc_pcmcia_add_one); + int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, struct skt_dev_info *sinfo) { struct soc_pcmcia_socket *skt; int ret, i; - mutex_lock(&soc_pcmcia_sockets_lock); - /* * Initialise the per-socket structure. */ - for (i = 0; i < sinfo->nskt; i++) { + for (i = ret = 0; i < sinfo->nskt; i++) { skt = &sinfo->skt[i]; - skt->socket.ops = &soc_common_pcmcia_operations; skt->socket.owner = ops->owner; skt->socket.dev.parent = dev; - init_timer(&skt->poll_timer); - skt->poll_timer.function = soc_common_pcmcia_poll_event; - skt->poll_timer.data = (unsigned long)skt; - skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD; - skt->dev = dev; skt->ops = ops; - ret = request_resource(&iomem_resource, &skt->res_skt); + ret = soc_pcmcia_add_one(skt); if (ret) - goto out_err_1; - - ret = request_resource(&skt->res_skt, &skt->res_io); - if (ret) - goto out_err_2; - - ret = request_resource(&skt->res_skt, &skt->res_mem); - if (ret) - goto out_err_3; - - ret = request_resource(&skt->res_skt, &skt->res_attr); - if (ret) - goto out_err_4; - - skt->virt_io = ioremap(skt->res_io.start, 0x10000); - if (skt->virt_io == NULL) { - ret = -ENOMEM; - goto out_err_5; - } - - list_add(&skt->node, &soc_pcmcia_sockets); - - /* - * We initialize default socket timing here, because - * we are not guaranteed to see a SetIOMap operation at - * runtime. - */ - ops->set_timing(skt); - - ret = ops->hw_init(skt); - if (ret) - goto out_err_6; - - skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; - skt->socket.resource_ops = &pccard_static_ops; - skt->socket.irq_mask = 0; - skt->socket.map_size = PAGE_SIZE; - skt->socket.pci_irq = skt->irq; - skt->socket.io_offset = (unsigned long)skt->virt_io; - - skt->status = soc_common_pcmcia_skt_state(skt); - - ret = pcmcia_register_socket(&skt->socket); - if (ret) - goto out_err_7; + break; WARN_ON(skt->socket.sock != i); - - add_timer(&skt->poll_timer); - - ret = device_create_file(&skt->socket.dev, &dev_attr_status); - if (ret) - goto out_err_8; } - dev_set_drvdata(dev, sinfo); - ret = 0; - goto out; + if (ret) { + while (--i >= 0) + soc_pcmcia_remove_one(&sinfo->skt[i]); + kfree(sinfo); + } else { + dev_set_drvdata(dev, sinfo); + } - do { - skt = &sinfo->skt[i]; - - device_remove_file(&skt->socket.dev, &dev_attr_status); - out_err_8: - del_timer_sync(&skt->poll_timer); - pcmcia_unregister_socket(&skt->socket); - - out_err_7: - flush_scheduled_work(); - - ops->hw_shutdown(skt); - out_err_6: - list_del(&skt->node); - iounmap(skt->virt_io); - out_err_5: - release_resource(&skt->res_attr); - out_err_4: - release_resource(&skt->res_mem); - out_err_3: - release_resource(&skt->res_io); - out_err_2: - release_resource(&skt->res_skt); - out_err_1: - i--; - } while (i > 0); - - kfree(sinfo); - - out: - mutex_unlock(&soc_pcmcia_sockets_lock); return ret; } EXPORT_SYMBOL(soc_common_drv_pcmcia_probe); @@ -748,29 +785,8 @@ int soc_common_drv_pcmcia_remove(struct device *dev) dev_set_drvdata(dev, NULL); - mutex_lock(&soc_pcmcia_sockets_lock); - for (i = 0; i < sinfo->nskt; i++) { - struct soc_pcmcia_socket *skt = &sinfo->skt[i]; - - del_timer_sync(&skt->poll_timer); - - pcmcia_unregister_socket(&skt->socket); - - flush_scheduled_work(); - - skt->ops->hw_shutdown(skt); - - soc_common_pcmcia_config_skt(skt, &dead_socket); - - list_del(&skt->node); - iounmap(skt->virt_io); - skt->virt_io = NULL; - release_resource(&skt->res_attr); - release_resource(&skt->res_mem); - release_resource(&skt->res_io); - release_resource(&skt->res_skt); - } - mutex_unlock(&soc_pcmcia_sockets_lock); + for (i = 0; i < sinfo->nskt; i++) + soc_pcmcia_remove_one(&sinfo->skt[i]); kfree(sinfo); diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index 290e143839ee..51c72bae018e 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h @@ -135,7 +135,8 @@ extern void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_ extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *); -extern struct list_head soc_pcmcia_sockets; +void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt); +int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt); extern int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, struct skt_dev_info *sinfo); extern int soc_common_drv_pcmcia_remove(struct device *dev);