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 <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index a2d448f..9bfa74a 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -574,7 +574,7 @@
 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 @@
 
 #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 @@
 
 	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);