Index: chromeos/drivers/ath6kl/hif/sdio/linux_sdio/src/hif.c |
diff --git a/chromeos/drivers/ath6kl/hif/sdio/linux_sdio/src/hif.c b/chromeos/drivers/ath6kl/hif/sdio/linux_sdio/src/hif.c |
index fcce4fb21b24e0776563e2f6f198c164028e5ce8..c307a555936243b3c04d5498b6882e44b5a8a69c 100644 |
--- a/chromeos/drivers/ath6kl/hif/sdio/linux_sdio/src/hif.c |
+++ b/chromeos/drivers/ath6kl/hif/sdio/linux_sdio/src/hif.c |
@@ -1,15 +1,19 @@ |
//------------------------------------------------------------------------------ |
// <copyright file="hif.c" company="Atheros"> |
-// Copyright (c) 2004-2008 Atheros Corporation. All rights reserved. |
+// Copyright (c) 2004-2010 Atheros Corporation. 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; |
// |
-// Software distributed under the License is distributed on an "AS |
-// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
-// implied. See the License for the specific language governing |
-// rights and limitations under the License. |
+// Permission to use, copy, modify, and/or distribute this software for any |
+// purpose with or without fee is hereby granted, provided that the above |
+// copyright notice and this permission notice appear in all copies. |
+// |
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
// |
// |
//------------------------------------------------------------------------------ |
@@ -24,6 +28,7 @@ |
#include <linux/mmc/sdio_func.h> |
#include <linux/mmc/sdio_ids.h> |
#include <linux/mmc/sdio.h> |
+#include <linux/mmc/sd.h> |
#include <linux/kthread.h> |
/* by default setup a bounce buffer for the data packets, if the underlying host controller driver |
@@ -39,13 +44,13 @@ |
* buffer is DMA'able and will bug-check otherwise (i.e. buffers on the stack). |
* virt_addr_valid check fails on stack memory. |
*/ |
-#define BUFFER_NEEDS_BOUNCE(buffer) (((A_UINT32)(buffer) & 0x3) || !virt_addr_valid((buffer))) |
+#define BUFFER_NEEDS_BOUNCE(buffer) (((unsigned long)(buffer) & 0x3) || !virt_addr_valid((buffer))) |
#else |
#define BUFFER_NEEDS_BOUNCE(buffer) (FALSE) |
#endif |
/* ATHENV */ |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) && defined(CONFIG_PM) |
+#if defined(CONFIG_PM) |
#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) |
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) |
static int hifDeviceSuspend(struct device *dev); |
@@ -57,6 +62,7 @@ static HIF_DEVICE *addHifDevice(struct sdio_func *func); |
static HIF_DEVICE *getHifDevice(struct sdio_func *func); |
static void delHifDevice(HIF_DEVICE * device); |
static int Func0_CMD52WriteByte(struct mmc_card *card, unsigned int address, unsigned char byte); |
+static int Func0_CMD52ReadByte(struct mmc_card *card, unsigned int address, unsigned char *byte); |
int reset_sdio_on_unload = 0; |
module_param(reset_sdio_on_unload, int, 0644); |
@@ -81,16 +87,12 @@ static struct sdio_driver ar6k_driver = { |
.remove = hifDeviceRemoved, |
}; |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) && defined(CONFIG_PM) |
+#if defined(CONFIG_PM) |
/* New suspend/resume based on linux-2.6.32 |
* Need to patch linux-2.6.32 with mmc2.6.32_suspend.patch |
* Need to patch with msmsdcc2.6.29_suspend.patch for msm_sdcc host |
*/ |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) |
static struct dev_pm_ops ar6k_device_pm_ops = { |
-#else |
-static struct pm_ops ar6k_device_pm_ops = { |
-#endif |
.suspend = hifDeviceSuspend, |
.resume = hifDeviceResume, |
}; |
@@ -105,6 +107,8 @@ extern A_UINT32 busspeedlow; |
extern A_UINT32 debughif; |
static void ResetAllCards(void); |
+static A_STATUS hifDisableFunc(HIF_DEVICE *device, struct sdio_func *func); |
+static A_STATUS hifEnableFunc(HIF_DEVICE *device, struct sdio_func *func); |
#ifdef DEBUG |
@@ -132,7 +136,7 @@ A_STATUS HIFInit(OSDRV_CALLBACKS *callbacks) |
/* Register with bus driver core */ |
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: HIFInit registering\n")); |
registered = 1; |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) && defined(CONFIG_PM) |
+#if defined(CONFIG_PM) |
if (callbacks->deviceSuspendHandler && callbacks->deviceResumeHandler) { |
ar6k_driver.drv.pm = &ar6k_device_pm_ops; |
} |
@@ -363,7 +367,7 @@ HIFReadWrite(HIF_DEVICE *device, |
AddToAsyncList(device, busrequest); |
if (request & HIF_SYNCHRONOUS) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: queued sync req: 0x%X\n", (unsigned int)busrequest)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: queued sync req: 0x%lX\n", (unsigned long)busrequest)); |
/* wait for completion */ |
up(&device->sem_async); |
@@ -372,14 +376,14 @@ HIFReadWrite(HIF_DEVICE *device, |
return A_ERROR; |
} else { |
A_STATUS status = busrequest->status; |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: sync return freeing 0x%X: 0x%X\n", |
- (unsigned int)busrequest, busrequest->status)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: sync return freeing 0x%lX: 0x%X\n", |
+ (unsigned long)busrequest, busrequest->status)); |
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: freeing req: 0x%X\n", (unsigned int)request)); |
hifFreeBusRequest(device, busrequest); |
return status; |
} |
} else { |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: queued async req: 0x%X\n", (unsigned int)busrequest)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: queued async req: 0x%lX\n", (unsigned long)busrequest)); |
up(&device->sem_async); |
return A_PENDING; |
} |
@@ -427,7 +431,7 @@ static int async_task(void *param) |
device->asyncreq = NULL; |
} |
spin_unlock_irqrestore(&device->asynclock, flags); |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task processing req: 0x%X\n", (unsigned int)request)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task processing req: 0x%lX\n", (unsigned long)request)); |
if (request->pScatterReq != NULL) { |
A_ASSERT(device->scatter_enabled); |
@@ -441,12 +445,12 @@ static int async_task(void *param) |
request->length, request->request & ~HIF_SYNCHRONOUS, NULL); |
if (request->request & HIF_ASYNCHRONOUS) { |
void *context = request->context; |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task freeing req: 0x%X\n", (unsigned int)request)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task freeing req: 0x%lX\n", (unsigned long)request)); |
hifFreeBusRequest(device, request); |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task completion routine req: 0x%X\n", (unsigned int)request)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task completion routine req: 0x%lX\n", (unsigned long)request)); |
device->htcCallbacks.rwCompletionHandler(context, status); |
} else { |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task upping req: 0x%X\n", (unsigned int)request)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task upping req: 0x%lX\n", (unsigned long)request)); |
request->status = status; |
up(&request->sem_req); |
} |
@@ -459,14 +463,239 @@ static int async_task(void *param) |
complete_and_exit(&device->async_completion, 0); |
return 0; |
- } |
+} |
+ |
+static A_INT32 IssueSDCommand(HIF_DEVICE *device, A_UINT32 opcode, A_UINT32 arg, A_UINT32 flags, A_UINT32 *resp) |
+{ |
+ struct mmc_command cmd; |
+ A_INT32 err; |
+ struct mmc_host *host; |
+ struct sdio_func *func; |
+ |
+ func = device->func; |
+ host = func->card->host; |
+ |
+ memset(&cmd, 0, sizeof(struct mmc_command)); |
+ cmd.opcode = opcode; |
+ cmd.arg = arg; |
+ cmd.flags = flags; |
+ err = mmc_wait_for_cmd(host, &cmd, 3); |
+ |
+ if ((!err) && (resp)) { |
+ *resp = cmd.resp[0]; |
+ } |
+ |
+ return err; |
+} |
+ |
+A_STATUS ReinitSDIO(HIF_DEVICE *device) |
+{ |
+ A_INT32 err; |
+ struct mmc_host *host; |
+ struct mmc_card *card; |
+ struct sdio_func *func; |
+ A_UINT8 cmd52_resp; |
+ A_UINT32 clock; |
+ |
+ func = device->func; |
+ card = func->card; |
+ host = card->host; |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +ReinitSDIO \n")); |
+ sdio_claim_host(func); |
+ |
+ do { |
+ if (!device->is_suspend) { |
+ A_UINT32 resp; |
+ A_UINT16 rca; |
+ A_UINT32 i; |
+ int bit = fls(host->ocr_avail) - 1; |
+ /* emulate the mmc_power_up(...) */ |
+ host->ios.vdd = bit; |
+ host->ios.chip_select = MMC_CS_DONTCARE; |
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; |
+ host->ios.power_mode = MMC_POWER_UP; |
+ host->ios.bus_width = MMC_BUS_WIDTH_1; |
+ host->ios.timing = MMC_TIMING_LEGACY; |
+ host->ops->set_ios(host, &host->ios); |
+ /* |
+ * This delay should be sufficient to allow the power supply |
+ * to reach the minimum voltage. |
+ */ |
+ msleep(2); |
+ |
+ host->ios.clock = host->f_min; |
+ host->ios.power_mode = MMC_POWER_ON; |
+ host->ops->set_ios(host, &host->ios); |
+ |
+ /* |
+ * This delay must be at least 74 clock sizes, or 1 ms, or the |
+ * time required to reach a stable voltage. |
+ */ |
+ msleep(2); |
+ |
+ /* Issue CMD0. Goto idle state */ |
+ host->ios.chip_select = MMC_CS_HIGH; |
+ host->ops->set_ios(host, &host->ios); |
+ msleep(1); |
+ err = IssueSDCommand(device, MMC_GO_IDLE_STATE, 0, (MMC_RSP_NONE | MMC_CMD_BC), NULL); |
+ host->ios.chip_select = MMC_CS_DONTCARE; |
+ host->ops->set_ios(host, &host->ios); |
+ msleep(1); |
+ host->use_spi_crc = 0; |
+ |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD0 failed : %d \n",err)); |
+ break; |
+ } |
+ |
+ if (!host->ocr) { |
+ /* Issue CMD5, arg = 0 */ |
+ err = IssueSDCommand(device, SD_IO_SEND_OP_COND, 0, (MMC_RSP_R4 | MMC_CMD_BCR), &resp); |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD5 failed : %d \n",err)); |
+ break; |
+ } |
+ host->ocr = resp; |
+ } |
+ |
+ /* Issue CMD5, arg = ocr. Wait till card is ready */ |
+ for (i=0;i<100;i++) { |
+ err = IssueSDCommand(device, SD_IO_SEND_OP_COND, host->ocr, (MMC_RSP_R4 | MMC_CMD_BCR), &resp); |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD5 failed : %d \n",err)); |
+ break; |
+ } |
+ if (resp & MMC_CARD_BUSY) { |
+ break; |
+ } |
+ msleep(10); |
+ } |
+ |
+ if ((i == 100) || (err)) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: card in not ready : %d %d \n",i,err)); |
+ break; |
+ } |
+ |
+ /* Issue CMD3, get RCA */ |
+ err = IssueSDCommand(device, SD_SEND_RELATIVE_ADDR, 0, MMC_RSP_R6 | MMC_CMD_BCR, &resp); |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD3 failed : %d \n",err)); |
+ break; |
+ } |
+ rca = resp >> 16; |
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; |
+ host->ops->set_ios(host, &host->ios); |
+ |
+ /* Issue CMD7, select card */ |
+ err = IssueSDCommand(device, MMC_SELECT_CARD, (rca << 16), MMC_RSP_R1 | MMC_CMD_AC, NULL); |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD7 failed : %d \n",err)); |
+ break; |
+ } |
+ } |
+ |
+ /* Enable high speed */ |
+ if (card->host->caps & MMC_CAP_SD_HIGHSPEED) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("ReinitSDIO: Set high speed mode\n")); |
+ err = Func0_CMD52ReadByte(card, SDIO_CCCR_SPEED, &cmd52_resp); |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD52 read to CCCR speed register failed : %d \n",err)); |
+ card->state &= ~MMC_STATE_HIGHSPEED; |
+ /* no need to break */ |
+ } else { |
+ err = Func0_CMD52WriteByte(card, SDIO_CCCR_SPEED, (cmd52_resp | SDIO_SPEED_EHS)); |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD52 write to CCCR speed register failed : %d \n",err)); |
+ break; |
+ } |
+ mmc_card_set_highspeed(card); |
+ host->ios.timing = MMC_TIMING_SD_HS; |
+ host->ops->set_ios(host, &host->ios); |
+ } |
+ } |
+ |
+ /* Set clock */ |
+ if (mmc_card_highspeed(card)) { |
+ clock = 50000000; |
+ } else { |
+ clock = card->cis.max_dtr; |
+ } |
+ |
+ if (clock > host->f_max) { |
+ clock = host->f_max; |
+ } |
+ host->ios.clock = clock; |
+ host->ops->set_ios(host, &host->ios); |
+ |
+ |
+ if (card->host->caps & MMC_CAP_4_BIT_DATA) { |
+ /* CMD52: Set bus width & disable card detect resistor */ |
+ err = Func0_CMD52WriteByte(card, SDIO_CCCR_IF, SDIO_BUS_CD_DISABLE | SDIO_BUS_WIDTH_4BIT); |
+ if (err) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD52 to set bus mode failed : %d \n",err)); |
+ break; |
+ } |
+ host->ios.bus_width = MMC_BUS_WIDTH_4; |
+ host->ops->set_ios(host, &host->ios); |
+ } |
+ } while (0); |
+ |
+ sdio_release_host(func); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -ReinitSDIO \n")); |
+ |
+ return (err) ? A_ERROR : A_OK; |
+} |
+ |
+A_STATUS |
+PowerStateChangeNotify(HIF_DEVICE *device, HIF_DEVICE_POWER_CHANGE_TYPE config) |
+{ |
+ A_STATUS status = A_OK; |
+#if defined(CONFIG_PM) |
+ struct sdio_func *func = device->func; |
+ int old_reset_val; |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +PowerStateChangeNotify %d\n", config)); |
+ switch (config) { |
+ case HIF_DEVICE_POWER_DOWN: |
+ case HIF_DEVICE_POWER_CUT: |
+ old_reset_val = reset_sdio_on_unload; |
+ reset_sdio_on_unload = 1; |
+ status = hifDisableFunc(device, func); |
+ reset_sdio_on_unload = old_reset_val; |
+ if (!device->is_suspend) { |
+ struct mmc_host *host = func->card->host; |
+ host->ios.clock = 0; |
+ host->ios.vdd = 0; |
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; |
+ host->ios.chip_select = MMC_CS_DONTCARE; |
+ host->ios.power_mode = MMC_POWER_OFF; |
+ host->ios.bus_width = MMC_BUS_WIDTH_1; |
+ host->ios.timing = MMC_TIMING_LEGACY; |
+ host->ops->set_ios(host, &host->ios); |
+ } |
+ break; |
+ case HIF_DEVICE_POWER_UP: |
+ if (device->powerConfig == HIF_DEVICE_POWER_CUT) { |
+ status = ReinitSDIO(device); |
+ } |
+ if (status == A_OK) { |
+ status = hifEnableFunc(device, func); |
+ } |
+ break; |
+ } |
+ device->powerConfig = config; |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -PowerStateChangeNotify\n")); |
+#endif |
+ return status; |
+} |
A_STATUS |
HIFConfigureDevice(HIF_DEVICE *device, HIF_DEVICE_CONFIG_OPCODE opcode, |
void *config, A_UINT32 configLen) |
{ |
A_UINT32 count; |
- A_STATUS status; |
+ A_STATUS status = A_OK; |
switch(opcode) { |
case HIF_DEVICE_GET_MBOX_BLOCK_SIZE: |
@@ -498,18 +727,21 @@ HIFConfigureDevice(HIF_DEVICE *device, HIF_DEVICE_CONFIG_OPCODE opcode, |
if (A_FAILED(status)) { |
device->scatter_enabled = FALSE; |
} |
- return status; |
+ break; |
case HIF_DEVICE_GET_OS_DEVICE: |
/* pass back a pointer to the SDIO function's "dev" struct */ |
((HIF_DEVICE_OS_DEVICE_INFO *)config)->pOSDevice = &device->func->dev; |
break; |
+ case HIF_DEVICE_POWER_STATE_CHANGE: |
+ status = PowerStateChangeNotify(device, *(HIF_DEVICE_POWER_CHANGE_TYPE *)config); |
+ break; |
default: |
AR_DEBUG_PRINTF(ATH_DEBUG_WARN, |
("AR6000: Unsupported configuration opcode: %d\n", opcode)); |
- return A_ERROR; |
+ status = A_ERROR; |
} |
- return A_OK; |
+ return status; |
} |
void |
@@ -571,28 +803,31 @@ static int startup_task(void *param) |
return 0; |
} |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) && defined(CONFIG_PM) |
-/* handle HTC startup via thread*/ |
-static int resume_task(void *param) |
+#if defined(CONFIG_PM) |
+static int enable_task(void *param) |
{ |
HIF_DEVICE *device; |
device = (HIF_DEVICE *)param; |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: call HTC from resume_task\n")); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: call from resume_task\n")); |
+ |
/* start up inform DRV layer */ |
- if (device && device->claimedContext && osdrvCallbacks.deviceResumeHandler && |
- osdrvCallbacks.deviceResumeHandler(device->claimedContext) != A_OK) { |
+ if (device && |
+ device->claimedContext && |
+ osdrvCallbacks.devicePowerChangeHandler && |
+ osdrvCallbacks.devicePowerChangeHandler(device->claimedContext, HIF_DEVICE_POWER_UP) != A_OK) |
+ { |
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Device rejected\n")); |
} |
+ |
return 0; |
} |
-#endif /* CONFIG_PM */ |
+#endif |
static int hifDeviceInserted(struct sdio_func *func, const struct sdio_device_id *id) |
{ |
int ret; |
HIF_DEVICE * device; |
int count; |
- struct task_struct* startup_task_struct; |
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, |
("AR6000: hifDeviceInserted, Function: 0x%X, Vendor ID: 0x%X, Device ID: 0x%X, block size: 0x%X/0x%X\n", |
@@ -601,6 +836,9 @@ static int hifDeviceInserted(struct sdio_func *func, const struct sdio_device_id |
addHifDevice(func); |
device = getHifDevice(func); |
+ device->id = id; |
+ device->is_disabled = TRUE; |
+ |
spin_lock_init(&device->lock); |
spin_lock_init(&device->asynclock); |
@@ -612,75 +850,17 @@ static int hifDeviceInserted(struct sdio_func *func, const struct sdio_device_id |
* unless globally overridden */ |
device->scatter_enabled = TRUE; |
} |
- |
- /* enable the SDIO function */ |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: claim\n")); |
- sdio_claim_host(func); |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: enable\n")); |
- if ((id->device & MANUFACTURER_ID_AR6K_BASE_MASK) >= MANUFACTURER_ID_AR6003_BASE) { |
- /* enable 4-bit ASYNC interrupt on AR6003 or later devices */ |
- ret = Func0_CMD52WriteByte(func->card, CCCR_SDIO_IRQ_MODE_REG, SDIO_IRQ_MODE_ASYNC_4BIT_IRQ); |
- if (ret) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AR6000: failed to enable 4-bit ASYNC IRQ mode %d \n",ret)); |
- sdio_release_host(func); |
- return ret; |
- } |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: 4-bit ASYNC IRQ mode enabled\n")); |
- } |
- |
- |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
- /* give us some time to enable, in ms */ |
- func->enable_timeout = 100; |
-#endif |
- ret = sdio_enable_func(func); |
- if (ret) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), Unable to enable AR6K: 0x%X\n", |
- __FUNCTION__, ret)); |
- sdio_release_host(func); |
- return ret; |
- } |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: set block size 0x%X\n", HIF_MBOX_BLOCK_SIZE)); |
- ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE); |
- sdio_release_host(func); |
- if (ret) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), Unable to set block size 0x%x AR6K: 0x%X\n", |
- __FUNCTION__, HIF_MBOX_BLOCK_SIZE, ret)); |
- return ret; |
- } |
/* Initialize the bus requests to be used later */ |
A_MEMZERO(device->busRequest, sizeof(device->busRequest)); |
for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) { |
sema_init(&device->busRequest[count].sem_req, 0); |
hifFreeBusRequest(device, &device->busRequest[count]); |
} |
- |
- /* create async I/O thread */ |
- device->async_shutdown = 0; |
- device->async_task = kthread_create(async_task, |
- (void *)device, |
- "AR6K Async"); |
- if (IS_ERR(device->async_task)) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), to create async task\n", __FUNCTION__)); |
- return A_ERROR; |
- } |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: start async task\n")); |
sema_init(&device->sem_async, 0); |
- wake_up_process(device->async_task ); |
- |
- /* create startup thread */ |
- startup_task_struct = kthread_create(startup_task, |
- (void *)device, |
- "AR6K startup"); |
- if (IS_ERR(startup_task_struct)) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), to create startup task\n", __FUNCTION__)); |
- return A_ERROR; |
- } |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: start startup task\n")); |
- wake_up_process(startup_task_struct); |
+ |
+ ret = hifEnableFunc(device, func); |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: return %d\n", ret)); |
return ret; |
} |
@@ -743,7 +923,6 @@ BUS_REQUEST *hifAllocateBusRequest(HIF_DEVICE *device) |
{ |
device->s_busRequestFreeQueue = busrequest->next; |
} |
- |
/* Release lock */ |
spin_unlock_irqrestore(&device->lock, flag); |
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: hifAllocateBusRequest: 0x%p\n", busrequest)); |
@@ -770,11 +949,12 @@ hifFreeBusRequest(HIF_DEVICE *device, BUS_REQUEST *busrequest) |
spin_unlock_irqrestore(&device->lock, flag); |
} |
-static int hifDisableFunc(HIF_DEVICE *device, struct sdio_func *func) |
+static A_STATUS hifDisableFunc(HIF_DEVICE *device, struct sdio_func *func) |
{ |
- int ret = 0; |
+ int ret; |
+ A_STATUS status = A_OK; |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifDeviceRemoved\n")); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifDisableFunc\n")); |
device = getHifDevice(func); |
if (!IS_ERR(device->async_task)) { |
init_completion(&device->async_completion); |
@@ -786,6 +966,9 @@ static int hifDisableFunc(HIF_DEVICE *device, struct sdio_func *func) |
/* Disable the card */ |
sdio_claim_host(device->func); |
ret = sdio_disable_func(device->func); |
+ if (ret) { |
+ status = A_ERROR; |
+ } |
if (reset_sdio_on_unload) { |
/* reset the SDIO interface. This is useful in automated testing where the card |
@@ -798,121 +981,62 @@ static int hifDisableFunc(HIF_DEVICE *device, struct sdio_func *func) |
ret = Func0_CMD52WriteByte(device->func->card, SDIO_CCCR_ABORT, (1 << 3)); |
if (ret) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AR6000: reset failed : %d \n",ret)); |
+ status = A_ERROR; |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AR6000: reset failed : %d \n",ret)); |
} |
} |
sdio_release_host(device->func); |
- return ret; |
-} |
- |
-static void hifDeviceRemoved(struct sdio_func *func) |
-{ |
- A_STATUS status = A_OK; |
- HIF_DEVICE *device; |
- AR_DEBUG_ASSERT(func != NULL); |
- |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifDeviceRemoved\n")); |
- device = getHifDevice(func); |
- if (device->claimedContext != NULL) { |
- status = osdrvCallbacks.deviceRemovedHandler(device->claimedContext, device); |
- } |
- |
- if (device->is_suspend) { |
- device->is_suspend = FALSE; |
- } else { |
- if (hifDisableFunc(device, func)!=0) { |
- status = A_ERROR; |
- } |
- } |
- CleanupHIFScatterResources(device); |
- |
- delHifDevice(device); |
- AR_DEBUG_ASSERT(status == A_OK); |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifDeviceRemoved\n")); |
-} |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) && defined(CONFIG_PM) |
-static int hifDeviceSuspend(struct device *dev) |
-{ |
- struct sdio_func *func = dev_to_sdio_func(dev); |
- A_STATUS status = A_OK; |
- HIF_DEVICE *device; |
- device = getHifDevice(func); |
- if (device && device->claimedContext && osdrvCallbacks.deviceSuspendHandler) { |
- status = osdrvCallbacks.deviceSuspendHandler(device->claimedContext); |
- } |
if (status == A_OK) { |
- int oldresetvalue = reset_sdio_on_unload; |
- reset_sdio_on_unload = 1; |
- hifDisableFunc(device, func); |
- reset_sdio_on_unload = oldresetvalue; |
- device->is_suspend = TRUE; |
- } else if (status == A_EBUSY) { |
- A_INT32 cnt = 10; |
- A_UINT8 host_int_status; |
- do { |
- while (atomic_read(&device->irqHandling)) { |
- /* wait until irq handler finished all the jobs */ |
- schedule_timeout(HZ/10); |
- } |
- /* check if there is any pending irq due to force done */ |
- host_int_status = 0; |
- status = HIFReadWrite(device, HOST_INT_STATUS_ADDRESS, |
- (A_UINT8 *)&host_int_status, sizeof(host_int_status), |
- HIF_RD_SYNC_BYTE_INC, NULL); |
- host_int_status = A_SUCCESS(status) ? (host_int_status & (1 << 0)) : 0; |
- if (host_int_status) { |
- schedule(); /* schedule for next dsrHandler */ |
- } |
- } while (host_int_status && --cnt > 0); |
- |
- if (host_int_status && cnt == 0) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
- ("AR6000: %s(), Unable clear up pending IRQ before the system suspended\n", |
- __FUNCTION__)); |
- } |
-#if 1 |
- status = A_OK; /* assume that sdio host controller will take care the power of wifi chip */ |
-#else |
- return -EBUSY; /* Return -EBUSY if customer use all android patch of mmc stack provided by us */ |
-#endif |
+ device->is_disabled = TRUE; |
} |
- return A_SUCCESS(status) ? 0 : status; |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifDisableFunc\n")); |
+ |
+ return status; |
} |
-static int hifDeviceResume(struct device *dev) |
+static int hifEnableFunc(HIF_DEVICE *device, struct sdio_func *func) |
{ |
struct task_struct* pTask; |
- const char *taskName; |
- int (*taskFunc)(void *); |
- struct sdio_func *func = dev_to_sdio_func(dev); |
- A_STATUS ret = A_OK; |
- HIF_DEVICE *device; |
+ const char *taskName = NULL; |
+ int (*taskFunc)(void *) = NULL; |
+ int ret = A_OK; |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifEnableFunc\n")); |
device = getHifDevice(func); |
- if (device->is_suspend) { |
+ if (device->is_disabled) { |
/* enable the SDIO function */ |
sdio_claim_host(func); |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
+ |
+ if ((device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK) >= MANUFACTURER_ID_AR6003_BASE) { |
+ /* enable 4-bit ASYNC interrupt on AR6003 or later devices */ |
+ ret = Func0_CMD52WriteByte(func->card, CCCR_SDIO_IRQ_MODE_REG, SDIO_IRQ_MODE_ASYNC_4BIT_IRQ); |
+ if (ret) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AR6000: failed to enable 4-bit ASYNC IRQ mode %d \n",ret)); |
+ sdio_release_host(func); |
+ return A_ERROR; |
+ } |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: 4-bit ASYNC IRQ mode enabled\n")); |
+ } |
/* give us some time to enable, in ms */ |
func->enable_timeout = 100; |
-#endif |
ret = sdio_enable_func(func); |
if (ret) { |
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), Unable to enable AR6K: 0x%X\n", |
__FUNCTION__, ret)); |
sdio_release_host(func); |
- return ret; |
- } |
+ return A_ERROR; |
+ } |
ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE); |
sdio_release_host(func); |
if (ret) { |
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), Unable to set block size 0x%x AR6K: 0x%X\n", |
__FUNCTION__, HIF_MBOX_BLOCK_SIZE, ret)); |
- return ret; |
+ return A_ERROR; |
} |
- device->is_suspend = FALSE; |
+ device->is_disabled = FALSE; |
/* create async I/O thread */ |
if (!device->async_task) { |
device->async_shutdown = 0; |
@@ -929,25 +1053,135 @@ static int hifDeviceResume(struct device *dev) |
} |
if (!device->claimedContext) { |
- printk("WARNING!!! No claimedContext during resume wlan\n"); |
taskFunc = startup_task; |
taskName = "AR6K startup"; |
+ ret = A_OK; |
+#if defined(CONFIG_PM) |
} else { |
- taskFunc = resume_task; |
- taskName = "AR6K resume"; |
+ taskFunc = enable_task; |
+ taskName = "AR6K enable"; |
+ ret = A_PENDING; |
+#endif /* CONFIG_PM */ |
} |
/* create resume thread */ |
pTask = kthread_create(taskFunc, (void *)device, taskName); |
if (IS_ERR(pTask)) { |
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), to create resume task\n", __FUNCTION__)); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), to create enabel task\n", __FUNCTION__)); |
return A_ERROR; |
} |
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: start resume task\n")); |
wake_up_process(pTask); |
- return A_SUCCESS(ret) ? 0 : ret; |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifEnableFunc\n")); |
+ |
+ /* task will call the enable func, indicate pending */ |
+ return ret; |
+} |
+ |
+#if defined(CONFIG_PM) |
+static int hifDeviceSuspend(struct device *dev) |
+{ |
+ struct sdio_func *func=dev_to_sdio_func(dev); |
+ A_STATUS status = A_OK; |
+ HIF_DEVICE *device; |
+ |
+ device = getHifDevice(func); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifDeviceSuspend\n")); |
+ if (device && device->claimedContext && osdrvCallbacks.deviceSuspendHandler) { |
+ device->is_suspend = TRUE; /* set true first for PowerStateChangeNotify(..) */ |
+ status = osdrvCallbacks.deviceSuspendHandler(device->claimedContext); |
+ if (status != A_OK) { |
+ device->is_suspend = FALSE; |
+ } |
+ } |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifDeviceSuspend\n")); |
+ |
+ switch (status) { |
+ case A_OK: |
+ return 0; |
+ case A_EBUSY: |
+ return -EBUSY; /* Hack for kernel in order to support deep sleep and wow */ |
+ default: |
+ return -1; |
+ } |
+} |
+ |
+static int hifDeviceResume(struct device *dev) |
+{ |
+ struct sdio_func *func=dev_to_sdio_func(dev); |
+ A_STATUS status = A_OK; |
+ HIF_DEVICE *device; |
+ |
+ device = getHifDevice(func); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifDeviceResume\n")); |
+ if (device && device->claimedContext && osdrvCallbacks.deviceSuspendHandler) { |
+ status = osdrvCallbacks.deviceResumeHandler(device->claimedContext); |
+ if (status == A_OK) { |
+ device->is_suspend = FALSE; |
+ } |
+ } |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifDeviceResume\n")); |
+ |
+ return A_SUCCESS(status) ? 0 : status; |
} |
#endif /* CONFIG_PM */ |
+static void hifDeviceRemoved(struct sdio_func *func) |
+{ |
+ A_STATUS status = A_OK; |
+ HIF_DEVICE *device; |
+ AR_DEBUG_ASSERT(func != NULL); |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifDeviceRemoved\n")); |
+ device = getHifDevice(func); |
+ if (device->claimedContext != NULL) { |
+ status = osdrvCallbacks.deviceRemovedHandler(device->claimedContext, device); |
+ } |
+ |
+ if (device->is_disabled) { |
+ device->is_disabled = FALSE; |
+ } else { |
+ status = hifDisableFunc(device, func); |
+ } |
+ CleanupHIFScatterResources(device); |
+ |
+ delHifDevice(device); |
+ AR_DEBUG_ASSERT(status == A_OK); |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifDeviceRemoved\n")); |
+} |
+ |
+/* |
+ * This should be moved to AR6K HTC layer. |
+ */ |
+A_STATUS hifWaitForPendingRecv(HIF_DEVICE *device) |
+{ |
+ A_INT32 cnt = 10; |
+ A_UINT8 host_int_status; |
+ A_STATUS status = A_OK; |
+ |
+ do { |
+ while (atomic_read(&device->irqHandling)) { |
+ /* wait until irq handler finished all the jobs */ |
+ schedule_timeout(HZ/10); |
+ } |
+ /* check if there is any pending irq due to force done */ |
+ host_int_status = 0; |
+ status = HIFReadWrite(device, HOST_INT_STATUS_ADDRESS, |
+ (A_UINT8 *)&host_int_status, sizeof(host_int_status), |
+ HIF_RD_SYNC_BYTE_INC, NULL); |
+ host_int_status = A_SUCCESS(status) ? (host_int_status & (1 << 0)) : 0; |
+ if (host_int_status) { |
+ schedule(); /* schedule for next dsrHandler */ |
+ } |
+ } while (host_int_status && --cnt > 0); |
+ |
+ if (host_int_status && cnt == 0) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
+ ("AR6000: %s(), Unable clear up pending IRQ before the system suspended\n", __FUNCTION__)); |
+ } |
+ |
+ return A_OK; |
+} |
+ |
+ |
static HIF_DEVICE * |
addHifDevice(struct sdio_func *func) |
{ |
@@ -961,6 +1195,7 @@ addHifDevice(struct sdio_func *func) |
AR_DEBUG_ASSERT(hifdevice->dma_buffer != NULL); |
#endif |
hifdevice->func = func; |
+ hifdevice->powerConfig = HIF_DEVICE_POWER_UP; |
sdio_set_drvdata(func, hifdevice); |
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: addHifDevice; 0x%p\n", hifdevice)); |
return hifdevice; |
@@ -1041,6 +1276,23 @@ static int Func0_CMD52WriteByte(struct mmc_card *card, unsigned int address, uns |
return mmc_wait_for_cmd(card->host, &ioCmd, 0); |
} |
+static int Func0_CMD52ReadByte(struct mmc_card *card, unsigned int address, unsigned char *byte) |
+{ |
+ struct mmc_command ioCmd; |
+ unsigned long arg; |
+ A_INT32 err; |
+ |
+ memset(&ioCmd,0,sizeof(ioCmd)); |
+ SDIO_SET_CMD52_READ_ARG(arg,0,address); |
+ ioCmd.opcode = SD_IO_RW_DIRECT; |
+ ioCmd.arg = arg; |
+ ioCmd.flags = MMC_RSP_R5 | MMC_CMD_AC; |
+ err = mmc_wait_for_cmd(card->host, &ioCmd, 0); |
+ if ((!err) && (byte)) { |
+ *byte = ioCmd.resp[0] & 0xFF; |
+ } |
+ return err; |
+} |