Skip to contentSkip to conversation

pcmcia: export pcmcia_bus_type

PATCH Superseded Olof Johansson 3 participants 7 messages May 12, 2007 – May 15, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 pcmcia: export pcmcia_bus_type

Export pcmcia_bus_type so modules can register bus notifiers.

drivers/pcmcia/ds.c +1 -0
Index: powerpc/drivers/pcmcia/ds.c
===================================================================
@@ -1515,6 +1515,7 @@ struct bus_type pcmcia_bus_type = {
 	.suspend = pcmcia_dev_suspend,
 	.resume = pcmcia_dev_resume,
 };
+EXPORT_SYMBOL(pcmcia_bus_type);
 
 
 static int __init init_pcmcia_bus(void)
#1 lore raw

PATCH v2 1/1 pcmcia: export pcmcia_bus_type

Export pcmcia_bus_type so module-based drivers can register bus notifiers, and add it to what seems to be the main include file.

---

Missed the include file in the last patch.

drivers/pcmcia/ds.c +1 -0
Index: 2.6.21/drivers/pcmcia/ds.c
===================================================================
@@ -1404,6 +1404,7 @@ struct bus_type pcmcia_bus_type = {
 	.suspend = pcmcia_dev_suspend,
 	.resume = pcmcia_dev_resume,
 };
+EXPORT_SYMBOL(pcmcia_bus_type);
 
 
 static int __init init_pcmcia_bus(void)
include/pcmcia/ss.h +1 -0
Index: 2.6.21/include/pcmcia/ss.h
===================================================================
@@ -298,6 +298,7 @@ extern int pcmcia_register_socket(struct
 extern void pcmcia_unregister_socket(struct pcmcia_socket *socket);
 
 extern struct class pcmcia_socket_class;
+extern struct bus_type pcmcia_bus_type;
 
 /* socket drivers are expected to use these callbacks in their .drv struct */
 extern int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state);
#2 lore raw
Quoted 2 lines · Olof Johansson

Export pcmcia_bus_type so module-based drivers can register bus notifiers, and add it to what seems to be the main include file.

This all sounds rather fishy to me. Care to send the code along that makes use of it.

And even if we're going to add the export it should be _GPL because it's a deeply internal implementation detail. Then again I'm not exactly convinced we should export it yet.

#3 lore raw

Hi,

Quoted 6 lines · Christoph Hellwig
Quoted 2 lines · Olof Johansson

Export pcmcia_bus_type so module-based drivers can register bus notifiers, and add it to what seems to be the main include file.

This all sounds rather fishy to me. Care to send the code along that makes use of it.

Sure. I was planning on sending the code that uses this on Friday but ran out of time. Also, given the other discussions regarding I/O space and mappings, I need to rework it so I ended up holding back until I had time to do that.

I use the notifier hooks to fix up the ->archdata.dma_ops for the devices added. Maybe there's an easier way to do it that I'm missing.

Current patch included below, note that it's not not 100% cleaned up.

Quoted 3 lines

And even if we're going to add the export it should be _GPL because it's a deeply internal implementation detail. Then again I'm not exactly convinced we should export it yet.

Other buses already export it (i.e. PCI). Also, other symbols in the drivers were exported non-_GPL so I went with that. I'll be happy to switch to the other kind of export if needed. Seems like PCI exports it non-_GPL as well.

-Olof

Driver for the CompactFlash slot on the PA Semi Electra eval board. It's a simple device sitting on localbus, with interrupts and detect/voltage control over GPIO.

The driver is implemented as an of_platform driver, and adds localbus as a bus being probed by the of_platform framework.

---

This patch depends on the PCMCIA pcmcia_bus_type export to compile:

http://lists.infradead.org/pipermail/linux-pcmcia/2007-May/004613.html

and on the ioaddr_t + ioport_map changes to work at runtime:

http://patchwork.ozlabs.org/linuxppc/patch?id=11092
http://patchwork.ozlabs.org/linuxppc/patch?id=11093

