r4686 - developers/werner/wlan-spi/patches

werner at docs.openmoko.org werner at docs.openmoko.org
Wed Oct 1 15:44:29 CEST 2008


Author: werner
Date: 2008-10-01 15:44:28 +0200 (Wed, 01 Oct 2008)
New Revision: 4686

Added:
   developers/werner/wlan-spi/patches/hif-can-do-async.patch
   developers/werner/wlan-spi/patches/hif-direct-interrupt.patch
Modified:
   developers/werner/wlan-spi/patches/series
Log:
Use direct SPI interrupts and let the HIF handle asynchronous operations.



Added: developers/werner/wlan-spi/patches/hif-can-do-async.patch
===================================================================
--- developers/werner/wlan-spi/patches/hif-can-do-async.patch	                        (rev 0)
+++ developers/werner/wlan-spi/patches/hif-can-do-async.patch	2008-10-01 13:44:28 UTC (rev 4686)
@@ -0,0 +1,20 @@
+After going to direct interrupts, our HIF can also handle asynchronous
+requests. Not entirely sure what kept them from working before. Most
+likely, there is some synchronization problem that we're now avoiding
+by also going through the system's main workqueue and thus implicitly
+synchronize with SPI.
+
+Index: korig/drivers/ar6000/hif/hif2.c
+===================================================================
+--- korig.orig/drivers/ar6000/hif/hif2.c	2008-10-01 09:34:27.000000000 -0300
++++ korig/drivers/ar6000/hif/hif2.c	2008-10-01 09:34:39.000000000 -0300
+@@ -282,8 +282,7 @@
+ 			mbs_cfg[i] = HIF_MBOX_START_ADDR(i);
+ 		break;
+ 	case HIF_DEVICE_GET_IRQ_PROC_MODE:
+-		*ipm_cfg = HIF_DEVICE_IRQ_SYNC_ONLY;
+-//		*ipm_cfg = HIF_DEVICE_IRQ_ASYNC_SYNC;
++		*ipm_cfg = HIF_DEVICE_IRQ_ASYNC_SYNC;
+ 		break;
+ 	default:
+ 		return A_ERROR;

