Andy Green wrote:
> Svn for kernel development was just resting, pining for the fjords...

This quilt patch set in SVN saved my hide more than once when an
attempt to pull the latest stuff from git ended with a merge
conflict on files I hadn't even touched :-)

> Could you post the patchset on the list?

I've attached it. (That's minus the experimental stuff that doesn't
work yet.)

Also note that the patch set isn't fully bisectable.

- Werner
#
# This patch stack is for the Openmoko stable-tracking branch. Before applying
# the patches, you need to remove the Atheros SDIO stack as follows:
#
# cd drivers
# rm -rf ar6000
# mv sdio/function/wlan/ar6000 .
# rm -rf sdio ../include/linux/sdio
#
# Alternative procedure:
#
# rm -rf drivers/ar6000         # in case this operation is repeated
# git mv drivers/sdio/function/wlan/ar6000 drivers/ar6000
# git rm -r drivers/sdio include/linux/sdio
# git commit -m "Remove Atheros SDIO stack."
# rm -rf drivers/sdio
#

ar6k-without-sdio.patch
gta02-remove-sdio.patch
sdio-add-atheros-ar6k.patch
hif-linux-sdio.patch

gta02-mmc-mci.patch

# dirty experimental stuff follows

#hif-can-do-async.patch

# still needs a bit more love ...
#s3c-mmc-sdio-int.patch
ar6k-without-sdio.patch

Make the AR6000 WLAN driver compile after moving it outside the
Atheros SDIO stack. Note that the config option's name changes
as well.

The choice of a non-standard location (drivers/ar6000/) is
intentional. The driver is still very far from being in shape for
mainline inclusion, and the odd location should serve as an
immediate warning.

Not-Yet-Signed-off-by: Werner Almesberger <[EMAIL PROTECTED]>

Index: ktrack/arch/arm/Kconfig
===================================================================
--- ktrack.orig/arch/arm/Kconfig	2008-11-02 02:50:01.000000000 -0200
+++ ktrack/arch/arm/Kconfig	2008-11-02 04:29:20.000000000 -0200
@@ -1269,7 +1269,7 @@
 
 source "drivers/uwb/Kconfig"
 
-source "drivers/sdio/Kconfig"
+source "drivers/ar6000/Kconfig"
 
 source "drivers/mmc/Kconfig"
 
Index: ktrack/drivers/Makefile
===================================================================
--- ktrack.orig/drivers/Makefile	2008-11-02 02:50:02.000000000 -0200
+++ ktrack/drivers/Makefile	2008-11-02 04:29:20.000000000 -0200
@@ -87,6 +87,7 @@
 obj-$(CONFIG_MMC)		+= mmc/
 obj-$(CONFIG_MEMSTICK)		+= memstick/
 obj-$(CONFIG_SDIO)              += sdio/
+obj-$(CONFIG_AR6000_WLAN)	+= ar6000/
 obj-$(CONFIG_NEW_LEDS)		+= leds/
 obj-$(CONFIG_INFINIBAND)	+= infiniband/
 obj-$(CONFIG_SGI_SN)		+= sn/
Index: ktrack/drivers/ar6000/Kconfig
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ ktrack/drivers/ar6000/Kconfig	2008-11-02 04:29:20.000000000 -0200
@@ -0,0 +1,7 @@
+config AR6000_WLAN
+        tristate "AR6000 wireless networking over SDIO"
+	depends on MMC
+        select WIRELESS_EXT
+        default m
+        help
+          good luck.
Index: ktrack/drivers/ar6000/Makefile
===================================================================
--- ktrack.orig/drivers/ar6000/Makefile	2008-11-02 02:50:02.000000000 -0200
+++ ktrack/drivers/ar6000/Makefile	2008-11-02 04:29:20.000000000 -0200
@@ -13,7 +13,7 @@
 
 EXTRA_CFLAGS += -DKERNEL_2_6
 
-obj-$(CONFIG_SDIO_AR6000_WLAN) += ar6000.o
+obj-$(CONFIG_AR6000_WLAN) += ar6000.o
 
 ar6000-objs += htc/ar6k.o      		   \
 	       htc/ar6k_events.o 	   \
gta02-remove-sdio.patch