drivers/pcmcia/Kconfig +7 -0
Index: 2.6.21/drivers/pcmcia/Kconfig
===================================================================
@@ -270,6 +270,13 @@ config AT91_CF
 	  Say Y here to support the CompactFlash controller on AT91 chips.
 	  Or choose M to compile the driver as a module named "at91_cf".
 
+config ELECTRA_CF
+	tristate "Electra CompactFlash Controller"
+	depends on PCMCIA && PPC_PASEMI
+	help
+	  Say Y here to support the CompactFlash controller on the
+	  PA Semi Electra eval board.
+
 config PCCARD_NONSTATIC
 	tristate
 
drivers/pcmcia/Makefile +1 -0
Index: 2.6.21/drivers/pcmcia/Makefile
===================================================================
@@ -37,6 +37,7 @@ obj-$(CONFIG_PCMCIA_VRC4171)			+= vrc417
 obj-$(CONFIG_PCMCIA_VRC4173)			+= vrc4173_cardu.o
 obj-$(CONFIG_OMAP_CF)				+= omap_cf.o
 obj-$(CONFIG_AT91_CF)				+= at91_cf.o
+obj-$(CONFIG_ELECTRA_CF)			+= electra_cf.o
 
 sa11xx_core-y					+= soc_common.o sa11xx_base.o
 pxa2xx_core-y					+= soc_common.o pxa2xx_base.o
drivers/pcmcia/electra_cf.c new file +393 -0

393 lines not on this page · raw message

arch/powerpc/platforms/pasemi/setup.c +1 -0
Index: 2.6.21/arch/powerpc/platforms/pasemi/setup.c
===================================================================
@@ -245,6 +245,7 @@ static void __init pas_init_early(void)
 }
 
 static struct of_device_id pasemi_bus_ids[] = {
+	{ .type = "localbus", },
 	{ .type = "sdc", },
 	{},
 };
#4 lore raw

Some random review comments on your driver, while I was passing. Spotted a few things.

drivers/pcmcia/electra_cf.c line 27

Quoted 5 lines · Olof Johansson
drivers/pcmcia/electra_cf.c new file +5 -0
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/platform_device.h>

Silly duplicate includes.

Quoted 6 lines
drivers/pcmcia/electra_cf.c new file +6 -0
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <pcmcia/ss.h>

drivers/pcmcia/electra_cf.c line 103

Quoted 10 lines
drivers/pcmcia/electra_cf.c new file +9 -0
+#include <asm/of_platform.h>
+
+static const char driver_name[] = "electra-cf";
...
+static int electra_cf_get_status(struct pcmcia_socket *s, u_int *sp)
+{
+	struct electra_cf_socket *cf;
+
+	if (!sp)
+		return -EINVAL;

Never called with a NULL argument, so useless check.

Quoted 10 lines
drivers/pcmcia/electra_cf.c new file +10 -0
+
+	cf = container_of(s, struct electra_cf_socket, socket);
+
+	/* NOTE CF is always 3VCARD */
+	if (electra_cf_present(cf)) {
+		struct electra_cf_socket *cf;
+
+		*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
+		cf = container_of(s, struct electra_cf_socket, socket);
+		s->pci_irq = cf->irq;

drivers/pcmcia/electra_cf.c line 157

Quoted 10 lines
drivers/pcmcia/electra_cf.c new file +9 -0
+	} else
+		*sp = 0;
+	return 0;
+}
...
+static int electra_cf_ss_suspend(struct pcmcia_socket *s)
+{
+	pr_debug("%s: %s\n", driver_name, __FUNCTION__);
+	return electra_cf_set_socket(s, &dead_socket);
+}

That's already done for you. Remove this function entirely and set the ".suspend" method to NULL.

drivers/pcmcia/electra_cf.c line 169

Quoted 11 lines
drivers/pcmcia/electra_cf.c new file +11 -0
+static int electra_cf_set_io_map(struct pcmcia_socket *s,
+				 struct pccard_io_map *io)
+{
+	struct electra_cf_socket *cf;
+
+	cf = container_of(s, struct electra_cf_socket, socket);
+	io->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
+	io->start = (unsigned long)cf->io_base;
+	io->stop = (unsigned long)cf->io_base + 0x800 - 1;
+	return 0;
+}