Added: developers/werner/wlan-spi/patches/hif-direct-interrupt.patch
===================================================================
--- developers/werner/wlan-spi/patches/hif-direct-interrupt.patch	                        (rev 0)
+++ developers/werner/wlan-spi/patches/hif-direct-interrupt.patch	2008-10-01 13:44:28 UTC (rev 4686)
@@ -0,0 +1,256 @@
+Proof-of-concept hack to directly use the interrupt coming from the card.
+
+The performance improvement isn't quite what I expected, only a meager
+4-5%, but perhaps more can be gained if we find a way to strip off some
+of the execution contexts. Right now, interrupts go through a workqueue,
+the AR6k driver, then the SDIO stack gets called from a kernel thread,
+and spi_bitbang uses a workqueue as well. It's almost surprising that we
+still get something done at all with those context switches.
+
+Index: korig/drivers/ar6000/hif/hif2.c
+===================================================================
+--- korig.orig/drivers/ar6000/hif/hif2.c	2008-10-01 09:34:08.000000000 -0300
++++ korig/drivers/ar6000/hif/hif2.c	2008-10-01 09:34:27.000000000 -0300
+@@ -21,9 +21,13 @@
+ #include <linux/list.h>
+ #include <linux/wait.h>
+ #include <linux/spinlock.h>
++#include <linux/irq.h>
++#include <linux/workqueue.h>
+ #include <linux/mmc/sdio_func.h>
+ #include <linux/mmc/sdio.h>
++
+ #include <asm/gpio.h>
++#include <asm/arch/regs-gpio.h>
+ 
+ #include "athdefs.h"
+ #include "a_types.h"
+@@ -59,6 +63,9 @@
+ 	void *htc_handle;
+ 	struct sdio_func *func;
+ 
++	int irq;
++	struct work_struct work;
++
+ 	/*
+ 	 * @@@ our sweet little bit of bogosity - the mechanism that lets us
+ 	 * use the SDIO stack from softirqs. This really wants to use skbs.
+@@ -99,6 +106,18 @@
+ }
+ 
+ 
++/*
++ * @@@ The slave select games below don't quite work, I think because SPI sets
++ * it to inactive _after_ telling its caller that the operation is complete
++ * (which it is, after all).
++ *
++ * Doing this properly will be a bit of fun, I suppose, because the SPI stack
++ * doesn't have the concept of briefly disabling slave select right before a
++ * transmission.
++ *
++ * For some yet unexplained reason, hacking the SPI driver isn't enough, and we
++ * need to force SS low with the s3c2410_gpio_setpin here.
++ */
+ static A_STATUS process_request(struct hif_request *req)
+ {
+ 	int ret;
+@@ -106,10 +125,12 @@
+ 
+ 	dev_dbg(&req->func->dev, "process_request(req %p)\n", req);
+ 	sdio_claim_host(req->func);
++	//s3c2410_gpio_setpin(S3C2410_GPG2, 1); /* raise SS */
+ 	if (req->read)
+ 		ret = req->read(req->func, req->buf, req->addr, req->len);
+ 	else
+ 		ret = req->write(req->func, req->addr, req->buf, req->len);
++	s3c2410_gpio_setpin(S3C2410_GPG2, 0); /* lower SS to allow interrupts */
+ 	sdio_release_host(req->func);
+ 	status = ret ? A_ERROR : A_OK;
+ 	if (req->completion)
+@@ -281,7 +302,7 @@
+ 
+ /* ========================================================================= */
+ 
+-#if 1
++#if 0
+ 
+ /*
+  * Volatile ought to be good enough to make gcc do the right thing on S3C24xx.
+@@ -396,69 +417,64 @@
+ /*
+  * The code below is for handling interrupts signalled out-of-band.
+  */
+-#if 0
+-#define IRQ_GPIO S3C2410_GPE8 /* SDDAT1 */
+-
+-
+-static atomic_t mask = ATOMIC_INIT(1);
++#if 1
+ 
+ 
+-static void sdio_ar6000_irq(struct sdio_func *func)
++static void sdio_ar6000_work(struct work_struct *work)
+ {
+-	HIF_DEVICE *device = sdio_get_drvdata(func);
++	HIF_DEVICE *device = container_of(work, struct hif_device, work);
++	struct device *dev = HIFGetOSDevice(device);
++	A_STATUS status;
++
++	dev_dbg(dev, "sdio_ar6000_work-> %p\n", htcCallbacks.dsrHandler);
+ 
+-	printk(KERN_DEBUG "sdio_ar6000_irq -> %p\n", htcCallbacks.dsrHandler);
+-	BUG();
++	/* absorb the usual initial stray interrupt */
++	if (!device->htc_handle) {
++		HIFAckInterrupt(device);
++		return;
++	}
++	status = htcCallbacks.dsrHandler(device->htc_handle);
++	BUG_ON(status != A_OK);
+ }
+ 
+ 
+-static void sdio_ar6000_poll(void *context)
++static irqreturn_t sdio_ar6000_irq(int irq, void *arg)
+ {
+-	HIF_DEVICE *device = context;
+-	A_STATUS status;
++	HIF_DEVICE *device = arg;
++	struct device *dev = HIFGetOSDevice(device);
+ 
+-	while (1) {
+-		yield();
+-		if (!gpio_get_value(IRQ_GPIO))
+-			continue;
+-		if (!atomic_add_unless(&mask, 1, 1))
+-			continue;
+-		status = htcCallbacks.dsrHandler(device->htc_handle);
+-		BUG_ON(status != A_OK);
+-	}
++	dev_dbg(dev, "sdio_ar6000_irq\n");
++	disable_irq(device->irq);
++	if (!schedule_work(&device->work))
++		dev_err(dev, "work already queued");
++	return IRQ_HANDLED;
+ }
+ 
+ 
+ void HIFAckInterrupt(HIF_DEVICE *device)
+ {
+ 	struct device *dev = HIFGetOSDevice(device);
+-	int ret;
+ 
+-	ret = atomic_dec_return(&mask);
+-	BUG_ON(ret < 0);
+-	dev_dbg(dev, "HIFAckInterrupt (%d)\n", ret);
++	dev_dbg(dev, "HIFAckInterrupt)\n");
++	enable_irq(device->irq);
+ }
+ 
+ 
+ void HIFUnMaskInterrupt(HIF_DEVICE *device)
+ {
+ 	struct device *dev = HIFGetOSDevice(device);
+-	int ret;
+ 
+-	ret = atomic_dec_return(&mask);
+-	BUG_ON(ret < 0);
+-	dev_dbg(dev, "HIFUnMaskInterrupt (%d)\n", ret);
++	dev_dbg(dev, "HIFUnMaskInterrupt)\n");
++	enable_irq(device->irq);
+ }
+ 
+ 
+ void HIFMaskInterrupt(HIF_DEVICE *device)
+ {
+ 	struct device *dev = HIFGetOSDevice(device);
+-	int ret;
+ 
+-	ret = atomic_inc_return(&mask);
+-	BUG_ON(ret > 1);
+-	dev_dbg(dev, "HIFMaskInterrupt (%d)\n", ret);
++	dev_dbg(dev, "HIFMaskInterrupt\n");
++	disable_irq(device->irq);
+ }
+ #endif
+ 
+@@ -506,7 +522,7 @@
+ 	struct device *dev = &func->dev;
+ 	int ret;
+ 	struct task_struct *task;
+-
++	u8 bus;
+ 
+ 	dev_dbg(dev, "sdio_ar6000_probe\n");
+ 	BUG_ON(!htcCallbacks.deviceInsertedHandler);
+@@ -524,11 +540,46 @@
+ 		dev_err(dev, "sdio_set_block_size returns %d\n", ret);
+ 		/* @@@ cleanup */
+ 	}
++#if 0
+ 	ret = sdio_claim_irq(func, sdio_ar6000_irq);
+ 	if (ret) {
+ 		dev_err(dev, "sdio_claim_irq returns %d\n", ret);
+ 		/* @@@ cleanup */
+ 	}
++#else
++	hif_device.irq = IRQ_EINT3;
++	INIT_WORK(&hif_device.work, sdio_ar6000_work);
++	ret = request_irq(hif_device.irq, sdio_ar6000_irq,
++	    IRQF_TRIGGER_LOW, "ar6000", &hif_device);
++	if (ret) {
++		dev_err(dev, "request_irq returns %d\n", ret);
++		/* @@@ cleanup */
++	}
++	/* driver wants to be in charge for enabling the interrupt */
++	disable_irq(hif_device.irq);
++	bus = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
++	if (ret) {
++		dev_err(dev, "sdio_f0_readb(SDIO_CCCR_IF) returns %d\n",
++		    ret);
++		/* @@@ cleanup */
++	}
++	printk(KERN_INFO "bus 0x%02x\n", bus);
++	/* try to set ECSI. Note: SCSI is 0, so this probably has no effect. */
++	bus |= 0x20;
++	sdio_f0_writeb(func, bus, SDIO_CCCR_IF, &ret);
++	if (ret) {
++		dev_err(dev, "sdio_f0_writeb(SDIO_CCCR_IF) returns %d\n",
++		    ret);
++		/* @@@ cleanup */
++	}
++	sdio_f0_writeb(func, 3, SDIO_CCCR_IENx, &ret);
++	if (ret) {
++		dev_err(dev, "sdio_f0_writeb(SDIO_CCCR_IENx) returns %d\n",
++		    ret);
++		/* @@@ cleanup */
++	}
++#endif
++
+ #if 0 /* only for hw SDIO */
+ 	sdio_f0_writeb(func, SDIO_BUS_CD_DISABLE | SDIO_BUS_WIDTH_4BIT,
+ 	    SDIO_CCCR_IF, &ret);
+Index: korig/drivers/spi/spi_bitbang.c
+===================================================================
+--- korig.orig/drivers/spi/spi_bitbang.c	2008-10-01 09:18:09.000000000 -0300
++++ korig/drivers/spi/spi_bitbang.c	2008-10-01 09:34:27.000000000 -0300
+@@ -323,6 +323,9 @@
+ 			 * selected ...)
+ 			 */
+ 			if (cs_change) {
++				/* @@@ AR6k SPI hack */
++				bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
++				ndelay(nsecs);
+ 				bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
+ 				ndelay(nsecs);
+ 			}
+@@ -380,7 +383,8 @@
+ 		 * cs_change has hinted that the next message will probably
+ 		 * be for this chip too.
+ 		 */
+-		if (!(status == 0 && cs_change)) {
++		/* @@@ AR6k SPI hack */
++		if (0&&!(status == 0 && cs_change)) {
+ 			ndelay(nsecs);
+ 			bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+ 			ndelay(nsecs);

Modified: developers/werner/wlan-spi/patches/series
===================================================================
--- developers/werner/wlan-spi/patches/series	2008-10-01 07:03:22 UTC (rev 4685)
+++ developers/werner/wlan-spi/patches/series	2008-10-01 13:44:28 UTC (rev 4686)
@@ -27,5 +27,9 @@
 hack-silence-battery.patch
 s3c-spi-fix-int-mode.patch
 s3c-spi-from-int-to-poll.patch
-#dont-poll-irq.patch
-#try
+
+# experimental hacks follow
+
+dont-poll-irq.patch
+hif-direct-interrupt.patch
+hif-can-do-async.patch




More information about the commitlog mailing list