After killing the Atheros SDIO stack, we shall no longer deny ourselves
the pleasure of also getting rid of the glue that keeps it on the
platform.

Not-Yet-Signed-off-by: Werner Almesberger <[EMAIL PROTECTED]>

Index: ktrack/arch/arm/mach-s3c2440/mach-gta02.c
===================================================================
--- ktrack.orig/arch/arm/mach-s3c2440/mach-gta02.c	2008-10-29 03:31:14.000000000 -0200
+++ ktrack/arch/arm/mach-s3c2440/mach-gta02.c	2008-10-29 03:33:37.000000000 -0200
@@ -876,36 +876,6 @@
 };
 
 
-
-static struct resource gta02_sdio_resources[] = {
-	[0] = {
-		.flags	= IORESOURCE_IRQ,
-		.start	= IRQ_SDI,
-		.end	= IRQ_SDI,
-	},
-	[1] = {
-		.flags = IORESOURCE_MEM,
-		.start = S3C2410_PA_SDI,
-		.end   = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
-	},
-	[2] = {
-		.flags = IORESOURCE_DMA,
-		.start = 0, /* Channel 0 for SDI */
-		.end = 0,
-	},
-};
-
-
-static struct platform_device gta02_sdio_dev = {
-        .name           = "s3c24xx-sdio",
-        .id             = -1,
-        .dev            = {
-                                .coherent_dma_mask      = 0xffffffff,
-        },
-        .resource       = gta02_sdio_resources,
-        .num_resources  = ARRAY_SIZE(gta02_sdio_resources),
-};
-
 struct platform_device s3c24xx_pwm_device = {
 	.name 		= "s3c24xx_pwm",
 	.num_resources	= 0,
@@ -1582,7 +1552,6 @@
 	&gta01_pm_gps_dev,
 	&gta01_pm_bt_dev,
 	&gta02_pm_gsm_dev,
-	&gta02_sdio_dev,
 	&gta02_pm_usbhost_dev,
 	&s3c_device_spi_acc1, /* input 2 */
 	&s3c_device_spi_acc2, /* input 3 */
Index: ktrack/include/linux/mmc/sdio_ids.h
===================================================================
--- ktrack.orig/include/linux/mmc/sdio_ids.h	2008-11-02 08:02:38.000000000 -0200
+++ ktrack/include/linux/mmc/sdio_ids.h	2008-11-02 08:02:55.000000000 -0200
@@ -25,5 +25,7 @@
 
 #define SDIO_VENDOR_ID_MARVELL			0x02df
 #define SDIO_DEVICE_ID_MARVELL_LIBERTAS		0x9103
+#define SDIO_VENDOR_ID_ATHEROS			0x0271
+#define SDIO_DEVICE_ID_ATHEROS_AR6000		0x0100
 
 #endif
hif-linux-sdio.patch

This is a replacement for Atheros' HIF layer that uses the Linux SDIO
stack.

Using GPLv2, like Atheros' code this is based on.

Work in progress.

Not-Yet-Signed-off-by: Werner Almesberger <[EMAIL PROTECTED]>

Index: ktrack/drivers/ar6000/Makefile
===================================================================
--- ktrack.orig/drivers/ar6000/Makefile	2008-11-02 23:09:13.000000000 -0200
+++ ktrack/drivers/ar6000/Makefile	2008-11-02 23:09:15.000000000 -0200
@@ -21,7 +21,7 @@
                htc/htc_recv.o       	   \
                htc/htc_services.o          \
                htc/htc.o     		   \
-               hif/hif.o     		   \
+               hif/hif2.o     		   \
                bmi/bmi.o                   \
                ar6000/ar6000_drv.o         \
                ar6000/ar6000_raw_if.o	   \
Index: ktrack/drivers/ar6000/hif/hif2.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ ktrack/drivers/ar6000/hif/hif2.c	2008-11-02 23:46:53.000000000 -0200
@@ -0,0 +1,598 @@
+/*
+ * hif2.c - HIF layer re-implementation for the Linux SDIO stack
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <[EMAIL PROTECTED]>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Based on:
+ *
+ * @abstract: HIF layer reference implementation for Atheros SDIO stack
+ * @notice: Copyright (c) 2004-2006 Atheros Communications Inc.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+#include <asm/gpio.h>
+
+#include "athdefs.h"
+#include "a_types.h"
+#include "hif.h"
+
+
+/*
+ * KNOWN BUGS:
+ *
+ * - HIF_DEVICE_IRQ_ASYNC_SYNC doesn't work yet (gets MMC errors)
+ * - driver doesn't remove cleanly yet
+ * - latency can reach hundreds of ms, probably because of scheduling delays
+ * - packets go through about three queues before finally hitting the network
+ */
+
+/*
+ * Differences from Atheros' HIFs:
+ *
+ * - synchronous and asynchronous requests may get reordered with respect to
+ *   each other, e.g., if HIFReadWrite returns for an asynchronous request and
+ *   then HIFReadWrite is called for a synchronous request, the synchronous
+ *   request may be executed before the asynchronous request.
+ *
+ * - request queue locking seems unnecessarily complex in the Atheros HIFs.
+ *
+ * - Atheros mask interrupts by calling sdio_claim_irq/sdio_release_irq, which
+ *   can cause quite a bit of overhead. This HIF has its own light-weight
+ *   interrupt masking.
+ *
+ * - Atheros call deviceInsertedHandler from a thread spawned off the probe or
+ *   device insertion function. The original explanation for the Atheros SDIO
+ *   stack said that this is done because a delay is needed to let the chip
+ *   complete initialization. There is indeed a one second delay in the thread.
+ *
+ *   The Atheros Linux SDIO HIF removes the delay and only retains the thread.
+ *   Experimentally removing the thread didn't show any conflicts, so let's get
+ *   rid of it for good.
+ *
+ * - The Atheros SDIO stack with Samuel's driver sets SDIO_CCCR_POWER in
+ *   SDIO_POWER_EMPC. Atheros' Linux SDIO code apparently doesn't. We don't
+ *   either, and this seems to work fine.
+ *   @@@ Need to check this with Atheros.
+ */
+
+
+#define MBOXES			4
+
+#define HIF_MBOX_BLOCK_SIZE	128
+#define	HIF_MBOX_BASE_ADDR	0x800
+#define	HIF_MBOX_WIDTH		0x800
+#define	HIF_MBOX_START_ADDR(mbox) \
+    (HIF_MBOX_BASE_ADDR+(mbox)*HIF_MBOX_WIDTH)
+
+
+struct hif_device {
+	void *htc_handle;
+	struct sdio_func *func;
+
+	/*
+	 * @@@ our sweet little bit of bogosity - the mechanism that lets us
+	 * use the SDIO stack from softirqs. This really wants to use skbs.
+	 */
+	struct list_head queue;
+	spinlock_t queue_lock;
+	struct task_struct *io_task;
+	wait_queue_head_t wait;
+};
+
+struct hif_request {
+	struct list_head list;
+	struct sdio_func *func;
+	int (*read)(struct sdio_func *func,
+	    void *dst, unsigned int addr, int count);
+	int (*write)(struct sdio_func *func,
+	    unsigned int addr, void *src, int count);
+	void *buf;
+	unsigned long addr;
+	int len;
+	A_STATUS (*completion)(void *context, A_STATUS status);
+	void *context;
+};
+
+
+static HTC_CALLBACKS htcCallbacks;
+
+
+/* ----- Request processing ------------------------------------------------ */
+
+
+static A_STATUS process_request(struct hif_request *req)
+{
+	int ret;
+	A_STATUS status;
+
+	dev_dbg(&req->func->dev, "process_request(req %p)\n", req);
+	sdio_claim_host(req->func);
+	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);
+	sdio_release_host(req->func);
+	status = ret ? A_ERROR : A_OK;
+	if (req->completion)
+		req->completion(req->context, status);
+	kfree(req);
+	return status;
+}
+
+
+static void enqueue_request(struct hif_device *hif, struct hif_request *req)
+{
+	unsigned long flags;
+
+	dev_dbg(&req->func->dev, "enqueue_request(req %p)\n", req);
+	spin_lock_irqsave(&hif->queue_lock, flags);
+	list_add_tail(&req->list, &hif->queue);
+	spin_unlock_irqrestore(&hif->queue_lock, flags);
+	wake_up(&hif->wait);
+}
+
+
+static struct hif_request *dequeue_request(struct hif_device *hif)
+{
+	struct hif_request *req;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hif->queue_lock, flags);
+	if (list_empty(&hif->queue))
+		req = NULL;
+	else {
+		req = list_first_entry(&hif->queue,
+		    struct hif_request, list);
+		list_del(&req->list);
+	}
+	spin_unlock_irqrestore(&hif->queue_lock, flags);
+	return req;
+}
+
+
+static void wait_queue_empty(struct hif_device *hif)
+{
+	unsigned long flags;
+	int empty;
+
+	while (1) {
+		spin_lock_irqsave(&hif->queue_lock, flags);
+		empty = list_empty(&hif->queue);
+		spin_unlock_irqrestore(&hif->queue_lock, flags);
+		if (empty)
+			break;
+		else
+			yield();
+	}
+}
+
+
+static int io(void *data)
+{
+	struct hif_device *hif = data;
+	struct sched_param param = { .sched_priority = 2 };
+		/* one priority level slower than ksdioirqd (which is at 1) */
+	DEFINE_WAIT(wait);
+	struct hif_request *req;
+
+	sched_setscheduler(current, SCHED_FIFO, &param);
+
+	while (1) {
+		while (1) {
+			/*
+			 * Since we never use signals here, one might think
+			 * that this ought to be TASK_UNINTERRUPTIBLE. However,
+			 * such a task would increase the load average and,
+			 * worse, it would trigger the softlockup check.
+			 */
+			prepare_to_wait(&hif->wait, &wait, TASK_INTERRUPTIBLE);
+			if (kthread_should_stop()) {
+				finish_wait(&hif->wait, &wait);
+				return 0;
+			}
+			req = dequeue_request(hif);
+			if (req)
+				break;
+			schedule();
+		}
+		finish_wait(&hif->wait, &wait);
+
+		(void) process_request(req);
+	}
+	return 0;
+}
+
+
+A_STATUS HIFReadWrite(HIF_DEVICE *hif, A_UINT32 address, A_UCHAR *buffer,
+    A_UINT32 length, A_UINT32 request, void *context)
+{
+	struct device *dev = HIFGetOSDevice(hif);
+	struct hif_request *req;
+
+	dev_dbg(dev, "HIFReadWrite(device %p, address 0x%x, buffer %p, "
+	    "length %d, request 0x%x, context %p)\n",
+	    hif, address, buffer, length, request, context);
+
+	BUG_ON(!(request & (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS)));
+	BUG_ON(!(request & (HIF_BYTE_BASIS | HIF_BLOCK_BASIS)));
+	BUG_ON(!(request & (HIF_READ | HIF_WRITE)));
+	BUG_ON(!(request & HIF_EXTENDED_IO));
+
+	if (address >= HIF_MBOX_START_ADDR(0) &&
+	    address < HIF_MBOX_START_ADDR(MBOXES+1)) {
+		BUG_ON(length > HIF_MBOX_WIDTH);
+		/* Adjust the address so that the last byte falls on the EOM
+		   address. */
+		address += HIF_MBOX_WIDTH-length;
+	}
+
+	req = kzalloc(sizeof(*req), GFP_ATOMIC);
+	if (!req) {
+		if (request & HIF_ASYNCHRONOUS)
+			htcCallbacks.rwCompletionHandler(context, A_ERROR);
+		return A_ERROR;
+	}
+
+	req->func = hif->func;
+	req->addr = address;
+	req->buf = buffer;
+	req->len = length;
+
+	if (request & HIF_READ) {
+		if (request & HIF_FIXED_ADDRESS)
+			req->read = sdio_readsb;
+		else
+			req->read = sdio_memcpy_fromio;
+	} else {
+		if (request & HIF_FIXED_ADDRESS)
+			req->write = sdio_writesb;
+		else
+			req->write = sdio_memcpy_toio;
+	}
+
+	if (!(request & HIF_ASYNCHRONOUS))
+		return process_request(req);
+
+	req->completion = htcCallbacks.rwCompletionHandler;
+	req->context = context;
+	enqueue_request(hif, req);
+
+	return A_OK;
+}
+
+
+/* ----- Interrupt handling ------------------------------------------------ */
+
+/*
+ * Volatile ought to be good enough to make gcc do the right thing on S3C24xx.
+ * No need to use atomic or put barriers, keeping the code more readable.
+ *
+ * Warning: this story changes if going SMP/SMT.
+ */
+
+static volatile int masked = 1;
+static volatile int pending;
+static volatile int in_interrupt;
+
+
+static void ar6000_do_irq(struct sdio_func *func)
+{
+	HIF_DEVICE *hif = sdio_get_drvdata(func);
+	struct device *dev = HIFGetOSDevice(hif);
+	A_STATUS status;
+
+	dev_dbg(dev, "ar6000_do_irq -> %p\n", htcCallbacks.dsrHandler);
+
+	status = htcCallbacks.dsrHandler(hif->htc_handle);
+	BUG_ON(status != A_OK);
+}
+
+
+static void sdio_ar6000_irq(struct sdio_func *func)
+{
+	HIF_DEVICE *hif = sdio_get_drvdata(func);
+	struct device *dev = HIFGetOSDevice(hif);
+
+	dev_dbg(dev, "sdio_ar6000_irq\n");
+
+	in_interrupt = 1;
+	if (masked) {
+		in_interrupt = 0;
+		pending++;
+		return;
+	}
+	/*
+	 * @@@ This is ugly. If we don't drop the lock, we'll deadlock when
+	 * the handler tries to do SDIO. So there are four choices:
+	 *
+	 * 1) Break the call chain by calling the callback from a workqueue.
+	 *    Ugh.
+	 * 2) Make process_request aware that we already have the lock.
+	 * 3) Drop the lock. Which is ugly but should be safe as long as we're
+	 *    making sure the device doesn't go away.
+	 * 4) Change the AR6k driver such that it only issues asynchronous
+	 *    quests when called from an interrupt.
+	 *
+	 * Solution 2) is probably the best for now. Will try it later.
+	 */
+	sdio_release_host(func);
+	ar6000_do_irq(func);
+	sdio_claim_host(func);
+	in_interrupt = 0;
+}
+
+
+void HIFAckInterrupt(HIF_DEVICE *hif)
+{
+	struct device *dev = HIFGetOSDevice(hif);
+
+	dev_dbg(dev, "HIFAckInterrupt\n");
+	/* do nothing */
+}
+
+
+void HIFUnMaskInterrupt(HIF_DEVICE *hif)
+{
+	struct device *dev = HIFGetOSDevice(hif);
+
+	dev_dbg(dev, "HIFUnMaskInterrupt\n");
+	do {
+		masked = 1;
+		if (pending) {
+			pending = 0;
+			ar6000_do_irq(hif->func);
+			/* We may take an interrupt before unmasking and thus
+			   get it pending. In this case, we just loop back. */
+		}
+		masked = 0;
+	}
+	while (pending);
+}
+
+
+void HIFMaskInterrupt(HIF_DEVICE *hif)
+{
+	struct device *dev = HIFGetOSDevice(hif);
+
+	dev_dbg(dev, "HIFMaskInterrupt\n");
+	/*
+	 * Since sdio_ar6000_irq can also be called from a process context, we
+	 * may conceivably end up racing with it. Thus, we need to wait until
+	 * we can be sure that no concurrent interrupt processing is going on
+	 * before we return.
+	 *
+	 * Note: this may be a bit on the paranoid side - the callers may
+	 * actually be nice enough to disable scheduling. Check later.
+	 */
+	masked = 1;
+	while (in_interrupt)
+		yield();
+}
+
+
+/* ----- HIF API glue functions -------------------------------------------- */
+
+
+struct device *HIFGetOSDevice(HIF_DEVICE *hif)
+{
+	return &hif->func->dev;
+}
+
+
+void HIFSetHandle(void *hif_handle, void *handle)
+{
+	HIF_DEVICE *hif = (HIF_DEVICE *) hif_handle;
+
+	hif->htc_handle = handle;
+}
+
+
+/* ----- Device configuration (HIF side) ----------------------------------- */
+
+
+A_STATUS HIFConfigureDevice(HIF_DEVICE *hif,
+    HIF_DEVICE_CONFIG_OPCODE opcode, void *config, A_UINT32 configLen)
+{
+	struct device *dev = HIFGetOSDevice(hif);
+	HIF_DEVICE_IRQ_PROCESSING_MODE *ipm_cfg = config;
+	A_UINT32 *mbs_cfg = config;
+	int i;
+
+	dev_dbg(dev, "HIFConfigureDevice\n");
+
+	switch (opcode) {
+	case HIF_DEVICE_GET_MBOX_BLOCK_SIZE:
+		for (i = 0; i != MBOXES; i++)
+			mbs_cfg[i] = HIF_MBOX_BLOCK_SIZE;
+		break;
+	case HIF_DEVICE_GET_MBOX_ADDR:
+		for (i = 0; i != MBOXES; i++)
+			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;
+		break;
+	default:
+		return A_ERROR;
+	}
+	return A_OK;
+}
+
+
+/* ----- Device probe and removal (Linux side) ----------------------------- */
+
+
+static int sdio_ar6000_probe(struct sdio_func *func,
+    const struct sdio_device_id *id)
+{
+	struct device *dev = &func->dev;
+	struct hif_device *hif;
+	int ret;
+
+	dev_dbg(dev, "sdio_ar6000_probe\n");
+	BUG_ON(!htcCallbacks.deviceInsertedHandler);
+
+	hif = kzalloc(sizeof(*hif), GFP_KERNEL);
+	if (!hif)
+		return -ENOMEM;
+
+	sdio_set_drvdata(func, hif);
+	sdio_claim_host(func);
+	sdio_enable_func(func);
+
+	hif->func = func;
+	INIT_LIST_HEAD(&hif->queue);
+	init_waitqueue_head(&hif->wait);
+	spin_lock_init(&hif->queue_lock);
+
+	ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE);
+	if (ret < 0) {
+		dev_err(dev, "sdio_set_block_size returns %d\n", ret);
+		goto out_enabled;
+	}
+	ret = sdio_claim_irq(func, sdio_ar6000_irq);
+	if (ret) {
+		dev_err(dev, "sdio_claim_irq returns %d\n", ret);
+		goto out_enabled;
+	}
+	/* Set SDIO_BUS_CD_DISABLE in SDIO_CCCR_IF ? */
+#if 0
+	sdio_f0_writeb(func, SDIO_CCCR_CAP_E4MI, SDIO_CCCR_CAPS, &ret);
+	if (ret) {
+		dev_err(dev, "sdio_f0_writeb(SDIO_CCCR_CAPS) returns %d\n",
+		    ret);
+		goto out_got_irq;
+	}
+#endif
+
+	sdio_release_host(func);
+
+	hif->io_task = kthread_run(io, hif, "ar6000_io");
+	if (IS_ERR(hif->io_task)) {
+		dev_err(dev, "kthread_run(ar6000_io): %d\n", ret);
+		goto out_func_ready;
+	}
+
+	ret = htcCallbacks.deviceInsertedHandler(hif);
+	if (ret == A_OK)
+		return 0;
+
+	dev_err(dev, "deviceInsertedHandler: %d\n", ret);
+
+	ret = kthread_stop(hif->io_task);
+	if (ret)
+		dev_err(dev, "kthread_stop (ar6000_io): %d\n", ret);
+
+out_func_ready:
+	sdio_claim_host(func);
+
+/* generates a warning */
+out_got_irq:
+	sdio_release_irq(func);
+
+out_enabled:
+	sdio_set_drvdata(func, NULL);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+
+	return ret;
+}
+
+
+static void sdio_ar6000_remove(struct sdio_func *func)
+{
+	struct device *dev = &func->dev;
+	HIF_DEVICE *hif = sdio_get_drvdata(func);
+	int ret;
+
+#if 0
+	/*
+	 * Funny, Atheros' HIF does this call, but this just puts us in a
+	 * recursion through HTCShutDown/HIFShutDown if unloading the
+	 * module.
+	 */
+	ret = htcCallbacks.deviceRemovedHandler(hif->htc_handle, A_OK);
+	if (ret != A_OK)
+		dev_err(dev, "deviceRemovedHandler: %d\n", ret);
+#endif
+	wait_queue_empty(hif);
+	ret = kthread_stop(hif->io_task);
+	if (ret)
+		dev_err(dev, "kthread_stop (ar6000_io): %d\n", ret);
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_set_drvdata(func, NULL);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+	kfree(hif);
+}
+
+
+/* ----- Device registration/unregistration (called by HIF) ---------------- */
+
+
+#define ATHEROS_SDIO_DEVICE(id, offset) \
+    SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_##id | (offset))
+
+static const struct sdio_device_id sdio_ar6000_ids[] = {
+	{ ATHEROS_SDIO_DEVICE(AR6000, 0)	},
+	{ ATHEROS_SDIO_DEVICE(AR6000, 0x1)	},
+	{ ATHEROS_SDIO_DEVICE(AR6000, 0x8)	},
+	{ ATHEROS_SDIO_DEVICE(AR6000, 0x9)	},
+	{ ATHEROS_SDIO_DEVICE(AR6000, 0xa)	},
+	{ ATHEROS_SDIO_DEVICE(AR6000, 0xb)	},
+	{ /* end: all zeroes */			},
+};
+
+MODULE_DEVICE_TABLE(sdio, sdio_ar6000_ids);
+
+
+static struct sdio_driver sdio_ar6000_driver = {
+	.probe		= sdio_ar6000_probe,
+	.remove		= sdio_ar6000_remove,
+	.name		= "sdio_ar6000",
+	.id_table	= sdio_ar6000_ids,
+};
+
+
+int HIFInit(HTC_CALLBACKS *callbacks)
+{
+	int ret;
+
+	BUG_ON(!callbacks);
+
+	printk(KERN_DEBUG "HIFInit\n");
+	htcCallbacks = *callbacks;
+
+	ret = sdio_register_driver(&sdio_ar6000_driver);
+	if (ret) {
+		printk(KERN_ERR
+		    "sdio_register_driver(sdio_ar6000_driver): %d\n", ret);
+		return A_ERROR;
+	}
+
+	return 0;
+}
+
+
+void HIFShutDownDevice(HIF_DEVICE *hif)
+{
+	/* Beware, HTCShutDown calls us with hif == NULL ! */
+	sdio_unregister_driver(&sdio_ar6000_driver);
+}
Use S3C SDI on GTA02.