PCMCIA code will ignore your writes to 'io'. Therefore, does this do anything useful? If not, an empty function will do.

Quoted 81 lines

...

drivers/pcmcia/electra_cf.c new file +80 -0
+static int __devinit electra_cf_probe(struct of_device *ofdev,
+				      const struct of_device_id *match)
+{
+	struct device *device = &ofdev->dev;
+	struct device_node *np = ofdev->node;
+	struct electra_cf_socket   *cf;
+	struct resource mem, io;
+	int status;
+	const unsigned int *prop;
+	int err;
+
+	err = of_address_to_resource(np, 0, &mem);
+	if (err)
+		return -EINVAL;
+
+	err = of_address_to_resource(np, 1, &io);
+	if (err)
+		return -EINVAL;
+
+	cf = kzalloc(sizeof *cf, GFP_KERNEL);
+	if (!cf)
+		return -ENOMEM;
+
+	init_timer(&cf->timer);
+	cf->timer.function = electra_cf_timer;
+	cf->timer.data = (unsigned long) cf;
+
+	cf->ofdev = ofdev;
+	cf->mem_phys = mem.start;
+	cf->mem_base = ioremap(mem.start, mem.end - mem.start);
+	cf->io_base = ioremap(io.start, io.end - io.start);
+	cf->gpio_base = ioremap(0xfc103000, 0x1000);
+	dev_set_drvdata(device, cf);
+
+	if (!cf->mem_base || !cf->io_base || !cf->gpio_base) {
+		dev_err(device, "can't ioremap ranges\n");
+		status = -ENOMEM;
+		goto fail1;
+	}
+
+	cf->iomem.start = (unsigned long)cf->io_base;
+	cf->iomem.end = (unsigned long)cf->io_base + (io.end - io.start);
+	cf->iomem.flags = IORESOURCE_MEM;
+
+	cf->irq = irq_of_parse_and_map(np, 0);
+
+	status = request_irq(cf->irq, electra_cf_irq, IRQF_SHARED,
+			     driver_name, cf);
+	if (status < 0) {
+		dev_err(device, "request_irq failed\n");
+		goto fail1;
+	}
+
+	cf->socket.pci_irq = cf->irq;
+
+	prop = get_property(np, "card-detect-gpio", NULL);
+	cf->gpio_detect = *prop;
+	prop = get_property(np, "card-vsense-gpio", NULL);
+	cf->gpio_vsense = *prop;
+	prop = get_property(np, "card-3v-gpio", NULL);
+	cf->gpio_3v = *prop;
+	prop = get_property(np, "card-5v-gpio", NULL);
+	cf->gpio_5v = *prop;
+
+	/* pcmcia layer only remaps "real" memory not iospace */
+	cf->socket.io_offset = (unsigned long)cf->io_base;
+
+	/* reserve chip-select regions */
+	if (!request_mem_region(mem.start, mem.end + 1 - mem.start,
+				driver_name)) {
+		status = -ENXIO;
+		dev_err(device, "Can't claim memory region\n");
+		goto fail1;
+	}
+
+	if (!request_mem_region(io.start, io.end + 1 - io.start,
+				driver_name)) {
+		status = -ENXIO;
+		dev_err(device, "Can't claim I/O region\n");
+		goto fail2;

drivers/pcmcia/electra_cf.c line 284

Quoted 10 lines
drivers/pcmcia/electra_cf.c new file +10 -0
+	}
+
+	cf->socket.owner = THIS_MODULE;
+	cf->socket.dev.parent = &ofdev->dev;
+	cf->socket.ops = &electra_cf_ops;
+	cf->socket.resource_ops = &pccard_static_ops;
+	cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP |
+				SS_CAP_MEM_ALIGN;
+	cf->socket.map_size = 0x800;
+	cf->socket.io[0].res = &cf->iomem;

Not a good idea - PCMCIA manages this resources itself and will free it when the card is removed. Use ss_cap_static_map and socket.io_offset.

Quoted 32 lines
drivers/pcmcia/electra_cf.c new file +32 -0
+
+	status = pcmcia_register_socket(&cf->socket);
+	if (status < 0) {
+		dev_err(device, "pcmcia_register_socket failed\n");
+		goto fail3;
+	}
+
+	dev_info(device, "at mem 0x%lx io 0x%lx irq %d\n",
+		 mem.start, io.start, cf->irq);
+
+	cf->active = 1;
+	electra_cf_timer((unsigned long)cf);
+	return 0;
+
+fail3:
+	release_mem_region(io.start, io.end + 1 - io.start);
+fail2:
+	release_mem_region(mem.start, mem.end + 1 - mem.start);
+fail1:
+	if (cf->io_base)
+		iounmap(cf->io_base);
+	if (cf->mem_base)
+		iounmap(cf->mem_base);
+	if (cf->gpio_base)
+		iounmap(cf->gpio_base);
+	device_init_wakeup(&ofdev->dev, 0);
+	kfree(cf);
+	return status;
+
+}
+
+static int __devexit electra_cf_remove(struct of_device *ofdev)

