diff -urN a/ar6000/hif/hif2.c b/ar6000/hif/hif2.c --- a/ar6000/hif/hif2.c 2009-03-25 00:23:38.000000000 +0300 +++ b/ar6000/hif/hif2.c 2009-03-30 00:08:26.000000000 +0400 @@ -25,14 +25,26 @@ #include #include #include -#include +#include +#include + +/* + * turn on GTA02 platform depened code. + * if you make driver for another, comment it. + */ +#define _GTA02_ + +#ifdef _GTA02_ #include #include +#include +#endif /* end _GTA02_ */ #include "athdefs.h" #include "a_types.h" #include "hif.h" +extern A_UINT32 onebitmode; /* * KNOWN BUGS: @@ -86,6 +98,13 @@ struct sdio_func *func; /* + * SDIO irq process handle, for prevent claim/relese in irq call. + * we must try not permit send any, not IRQ process necessary data, + * while IRQ not completed. + */ + struct task_struct *irq_task; + + /* * @@@ our sweet little bit of bogosity - the mechanism that lets us * use the SDIO stack from softirqs. This really wants to use skbs. */ @@ -109,14 +128,10 @@ 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); + u32 addr; + u32 len; + u32 flags; void *context; }; @@ -132,35 +147,95 @@ /* ----- Request processing ------------------------------------------------ */ -#include - -static A_STATUS process_request(struct hif_request *req) +static A_STATUS \ +process_request(struct sdio_func *func, A_UCHAR *buff, A_UINT32 addr, + A_UINT32 len, A_UINT32 flags) { - int ret; - A_STATUS status; + u32 blksz, blocks, arg; + struct hif_device *hif; + struct mmc_card *card; + struct mmc_host *host; + struct { + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + } req; - dev_dbg(&req->func->dev, "process_request(req %p)\n", req); - sdio_claim_host(req->func); - if (req->read) { - while (!s3c2410_gpio_getpin(S3C2410_GPE7)) { - printk(KERN_INFO "READ WHILE BUSY !\n"); - yield(); - } - ret = req->read(req->func, req->buf, req->addr, req->len); + /* + * Next code based on: 'mmc_io_rw_extended' (mmc/core/sdio_ops.c), + * but have shorten path and send small blocks not in byte mode. + */ + + memset(&req, 0, sizeof(req)); + + req.mrq.cmd = &req.cmd; + req.mrq.data = &req.data; + + req.cmd.opcode = SD_IO_RW_EXTENDED; + + arg = (flags & HIF_WRITE) ? 0x80000000 : 0x00000000; + arg |= (func->num << 28); + arg |= (flags & HIF_INCREMENTAL_ADDRESS) ? 0x04000000 : 0x00000000; + arg |= (addr << 9); + + if (flags & HIF_BYTE_BASIS) { + /* byte mode */ + blksz = len; + blocks = 1; + arg |= blksz; } else { - while (!s3c2410_gpio_getpin(S3C2410_GPE7)) { - printk(KERN_INFO "WRITE WHILE BUSY !\n"); - yield(); + /* block mode */ + blksz = HIF_MBOX_BLOCK_SIZE; + blocks = len / HIF_MBOX_BLOCK_SIZE; + arg |= blocks | 0x08000000; + } + + req.cmd.arg = arg; + req.cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + req.data.blksz = blksz; + req.data.blocks = blocks; + req.data.flags = (flags & HIF_WRITE) ? MMC_DATA_WRITE : MMC_DATA_READ; + req.data.sg = &req.sg; + req.data.sg_len = 1; + + sg_init_one(&req.sg, buff, blksz * blocks); + + card = func->card; + host = card->host; + + mmc_set_data_timeout(&req.data, card); + + hif = sdio_get_drvdata(func); + + /* prevent claim in irq */ + if (hif->irq_task != current) + sdio_claim_host(func); + +#ifdef _GTA02_ + while (!s3c2410_gpio_getpin(S3C2410_GPE7)) { + printk(KERN_INFO "READ/WRITE WHILE BUSY !\n"); + yield(); +#endif /* end _GTA02_ */ + + mmc_wait_for_req(host, &req.mrq); + + /* prevent relese in irq */ + if (hif->irq_task != current) + sdio_release_host(func); + + if ((req.cmd.error) || (req.data.error)) + return A_ERROR; + + if (!(mmc_host_is_spi(host))) { + if (req.cmd.resp[0] & (R5_ERROR|R5_FUNCTION_NUMBER|R5_OUT_OF_RANGE)) { + return A_ERROR; } - 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; + + return A_OK; } @@ -241,7 +316,18 @@ } finish_wait(&hif->wait, &wait); - (void) process_request(req); + { + A_STATUS status; + void *context; + + status = process_request(req->func, req->buf, req->addr, + req->len, req->flags); + context = req->context; + + kfree(req); + + htcCallbacks.rwCompletionHandler(context, status); + } } return 0; } @@ -270,38 +356,25 @@ 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; + if (!(request & HIF_ASYNCHRONOUS)) { + return process_request(hif->func, buffer, address, length, request); } - 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 ((req = kzalloc(sizeof(*req), GFP_ATOMIC))) { + req->func = hif->func; + req->buf = buffer; + req->addr = address; + req->len = length; + req->flags = request; + req->context = context; - if (!(request & HIF_ASYNCHRONOUS)) - return process_request(req); + enqueue_request(hif, req); - req->completion = htcCallbacks.rwCompletionHandler; - req->context = context; - enqueue_request(hif, req); + return A_OK; + } - return A_OK; + htcCallbacks.rwCompletionHandler(context, A_ERROR); + return A_ERROR; } @@ -359,9 +432,11 @@ * * Solution 2) is probably the best for now. Will try it later. */ - sdio_release_host(func); + + hif->irq_task = current; ar6000_do_irq(func); - sdio_claim_host(func); + hif->irq_task = NULL; + in_interrupt = 0; } @@ -454,8 +529,13 @@ 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; + /* + * WARN: + * HIF_DEVICE_IRQ_ASYNC_SYNC in BUG list, but I not have it problem. + * Need additional testing on different boards. + */ +// *ipm_cfg = HIF_DEVICE_IRQ_SYNC_ONLY; + *ipm_cfg = HIF_DEVICE_IRQ_ASYNC_SYNC; break; default: return A_ERROR; @@ -464,6 +544,55 @@ } +/* + * sdio_f0_writeb function allow write only to address range 0xF0...0xFF. + * this function is a copy of 'mmc_io_rw_direct' (mmc/core/sdio_ops.c) + * for leave out this restriction. + */ +static int sdio_rw_direct(struct mmc_card *card, int write, unsigned fn, + unsigned addr, u8 in, u8* out) +{ + struct mmc_command cmd; + int err; + + BUG_ON(!card); + BUG_ON(fn > 7); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_IO_RW_DIRECT; + cmd.arg = write ? 0x80000000 : 0x00000000; + cmd.arg |= fn << 28; + cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; + cmd.arg |= addr << 9; + cmd.arg |= in; + cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err) + return err; + + if (mmc_host_is_spi(card->host)) { + /* host driver already reported errors */ + } else { + if (cmd.resp[0] & R5_ERROR) + return -EIO; + if (cmd.resp[0] & R5_FUNCTION_NUMBER) + return -EINVAL; + if (cmd.resp[0] & R5_OUT_OF_RANGE) + return -ERANGE; + } + + if (out) { + if (mmc_host_is_spi(card->host)) + *out = (cmd.resp[0] >> 8) & 0xFF; + else + *out = cmd.resp[0] & 0xFF; + } + + return 0; +} + /* ----- Device probe and removal (Linux side) ----------------------------- */ @@ -476,6 +605,40 @@ dev_dbg(dev, "ar6000_do_activate\n"); sdio_claim_host(func); + + if (onebitmode) + { + struct mmc_card *card; + struct mmc_host *host; + u8 ctrl; + + /* + * Init SDIO 1-wire mode. + */ + card = func->card; + host = card->host; + + ret = sdio_rw_direct (card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); + if (ret) { + dev_err(dev, "sdio_f0_readb (SDIO_CCCR_IF) returns %d\n", + ret); + goto out_claimed; + } + + ctrl &= ~(SDIO_BUS_WIDTH_4BIT|SDIO_BUS_WIDTH_1BIT); + ctrl |= SDIO_BUS_WIDTH_1BIT; + + ret = sdio_rw_direct (card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); + if (ret) { + dev_err(dev, "sdio_f0_writeb (SDIO_CCCR_IF) returns %d\n", + ret); + goto out_claimed; + } + + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ops->set_ios(host, &host->ios); + } + sdio_enable_func(func); INIT_LIST_HEAD(&hif->queue); @@ -531,6 +694,7 @@ out_enabled: sdio_disable_func(func); +out_claimed: sdio_release_host(func); return ret; @@ -568,6 +732,40 @@ sdio_claim_host(func); sdio_release_irq(func); sdio_disable_func(func); + + if (onebitmode) + { + struct mmc_card *card; + struct mmc_host *host; + u8 ctrl; + + /* + * Release SDIO 1-wire mode, + * or init back to SDIO 4-wire. + */ + card = func->card; + host = card->host; + + ret = sdio_rw_direct (card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); + if (ret) { + dev_err(dev, "sdio_f0_readb (SDIO_CCCR_IF) returns %d\n", + ret); + ctrl = 0; + } + + ctrl &= ~(SDIO_BUS_WIDTH_4BIT|SDIO_BUS_WIDTH_1BIT); + ctrl |= SDIO_BUS_WIDTH_4BIT; + + ret = sdio_rw_direct (card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); + if (ret) { + dev_err(dev, "sdio_f0_writeb (SDIO_CCCR_IF) returns %d\n", + ret); + } + + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ops->set_ios(host, &host->ios); + } + sdio_release_host(func); } @@ -599,6 +797,7 @@ } +#ifdef _GTA02_ static int ar6000_rfkill_cb(void *data, int on) { struct hif_device *hif = data; @@ -611,6 +810,7 @@ ar6000_deactivate(hif); return 0; } +#endif /* end _GTA02_ */ static int sdio_ar6000_probe(struct sdio_func *func, @@ -632,6 +832,7 @@ mutex_init(&hif->activate_lock); hif->active = 0; +#ifdef _GTA02_ if (gta02_wlan_query_rfkill_lock()) ret = ar6000_activate(hif); if (!ret) { @@ -639,6 +840,10 @@ return 0; } gta02_wlan_query_rfkill_unlock(); +#else /* not _GTA02_ */ + if (!(ret = ar6000_activate(hif))) + return 0; +#endif /* end _GTA02_ */ sdio_set_drvdata(func, NULL); kfree(hif); return ret; @@ -651,7 +856,9 @@ HIF_DEVICE *hif = sdio_get_drvdata(func); dev_dbg(dev, "sdio_ar6000_remove\n"); +#ifdef _GTA02_ gta02_wlan_clear_rfkill_cb(); +#endif /* end _GTA02_ */ ar6000_deactivate(hif); sdio_set_drvdata(func, NULL); kfree(hif); @@ -660,18 +867,25 @@ /* ----- Device registration/unregistration (called by HIF) ---------------- */ +/* + * define vendor/device IDs. Prefferd keep it in this file, + * while it is not will added to official linux kernel. + */ +#define SDIO_VENDOR_ID_ATHEROS 0x271 +#define SDIO_DEVICE_ID_ATHEROS_AR6001 0x100 +#define SDIO_DEVICE_ID_ATHEROS_AR6002 0x200 #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 */ }, + { ATHEROS_SDIO_DEVICE(AR6002, 0x0) }, + { ATHEROS_SDIO_DEVICE(AR6002, 0x1) }, + { ATHEROS_SDIO_DEVICE(AR6001, 0x8) }, + { ATHEROS_SDIO_DEVICE(AR6001, 0x9) }, + { ATHEROS_SDIO_DEVICE(AR6001, 0xa) }, + { ATHEROS_SDIO_DEVICE(AR6001, 0xb) }, + { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, sdio_ar6000_ids);