Index: ktrack/arch/arm/mach-s3c2440/mach-gta02.c
===================================================================
--- ktrack.orig/arch/arm/mach-s3c2440/mach-gta02.c	2008-11-02 22:49:21.000000000 -0200
+++ ktrack/arch/arm/mach-s3c2440/mach-gta02.c	2008-11-02 22:53:59.000000000 -0200
@@ -902,12 +902,6 @@
 	.software_ecc	= 1,
 };
 
-static struct s3c24xx_mci_pdata gta02_mmc_cfg = {
-	.gpio_detect	= GTA02v1_GPIO_nSD_DETECT,
-	.set_power	= NULL,
-	.ocr_avail	= MMC_VDD_32_33,
-};
-
 static void gta02_udc_command(enum s3c2410_udc_cmd_e cmd)
 {
 	printk(KERN_DEBUG "%s(%d)\n", __func__, cmd);
@@ -1529,7 +1523,7 @@
 	&s3c_device_usb,
 	&s3c_device_wdt,
 	&gta02_memconfig_device,
-	// &s3c_device_sdi, /* FIXME: temporary disable to avoid s3cmci bind */
+	&s3c_device_sdi,
 	&s3c_device_usbgadget,
 	&s3c_device_nand,
 	&gta02_nor_flash,
@@ -1607,17 +1601,6 @@
 
 	s3c_device_usb.dev.platform_data = &gta02_usb_info;
 	s3c_device_nand.dev.platform_data = &gta02_nand_info;
-	s3c_device_sdi.dev.platform_data = &gta02_mmc_cfg;
-
-	/* Only GTA02v1 has a SD_DETECT GPIO.  Since the slot is not
-	 * hot-pluggable, this is not required anyway */
-	switch (system_rev) {
-	case GTA02v1_SYSTEM_REV:
-		break;
-	default:
-		gta02_mmc_cfg.gpio_detect = 0;
-		break;
-	}
 
 	/* acc sensor chip selects */
 	s3c2410_gpio_setpin(S3C2410_GPD12, 1);

Reply via email to