drivers/pcmcia/electra_cf.c line 326

Quoted 10 lines
drivers/pcmcia/electra_cf.c new file +10 -0
+{
+	struct device *device = &ofdev->dev;
+	struct electra_cf_socket *cf;
+
+	cf = dev_get_drvdata(device);
+
+	cf->active = 0;
+	pcmcia_unregister_socket(&cf->socket);
+	del_timer_sync(&cf->timer);
+	free_irq(cf->irq, cf);

What if an IRQ occurs after the timer has been deleted? Doesn't the interrupt handler re-add the timer back?

Quoted 11 lines
drivers/pcmcia/electra_cf.c new file +11 -0
+
+	iounmap(cf->io_base);
+	iounmap(cf->mem_base);
+	iounmap(cf->gpio_base);
+	release_mem_region(cf->mem_phys, cf->mem_size);
+	release_region(cf->io_phys, cf->io_size);
+
+	kfree(cf);
+
+	return 0;
+}
Signature · 3 lines
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:
#5 lore raw

Hi,

Quoted 2 lines · Russell King

Some random review comments on your driver, while I was passing. Spotted a few things.

Thanks, I appreciate the feedback.

Quoted 8 lines · Olof Johansson

drivers/pcmcia/electra_cf.c line 27

Quoted 5 lines
drivers/pcmcia/electra_cf.c new file +5 -0
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/platform_device.h>

Silly duplicate includes.

Silly indeed.

Quoted 10 lines
Quoted 10 lines
drivers/pcmcia/electra_cf.c new file +9 -0
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <pcmcia/ss.h>
+#include <asm/of_platform.h>
+
+static const char driver_name[] = "electra-cf";
...
Quoted 8 lines

drivers/pcmcia/electra_cf.c line 103

Quoted 6 lines
drivers/pcmcia/electra_cf.c new file +6 -0
+static int electra_cf_get_status(struct pcmcia_socket *s, u_int *sp)
+{
+	struct electra_cf_socket *cf;
+
+	if (!sp)
+		return -EINVAL;

Never called with a NULL argument, so useless check.

Ok. Carried over from omap_cf, but I'll definitely fix it here.

Quoted 15 lines
Quoted 15 lines
drivers/pcmcia/electra_cf.c new file +14 -0
+
+	cf = container_of(s, struct electra_cf_socket, socket);
+
+	/* NOTE CF is always 3VCARD */
+	if (electra_cf_present(cf)) {
+		struct electra_cf_socket *cf;
+
+		*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
+		cf = container_of(s, struct electra_cf_socket, socket);
+		s->pci_irq = cf->irq;
+	} else
+		*sp = 0;
+	return 0;
+}
...
Quoted 8 lines

drivers/pcmcia/electra_cf.c line 157

Quoted 5 lines
drivers/pcmcia/electra_cf.c new file +5 -0
+static int electra_cf_ss_suspend(struct pcmcia_socket *s)
+{
+	pr_debug("%s: %s\n", driver_name, __FUNCTION__);
+	return electra_cf_set_socket(s, &dead_socket);
+}

That's already done for you. Remove this function entirely and set the ".suspend" method to NULL.

Thanks, will do.

Quoted 15 lines

drivers/pcmcia/electra_cf.c line 169

Quoted 12 lines
drivers/pcmcia/electra_cf.c new file +11 -0
+static int electra_cf_set_io_map(struct pcmcia_socket *s,
+				 struct pccard_io_map *io)
+{
+	struct electra_cf_socket *cf;
+
+	cf = container_of(s, struct electra_cf_socket, socket);
+	io->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
+	io->start = (unsigned long)cf->io_base;
+	io->stop = (unsigned long)cf->io_base + 0x800 - 1;
+	return 0;
+}

PCMCIA code will ignore your writes to 'io'. Therefore, does this do anything useful? If not, an empty function will do.

Not sure if it does, it came over from omap_cf with some modifications by me. I'll take a closer look when I rework the rest of the I/O mapping stuff.

Quoted 11 lines

drivers/pcmcia/electra_cf.c line 284

Quoted 8 lines
drivers/pcmcia/electra_cf.c new file +8 -0
+	cf->socket.owner = THIS_MODULE;
+	cf->socket.dev.parent = &ofdev->dev;
+	cf->socket.ops = &electra_cf_ops;
+	cf->socket.resource_ops = &pccard_static_ops;
+	cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP |
+				SS_CAP_MEM_ALIGN;
+	cf->socket.map_size = 0x800;
+	cf->socket.io[0].res = &cf->iomem;

Not a good idea - PCMCIA manages this resources itself and will free it when the card is removed. Use ss_cap_static_map and socket.io_offset.

Right, I'm going to rework those parts alltogether.

Quoted 6 lines
Quoted 6 lines
drivers/pcmcia/electra_cf.c new file +6 -0
+static int __devexit electra_cf_remove(struct of_device *ofdev)
+{
+	struct device *device = &ofdev->dev;
+	struct electra_cf_socket *cf;
+
+	cf = dev_get_drvdata(device);
Quoted 8 lines

drivers/pcmcia/electra_cf.c line 326

Quoted 5 lines
drivers/pcmcia/electra_cf.c new file +5 -0
+
+	cf->active = 0;
+	pcmcia_unregister_socket(&cf->socket);
+	del_timer_sync(&cf->timer);
+	free_irq(cf->irq, cf);

What if an IRQ occurs after the timer has been deleted? Doesn't the interrupt handler re-add the timer back?

Ah, yes, good point.

Thanks,

-Olof

#6 lore raw

PATCH v3 1/1 pcmcia: export pcmcia_bus_type (GPL-only)

In reply to Olof Johansson

Export pcmcia_bus_type so module-based drivers can register bus notifiers.

drivers/pcmcia/ds.c +1 -0
Index: powerpc/drivers/pcmcia/ds.c
===================================================================
@@ -1404,6 +1404,7 @@ struct bus_type pcmcia_bus_type = {
 	.suspend = pcmcia_dev_suspend,
 	.resume = pcmcia_dev_resume,
 };
+EXPORT_SYMBOL_GPL(pcmcia_bus_type);
 
 
 static int __init init_pcmcia_bus(void)
include/pcmcia/ss.h +1 -0
Index: powerpc/include/pcmcia/ss.h
===================================================================
@@ -298,6 +298,7 @@ extern int pcmcia_register_socket(struct
 extern void pcmcia_unregister_socket(struct pcmcia_socket *socket);
 
 extern struct class pcmcia_socket_class;
+extern struct bus_type pcmcia_bus_type;
 
 /* socket drivers are expected to use these callbacks in their .drv struct */
 extern int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state);