Add ath6kl cleaned up driver

Last May we started working on cleaning up ath6kl driver which is
currently in staging. The work has happened in a separate
ath6kl-cleanup tree:

http://git.kernel.org/?p=linux/kernel/git/kvalo/ath6kl-cleanup.git;a=summary

After over 1100 (!) patches we have now reached a state where I would
like to start discussing about pushing the driver to the wireless
trees and replacing the staging driver.

The driver is now a lot smaller and looks like a proper Linux driver.
The size of the driver (measured with simple wc -l) dropped from 49
kLOC to 18 kLOC and the number of the .c and .h files dropped from 107
to 22. Most importantly the number of subdirectories reduced from 26
to zero :)

There are two remaining checkpatch warnings in the driver which we
decided to omit for now:

drivers/net/wireless/ath/ath6kl/debug.c:31:
  WARNING: printk() should include KERN_ facility level
drivers/net/wireless/ath/ath6kl/sdio.c:527:
  WARNING: msleep < 20ms can sleep for up to 20ms;
  see Documentation/timers/timers-howto.txt

The driver has endian annotations for all the hardware specific
structures and there are no sparse errors. Unfortunately I don't have
any big endian hardware to test that right now.

We have been testing the driver both on x86 and arm platforms. The
code is also compiled with sparc and parisc cross compilers.

Notable missing features compared to the current staging driver are:

o HCI over SDIO support
o nl80211 testmode
o firmware logging
o suspend support

Testmode, firmware logging and suspend support will be added soon. HCI
over SDIO support will be more difficult as the HCI driver needs to
share code with the wifi driver. This is something we need to research
more.

Also I want to point out the changes I did for signed endian support.
As I wasn't able to find any support for signed endian annotations I
decided to follow what NTFS has done and added my own. Grep for sle16
and sle32, especially from wmi.h.

Various people have been working on the cleanup, the hall of
fame based on number of patches is:

   543  Vasanthakumar Thiagarajan
   403  Raja Mani
   252  Kalle Valo
    16  Vivek Natarajan
    12  Suraj Sumangala
     3  Joe Perches
     2  Jouni Malinen

Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>
Signed-off-by: Raja Mani <rmani@qca.qualcomm.com>
Signed-off-by: Vivek Natarajan <nataraja@qca.qualcomm.com>
Signed-off-by: Suraj Sumangala <surajs@qca.qualcomm.com>
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
Kalle Valo 2011-07-18 00:22:30 +03:00
parent f749b94679
commit bdcd817079
26 changed files with 18115 additions and 0 deletions

View File

@ -25,5 +25,6 @@ config ATH_DEBUG
source "drivers/net/wireless/ath/ath5k/Kconfig"
source "drivers/net/wireless/ath/ath9k/Kconfig"
source "drivers/net/wireless/ath/carl9170/Kconfig"
source "drivers/net/wireless/ath/ath6kl/Kconfig"
endif

View File

@ -1,6 +1,7 @@
obj-$(CONFIG_ATH5K) += ath5k/
obj-$(CONFIG_ATH9K_HW) += ath9k/
obj-$(CONFIG_CARL9170) += carl9170/
obj-$(CONFIG_ATH6KL) += ath6kl/
obj-$(CONFIG_ATH_COMMON) += ath.o

View File

@ -0,0 +1,17 @@
config ATH6KL
tristate "Atheros ath6kl support"
depends on MMC
depends on CFG80211
select WIRELESS_EXT
select WEXT_PRIV
---help---
This module adds support for wireless adapters based on
Atheros AR6003 chipset running over SDIO. If you choose to
build it as a module, it will be called ath6kl. Pls note
that AR6002 and AR6001 are not supported by this driver.
config ATH6KL_DEBUG
bool "Atheros ath6kl debugging"
depends on ATH6KL
---help---
Enables debug support

View File

@ -0,0 +1,35 @@
#------------------------------------------------------------------------------
# Copyright (c) 2004-2010 Atheros Communications Inc.
# All rights reserved.
#
#
#
# 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.
#
#
#
# Author(s): ="Atheros"
#------------------------------------------------------------------------------
obj-$(CONFIG_ATH6KL) := ath6kl.o
ath6kl-y += debug.o
ath6kl-y += htc_hif.o
ath6kl-y += htc.o
ath6kl-y += bmi.o
ath6kl-y += cfg80211.o
ath6kl-y += init.o
ath6kl-y += main.o
ath6kl-y += txrx.o
ath6kl-y += wmi.o
ath6kl-y += node.o
ath6kl-y += sdio.o

View File

@ -0,0 +1,692 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#include "core.h"
#include "hif-ops.h"
#include "target.h"
#include "debug.h"
static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar)
{
u32 addr;
unsigned long timeout;
int ret;
ar->bmi.cmd_credits = 0;
/* Read the counter register to get the command credits */
addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
/*
* Hit the credit counter with a 4-byte access, the first byte
* read will hit the counter and cause a decrement, while the
* remaining 3 bytes has no effect. The rationale behind this
* is to make all HIF accesses 4-byte aligned.
*/
ret = hif_read_write_sync(ar, addr,
(u8 *)&ar->bmi.cmd_credits, 4,
HIF_RD_SYNC_BYTE_INC);
if (ret) {
ath6kl_err("Unable to decrement the command credit count register: %d\n",
ret);
return ret;
}
/* The counter is only 8 bits.
* Ignore anything in the upper 3 bytes
*/
ar->bmi.cmd_credits &= 0xFF;
}
if (!ar->bmi.cmd_credits) {
ath6kl_err("bmi communication timeout\n");
return -ETIMEDOUT;
}
return 0;
}
static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar, bool need_timeout)
{
unsigned long timeout;
u32 rx_word = 0;
int ret = 0;
timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
while ((!need_timeout || time_before(jiffies, timeout)) && !rx_word) {
ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS,
(u8 *)&rx_word, sizeof(rx_word),
HIF_RD_SYNC_BYTE_INC);
if (ret) {
ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n");
return ret;
}
/* all we really want is one bit */
rx_word &= (1 << ENDPOINT1);
}
if (!rx_word) {
ath6kl_err("bmi_recv_buf FIFO empty\n");
return -EINVAL;
}
return ret;
}
static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len)
{
int ret;
u32 addr;
ret = ath6kl_get_bmi_cmd_credits(ar);
if (ret)
return ret;
addr = ar->mbox_info.htc_addr;
ret = hif_read_write_sync(ar, addr, buf, len,
HIF_WR_SYNC_BYTE_INC);
if (ret)
ath6kl_err("unable to send the bmi data to the device\n");
return ret;
}
static int ath6kl_bmi_recv_buf(struct ath6kl *ar,
u8 *buf, u32 len, bool want_timeout)
{
int ret;
u32 addr;
/*
* During normal bootup, small reads may be required.
* Rather than issue an HIF Read and then wait as the Target
* adds successive bytes to the FIFO, we wait here until
* we know that response data is available.
*
* This allows us to cleanly timeout on an unexpected
* Target failure rather than risk problems at the HIF level.
* In particular, this avoids SDIO timeouts and possibly garbage
* data on some host controllers. And on an interconnect
* such as Compact Flash (as well as some SDIO masters) which
* does not provide any indication on data timeout, it avoids
* a potential hang or garbage response.
*
* Synchronization is more difficult for reads larger than the
* size of the MBOX FIFO (128B), because the Target is unable
* to push the 129th byte of data until AFTER the Host posts an
* HIF Read and removes some FIFO data. So for large reads the
* Host proceeds to post an HIF Read BEFORE all the data is
* actually available to read. Fortunately, large BMI reads do
* not occur in practice -- they're supported for debug/development.
*
* So Host/Target BMI synchronization is divided into these cases:
* CASE 1: length < 4
* Should not happen
*
* CASE 2: 4 <= length <= 128
* Wait for first 4 bytes to be in FIFO
* If CONSERVATIVE_BMI_READ is enabled, also wait for
* a BMI command credit, which indicates that the ENTIRE
* response is available in the the FIFO
*
* CASE 3: length > 128
* Wait for the first 4 bytes to be in FIFO
*
* For most uses, a small timeout should be sufficient and we will
* usually see a response quickly; but there may be some unusual
* (debug) cases of BMI_EXECUTE where we want an larger timeout.
* For now, we use an unbounded busy loop while waiting for
* BMI_EXECUTE.
*
* If BMI_EXECUTE ever needs to support longer-latency execution,
* especially in production, this code needs to be enhanced to sleep
* and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently
* a function of Host processor speed.
*/
if (len >= 4) { /* NB: Currently, always true */
ret = ath6kl_bmi_get_rx_lkahd(ar, want_timeout);
if (ret)
return ret;
}
addr = ar->mbox_info.htc_addr;
ret = hif_read_write_sync(ar, addr, buf, len,
HIF_RD_SYNC_BYTE_INC);
if (ret) {
ath6kl_err("Unable to read the bmi data from the device: %d\n",
ret);
return ret;
}
return 0;
}
int ath6kl_bmi_done(struct ath6kl *ar)
{
int ret;
u32 cid = BMI_DONE;
if (ar->bmi.done_sent) {
ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
return 0;
}
ar->bmi.done_sent = true;
ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
if (ret) {
ath6kl_err("Unable to send bmi done: %d\n", ret);
return ret;
}
ath6kl_bmi_cleanup(ar);
return 0;
}
int ath6kl_bmi_get_target_info(struct ath6kl *ar,
struct ath6kl_bmi_target_info *targ_info)
{
int ret;
u32 cid = BMI_GET_TARGET_INFO;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
if (ret) {
ath6kl_err("Unable to send get target info: %d\n", ret);
return ret;
}
ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version,
sizeof(targ_info->version), true);
if (ret) {
ath6kl_err("Unable to recv target info: %d\n", ret);
return ret;
}
if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
/* Determine how many bytes are in the Target's targ_info */
ret = ath6kl_bmi_recv_buf(ar,
(u8 *)&targ_info->byte_count,
sizeof(targ_info->byte_count),
true);
if (ret) {
ath6kl_err("unable to read target info byte count: %d\n",
ret);
return ret;
}
/*
* The target's targ_info doesn't match the host's targ_info.
* We need to do some backwards compatibility to make this work.
*/
if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
WARN_ON(1);
return -EINVAL;
}
/* Read the remainder of the targ_info */
ret = ath6kl_bmi_recv_buf(ar,
((u8 *)targ_info) +
sizeof(targ_info->byte_count),
sizeof(*targ_info) -
sizeof(targ_info->byte_count),
true);
if (ret) {
ath6kl_err("Unable to read target info (%d bytes): %d\n",
targ_info->byte_count, ret);
return ret;
}
}
ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
targ_info->version, targ_info->type);
return 0;
}
int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
{
u32 cid = BMI_READ_MEMORY;
int ret;
u32 offset;
u32 len_remain, rx_len;
u16 size;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len);
if (size > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, size);
ath6kl_dbg(ATH6KL_DBG_BMI,
"bmi read memory: device: addr: 0x%x, len: %d\n",
addr, len);
len_remain = len;
while (len_remain) {
rx_len = (len_remain < BMI_DATASZ_MAX) ?
len_remain : BMI_DATASZ_MAX;
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
offset += sizeof(len);
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n",
ret);
return ret;
}
ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len, true);
if (ret) {
ath6kl_err("Unable to read from the device: %d\n",
ret);
return ret;
}
memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
len_remain -= rx_len; addr += rx_len;
}
return 0;
}
int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
{
u32 cid = BMI_WRITE_MEMORY;
int ret;
u32 offset;
u32 len_remain, tx_len;
const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
u8 aligned_buf[BMI_DATASZ_MAX];
u8 *src;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header);
ath6kl_dbg(ATH6KL_DBG_BMI,
"bmi write memory: addr: 0x%x, len: %d\n", addr, len);
len_remain = len;
while (len_remain) {
src = &buf[len - len_remain];
if (len_remain < (BMI_DATASZ_MAX - header)) {
if (len_remain & 3) {
/* align it with 4 bytes */
len_remain = len_remain +
(4 - (len_remain & 3));
memcpy(aligned_buf, src, len_remain);
src = aligned_buf;
}
tx_len = len_remain;
} else {
tx_len = (BMI_DATASZ_MAX - header);
}
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
offset += sizeof(tx_len);
memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
offset += tx_len;
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n",
ret);
return ret;
}
len_remain -= tx_len; addr += tx_len;
}
return 0;
}
int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
{
u32 cid = BMI_EXECUTE;
int ret;
u32 offset;
u16 size;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
size = sizeof(cid) + sizeof(addr) + sizeof(param);
if (size > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, size);
ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
addr, *param);
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
offset += sizeof(*param);
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
}
ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), false);
if (ret) {
ath6kl_err("Unable to read from the device: %d\n", ret);
return ret;
}
memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
return 0;
}
int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
{
u32 cid = BMI_SET_APP_START;
int ret;
u32 offset;
u16 size;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
size = sizeof(cid) + sizeof(addr);
if (size > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, size);
ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
}
return 0;
}
int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
{
u32 cid = BMI_READ_SOC_REGISTER;
int ret;
u32 offset;
u16 size;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
size = sizeof(cid) + sizeof(addr);
if (size > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, size);
ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
}
ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), true);
if (ret) {
ath6kl_err("Unable to read from the device: %d\n", ret);
return ret;
}
memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
return 0;
}
int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
{
u32 cid = BMI_WRITE_SOC_REGISTER;
int ret;
u32 offset;
u16 size;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
size = sizeof(cid) + sizeof(addr) + sizeof(param);
if (size > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, size);
ath6kl_dbg(ATH6KL_DBG_BMI,
"bmi write SOC reg: addr: 0x%x, param: %d\n",
addr, param);
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
offset += sizeof(param);
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
}
return 0;
}
int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
{
u32 cid = BMI_LZ_DATA;
int ret;
u32 offset;
u32 len_remain, tx_len;
const u32 header = sizeof(cid) + sizeof(len);
u16 size;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
size = BMI_DATASZ_MAX + header;
if (size > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, size);
ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
len);
len_remain = len;
while (len_remain) {
tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ?
len_remain : (BMI_DATASZ_MAX - header);
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
offset += sizeof(tx_len);
memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
tx_len);
offset += tx_len;
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n",
ret);
return ret;
}
len_remain -= tx_len;
}
return 0;
}
int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
{
u32 cid = BMI_LZ_STREAM_START;
int ret;
u32 offset;
u16 size;
if (ar->bmi.done_sent) {
ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
return -EACCES;
}
size = sizeof(cid) + sizeof(addr);
if (size > MAX_BMI_CMDBUF_SZ) {
WARN_ON(1);
return -EINVAL;
}
memset(ar->bmi.cmd_buf, 0, size);
ath6kl_dbg(ATH6KL_DBG_BMI,
"bmi LZ stream start: addr: 0x%x)\n",
addr);
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to start LZ stream to the device: %d\n",
ret);
return ret;
}
return 0;
}
int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
{
int ret;
u32 last_word = 0;
u32 last_word_offset = len & ~0x3;
u32 unaligned_bytes = len & 0x3;
ret = ath6kl_bmi_lz_stream_start(ar, addr);
if (ret)
return ret;
if (unaligned_bytes) {
/* copy the last word into a zero padded buffer */
memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
}
ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
if (ret)
return ret;
if (unaligned_bytes)
ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
if (!ret) {
/* Close compressed stream and open a new (fake) one.
* This serves mainly to flush Target caches. */
ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
}
return ret;
}
int ath6kl_bmi_init(struct ath6kl *ar)
{
ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC);
if (!ar->bmi.cmd_buf)
return -ENOMEM;
return 0;
}
void ath6kl_bmi_cleanup(struct ath6kl *ar)
{
kfree(ar->bmi.cmd_buf);
ar->bmi.cmd_buf = NULL;
}

View File

@ -0,0 +1,250 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef BMI_H
#define BMI_H
/*
* Bootloader Messaging Interface (BMI)
*
* BMI is a very simple messaging interface used during initialization
* to read memory, write memory, execute code, and to define an
* application entry PC.
*
* It is used to download an application to ATH6KL, to provide
* patches to code that is already resident on ATH6KL, and generally
* to examine and modify state. The Host has an opportunity to use
* BMI only once during bootup. Once the Host issues a BMI_DONE
* command, this opportunity ends.
*
* The Host writes BMI requests to mailbox0, and reads BMI responses
* from mailbox0. BMI requests all begin with a command
* (see below for specific commands), and are followed by
* command-specific data.
*
* Flow control:
* The Host can only issue a command once the Target gives it a
* "BMI Command Credit", using ATH6KL Counter #4. As soon as the
* Target has completed a command, it issues another BMI Command
* Credit (so the Host can issue the next command).
*
* BMI handles all required Target-side cache flushing.
*/
#define MAX_BMI_CMDBUF_SZ (BMI_DATASZ_MAX + \
(sizeof(u32) * 3 /* cmd + addr + len */))
/* Maximum data size used for BMI transfers */
#define BMI_DATASZ_MAX 256
/* BMI Commands */
#define BMI_NO_COMMAND 0
#define BMI_DONE 1
/*
* Semantics: Host is done using BMI
* Request format:
* u32 command (BMI_DONE)
* Response format: none
*/
#define BMI_READ_MEMORY 2
/*
* Semantics: Host reads ATH6KL memory
* Request format:
* u32 command (BMI_READ_MEMORY)
* u32 address
* u32 length, at most BMI_DATASZ_MAX
* Response format:
* u8 data[length]
*/
#define BMI_WRITE_MEMORY 3
/*
* Semantics: Host writes ATH6KL memory
* Request format:
* u32 command (BMI_WRITE_MEMORY)
* u32 address
* u32 length, at most BMI_DATASZ_MAX
* u8 data[length]
* Response format: none
*/
#define BMI_EXECUTE 4
/*
* Semantics: Causes ATH6KL to execute code
* Request format:
* u32 command (BMI_EXECUTE)
* u32 address
* u32 parameter
* Response format:
* u32 return value
*/
#define BMI_SET_APP_START 5
/*
* Semantics: Set Target application starting address
* Request format:
* u32 command (BMI_SET_APP_START)
* u32 address
* Response format: none
*/
#define BMI_READ_SOC_REGISTER 6
/*
* Semantics: Read a 32-bit Target SOC register.
* Request format:
* u32 command (BMI_READ_REGISTER)
* u32 address
* Response format:
* u32 value
*/
#define BMI_WRITE_SOC_REGISTER 7
/*
* Semantics: Write a 32-bit Target SOC register.
* Request format:
* u32 command (BMI_WRITE_REGISTER)
* u32 address
* u32 value
*
* Response format: none
*/
#define BMI_GET_TARGET_ID 8
#define BMI_GET_TARGET_INFO 8
/*
* Semantics: Fetch the 4-byte Target information
* Request format:
* u32 command (BMI_GET_TARGET_ID/INFO)
* Response format1 (old firmware):
* u32 TargetVersionID
* Response format2 (newer firmware):
* u32 TARGET_VERSION_SENTINAL
* struct bmi_target_info;
*/
#define TARGET_VERSION_SENTINAL 0xffffffff
#define TARGET_TYPE_AR6003 3
#define BMI_ROMPATCH_INSTALL 9
/*
* Semantics: Install a ROM Patch.
* Request format:
* u32 command (BMI_ROMPATCH_INSTALL)
* u32 Target ROM Address
* u32 Target RAM Address or Value (depending on Target Type)
* u32 Size, in bytes
* u32 Activate? 1-->activate;
* 0-->install but do not activate
* Response format:
* u32 PatchID
*/
#define BMI_ROMPATCH_UNINSTALL 10
/*
* Semantics: Uninstall a previously-installed ROM Patch,
* automatically deactivating, if necessary.
* Request format:
* u32 command (BMI_ROMPATCH_UNINSTALL)
* u32 PatchID
*
* Response format: none
*/
#define BMI_ROMPATCH_ACTIVATE 11
/*
* Semantics: Activate a list of previously-installed ROM Patches.
* Request format:
* u32 command (BMI_ROMPATCH_ACTIVATE)
* u32 rompatch_count
* u32 PatchID[rompatch_count]
*
* Response format: none
*/
#define BMI_ROMPATCH_DEACTIVATE 12
/*
* Semantics: Deactivate a list of active ROM Patches.
* Request format:
* u32 command (BMI_ROMPATCH_DEACTIVATE)
* u32 rompatch_count
* u32 PatchID[rompatch_count]
*
* Response format: none
*/
#define BMI_LZ_STREAM_START 13
/*
* Semantics: Begin an LZ-compressed stream of input
* which is to be uncompressed by the Target to an
* output buffer at address. The output buffer must
* be sufficiently large to hold the uncompressed
* output from the compressed input stream. This BMI
* command should be followed by a series of 1 or more
* BMI_LZ_DATA commands.
* u32 command (BMI_LZ_STREAM_START)
* u32 address
* Note: Not supported on all versions of ROM firmware.
*/
#define BMI_LZ_DATA 14
/*
* Semantics: Host writes ATH6KL memory with LZ-compressed
* data which is uncompressed by the Target. This command
* must be preceded by a BMI_LZ_STREAM_START command. A series
* of BMI_LZ_DATA commands are considered part of a single
* input stream until another BMI_LZ_STREAM_START is issued.
* Request format:
* u32 command (BMI_LZ_DATA)
* u32 length (of compressed data),
* at most BMI_DATASZ_MAX
* u8 CompressedData[length]
* Response format: none
* Note: Not supported on all versions of ROM firmware.
*/
#define BMI_COMMUNICATION_TIMEOUT 1000 /* in msec */
struct ath6kl;
struct ath6kl_bmi_target_info {
__le32 byte_count; /* size of this structure */
__le32 version; /* target version id */
__le32 type; /* target type */
} __packed;
int ath6kl_bmi_init(struct ath6kl *ar);
void ath6kl_bmi_cleanup(struct ath6kl *ar);
int ath6kl_bmi_done(struct ath6kl *ar);
int ath6kl_bmi_get_target_info(struct ath6kl *ar,
struct ath6kl_bmi_target_info *targ_info);
int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len);
int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len);
int ath6kl_bmi_execute(struct ath6kl *ar,
u32 addr, u32 *param);
int ath6kl_bmi_set_app_start(struct ath6kl *ar,
u32 addr);
int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param);
int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param);
int ath6kl_bmi_lz_data(struct ath6kl *ar,
u8 *buf, u32 len);
int ath6kl_bmi_lz_stream_start(struct ath6kl *ar,
u32 addr);
int ath6kl_bmi_fast_download(struct ath6kl *ar,
u32 addr, u8 *buf, u32 len);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef ATH6KL_CFG80211_H
#define ATH6KL_CFG80211_H
struct wireless_dev *ath6kl_cfg80211_init(struct device *dev);
void ath6kl_cfg80211_deinit(struct ath6kl *ar);
void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status);
void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
u8 *bssid, u16 listen_intvl,
u16 beacon_intvl,
enum network_type nw_type,
u8 beacon_ie_len, u8 assoc_req_len,
u8 assoc_resp_len, u8 *assoc_info);
void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
u8 *bssid, u8 assoc_resp_len,
u8 *assoc_info, u16 proto_reason);
void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
bool ismcast);
#endif /* ATH6KL_CFG80211_H */

View File

@ -0,0 +1,183 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef COMMON_H
#define COMMON_H
#include <linux/netdevice.h>
#define ATH6KL_MAX_IE 256
extern int ath6kl_printk(const char *level, const char *fmt, ...);
#define A_CACHE_LINE_PAD 128
/*
* Reflects the version of binary interface exposed by ATH6KL target
* firmware. Needs to be incremented by 1 for any change in the firmware
* that requires upgrade of the driver on the host side for the change to
* work correctly
*/
#define ATH6KL_ABI_VERSION 1
#define SIGNAL_QUALITY_METRICS_NUM_MAX 2
enum {
SIGNAL_QUALITY_METRICS_SNR = 0,
SIGNAL_QUALITY_METRICS_RSSI,
SIGNAL_QUALITY_METRICS_ALL,
};
/*
* Data Path
*/
#define WMI_MAX_TX_DATA_FRAME_LENGTH \
(1500 + sizeof(struct wmi_data_hdr) + \
sizeof(struct ethhdr) + \
sizeof(struct ath6kl_llc_snap_hdr))
/* An AMSDU frame */ /* The MAX AMSDU length of AR6003 is 3839 */
#define WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH \
(3840 + sizeof(struct wmi_data_hdr) + \
sizeof(struct ethhdr) + \
sizeof(struct ath6kl_llc_snap_hdr))
#define EPPING_ALIGNMENT_PAD \
(((sizeof(struct htc_frame_hdr) + 3) & (~0x3)) \
- sizeof(struct htc_frame_hdr))
struct ath6kl_llc_snap_hdr {
u8 dsap;
u8 ssap;
u8 cntl;
u8 org_code[3];
__be16 eth_type;
} __packed;
enum crypto_type {
NONE_CRYPT = 0x01,
WEP_CRYPT = 0x02,
TKIP_CRYPT = 0x04,
AES_CRYPT = 0x08,
};
#define ATH6KL_NODE_HASHSIZE 32
/* simple hash is enough for variation of macaddr */
#define ATH6KL_NODE_HASH(addr) \
(((const u8 *)(addr))[ETH_ALEN - 1] % \
ATH6KL_NODE_HASHSIZE)
/*
* Table of ath6kl_node instances. Each ieee80211com
* has at least one for holding the scan candidates.
* When operating as an access point or in ibss mode there
* is a second table for associated stations or neighbors.
*/
struct ath6kl_node_table {
void *nt_wmi; /* back reference */
spinlock_t nt_nodelock; /* on node table */
struct bss *nt_node_first; /* information of all nodes */
struct bss *nt_node_last; /* information of all nodes */
struct bss *nt_hash[ATH6KL_NODE_HASHSIZE];
const char *nt_name; /* for debugging */
u32 nt_node_age; /* node aging time */
};
#define WLAN_NODE_INACT_TIMEOUT_MSEC 120000
#define WLAN_NODE_INACT_CNT 4
struct ath6kl_common_ie {
u16 ie_chan;
u8 *ie_tstamp;
u8 *ie_ssid;
u8 *ie_rates;
u8 *ie_xrates;
u8 *ie_country;
u8 *ie_wpa;
u8 *ie_rsn;
u8 *ie_wmm;
u8 *ie_ath;
u16 ie_capInfo;
u16 ie_beaconInt;
u8 *ie_tim;
u8 *ie_chswitch;
u8 ie_erp;
u8 *ie_wsc;
u8 *ie_htcap;
u8 *ie_htop;
};
struct bss {
u8 ni_macaddr[ETH_ALEN];
u8 ni_snr;
s16 ni_rssi;
struct bss *ni_list_next;
struct bss *ni_list_prev;
struct bss *ni_hash_next;
struct bss *ni_hash_prev;
struct ath6kl_common_ie ni_cie;
u8 *ni_buf;
u16 ni_framelen;
struct ath6kl_node_table *ni_table;
u32 ni_refcnt;
u32 ni_tstamp;
u32 ni_actcnt;
};
struct htc_endpoint_credit_dist;
struct ath6kl;
enum htc_credit_dist_reason;
struct htc_credit_state_info;
struct bss *wlan_node_alloc(int wh_size);
void wlan_node_free(struct bss *ni);
void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
const u8 *mac_addr);
struct bss *wlan_find_node(struct ath6kl_node_table *nt,
const u8 *mac_addr);
void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni);
void wlan_free_allnodes(struct ath6kl_node_table *nt);
void wlan_iterate_nodes(struct ath6kl_node_table *nt,
void (*f) (void *arg, struct bss *),
void *arg);
void wlan_node_table_init(void *wmip, struct ath6kl_node_table *nt);
void wlan_node_table_cleanup(struct ath6kl_node_table *nt);
void wlan_refresh_inactive_nodes(struct ath6kl_node_table *nt);
struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 *ssid,
u32 ssid_len, bool is_wpa2, bool match_ssid);
void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni);
int ath6k_setup_credit_dist(void *htc_handle,
struct htc_credit_state_info *cred_info);
void ath6k_credit_distribute(struct htc_credit_state_info *cred_inf,
struct list_head *epdist_list,
enum htc_credit_dist_reason reason);
void ath6k_credit_init(struct htc_credit_state_info *cred_inf,
struct list_head *ep_list,
int tot_credits);
void ath6k_seek_credits(struct htc_credit_state_info *cred_inf,
struct htc_endpoint_credit_dist *ep_dist);
struct ath6kl *ath6kl_core_alloc(struct device *sdev);
int ath6kl_core_init(struct ath6kl *ar);
int ath6kl_unavail_ev(struct ath6kl *ar);
struct sk_buff *ath6kl_buf_alloc(int size);
#endif /* COMMON_H */

View File

@ -0,0 +1,546 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef CORE_H
#define CORE_H
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/firmware.h>
#include <linux/sched.h>
#include <net/cfg80211.h>
#include "htc.h"
#include "wmi.h"
#include "bmi.h"
#define MAX_ATH6KL 1
#define ATH6KL_MAX_RX_BUFFERS 16
#define ATH6KL_BUFFER_SIZE 1664
#define ATH6KL_MAX_AMSDU_RX_BUFFERS 4
#define ATH6KL_AMSDU_REFILL_THRESHOLD 3
#define ATH6KL_AMSDU_BUFFER_SIZE (WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH + 128)
#define MAX_MSDU_SUBFRAME_PAYLOAD_LEN 1508
#define MIN_MSDU_SUBFRAME_PAYLOAD_LEN 46
#define USER_SAVEDKEYS_STAT_INIT 0
#define USER_SAVEDKEYS_STAT_RUN 1
#define ATH6KL_TX_TIMEOUT 10
#define ATH6KL_MAX_ENDPOINTS 4
#define MAX_NODE_NUM 15
/* MAX_HI_COOKIE_NUM are reserved for high priority traffic */
#define MAX_DEF_COOKIE_NUM 180
#define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */
#define MAX_COOKIE_NUM (MAX_DEF_COOKIE_NUM + MAX_HI_COOKIE_NUM)
#define MAX_DEFAULT_SEND_QUEUE_DEPTH (MAX_DEF_COOKIE_NUM / WMM_NUM_AC)
#define DISCON_TIMER_INTVAL 10000 /* in msec */
#define A_DEFAULT_LISTEN_INTERVAL 100
#define A_MAX_WOW_LISTEN_INTERVAL 1000
/* AR6003 1.0 definitions */
#define AR6003_REV1_VERSION 0x300002ba
/* AR6003 2.0 definitions */
#define AR6003_REV2_VERSION 0x30000384
#define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS 0x57e910
#define AR6003_REV2_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77"
#define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77"
#define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin"
#define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin"
#define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin"
/* AR6003 3.0 definitions */
#define AR6003_REV3_VERSION 0x30000582
#define AR6003_REV3_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin"
#define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin"
#define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin"
#define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin"
#define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \
"ath6k/AR6003/hw2.1.1/bdata.SD31.bin"
/* Per STA data, used in AP mode */
#define STA_PS_AWAKE BIT(0)
#define STA_PS_SLEEP BIT(1)
#define STA_PS_POLLED BIT(2)
/* HTC TX packet tagging definitions */
#define ATH6KL_CONTROL_PKT_TAG HTC_TX_PACKET_TAG_USER_DEFINED
#define ATH6KL_DATA_PKT_TAG (ATH6KL_CONTROL_PKT_TAG + 1)
#define AR6003_CUST_DATA_SIZE 16
#define AGGR_WIN_IDX(x, y) ((x) % (y))
#define AGGR_INCR_IDX(x, y) AGGR_WIN_IDX(((x) + 1), (y))
#define AGGR_DCRM_IDX(x, y) AGGR_WIN_IDX(((x) - 1), (y))
#define ATH6KL_MAX_SEQ_NO 0xFFF
#define ATH6KL_NEXT_SEQ_NO(x) (((x) + 1) & ATH6KL_MAX_SEQ_NO)
#define NUM_OF_TIDS 8
#define AGGR_SZ_DEFAULT 8
#define AGGR_WIN_SZ_MIN 2
#define AGGR_WIN_SZ_MAX 8
#define TID_WINDOW_SZ(_x) ((_x) << 1)
#define AGGR_NUM_OF_FREE_NETBUFS 16
#define AGGR_RX_TIMEOUT 400 /* in ms */
#define WMI_TIMEOUT (2 * HZ)
#define MBOX_YIELD_LIMIT 99
/* configuration lags */
/*
* ATH6KL_CONF_IGNORE_ERP_BARKER: Ignore the barker premable in
* ERP IE of beacon to determine the short premable support when
* sending (Re)Assoc req.
* ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN: Don't send the power
* module state transition failure events which happen during
* scan, to the host.
*/
#define ATH6KL_CONF_IGNORE_ERP_BARKER BIT(0)
#define ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN BIT(1)
#define ATH6KL_CONF_ENABLE_11N BIT(2)
#define ATH6KL_CONF_ENABLE_TX_BURST BIT(3)
enum wlan_low_pwr_state {
WLAN_POWER_STATE_ON,
WLAN_POWER_STATE_CUT_PWR,
WLAN_POWER_STATE_DEEP_SLEEP,
WLAN_POWER_STATE_WOW
};
enum sme_state {
SME_DISCONNECTED,
SME_CONNECTING,
SME_CONNECTED
};
enum ath6kl_wlan_state {
WLAN_DISABLED,
WLAN_ENABLED
};
struct skb_hold_q {
struct sk_buff *skb;
bool is_amsdu;
u16 seq_no;
};
struct rxtid {
bool aggr;
bool progress;
bool timer_mon;
u16 win_sz;
u16 seq_next;
u32 hold_q_sz;
struct skb_hold_q *hold_q;
struct sk_buff_head q;
spinlock_t lock;
};
struct rxtid_stats {
u32 num_into_aggr;
u32 num_dups;
u32 num_oow;
u32 num_mpdu;
u32 num_amsdu;
u32 num_delivered;
u32 num_timeouts;
u32 num_hole;
u32 num_bar;
};
struct aggr_info {
u8 aggr_sz;
u8 timer_scheduled;
struct timer_list timer;
struct net_device *dev;
struct rxtid rx_tid[NUM_OF_TIDS];
struct sk_buff_head free_q;
struct rxtid_stats stat[NUM_OF_TIDS];
};
struct ath6kl_wep_key {
u8 key_index;
u8 key_len;
u8 key[64];
};
#define ATH6KL_KEY_SEQ_LEN 8
struct ath6kl_key {
u8 key[WLAN_MAX_KEY_LEN];
u8 key_len;
u8 seq[ATH6KL_KEY_SEQ_LEN];
u8 seq_len;
u32 cipher;
};
struct ath6kl_node_mapping {
u8 mac_addr[ETH_ALEN];
u8 ep_id;
u8 tx_pend;
};
struct ath6kl_cookie {
struct sk_buff *skb;
u32 map_no;
struct htc_packet htc_pkt;
struct ath6kl_cookie *arc_list_next;
};
struct ath6kl_sta {
u16 sta_flags;
u8 mac[ETH_ALEN];
u8 aid;
u8 keymgmt;
u8 ucipher;
u8 auth;
u8 wpa_ie[ATH6KL_MAX_IE];
struct sk_buff_head psq;
spinlock_t psq_lock;
};
struct ath6kl_version {
u32 target_ver;
u32 wlan_ver;
u32 abi_ver;
};
struct ath6kl_bmi {
u32 cmd_credits;
bool done_sent;
u8 *cmd_buf;
};
struct target_stats {
u64 tx_pkt;
u64 tx_byte;
u64 tx_ucast_pkt;
u64 tx_ucast_byte;
u64 tx_mcast_pkt;
u64 tx_mcast_byte;
u64 tx_bcast_pkt;
u64 tx_bcast_byte;
u64 tx_rts_success_cnt;
u64 tx_pkt_per_ac[4];
u64 tx_err;
u64 tx_fail_cnt;
u64 tx_retry_cnt;
u64 tx_mult_retry_cnt;
u64 tx_rts_fail_cnt;
u64 rx_pkt;
u64 rx_byte;
u64 rx_ucast_pkt;
u64 rx_ucast_byte;
u64 rx_mcast_pkt;
u64 rx_mcast_byte;
u64 rx_bcast_pkt;
u64 rx_bcast_byte;
u64 rx_frgment_pkt;
u64 rx_err;
u64 rx_crc_err;
u64 rx_key_cache_miss;
u64 rx_decrypt_err;
u64 rx_dupl_frame;
u64 tkip_local_mic_fail;
u64 tkip_cnter_measures_invoked;
u64 tkip_replays;
u64 tkip_fmt_err;
u64 ccmp_fmt_err;
u64 ccmp_replays;
u64 pwr_save_fail_cnt;
u64 cs_bmiss_cnt;
u64 cs_low_rssi_cnt;
u64 cs_connect_cnt;
u64 cs_discon_cnt;
s32 tx_ucast_rate;
s32 rx_ucast_rate;
u32 lq_val;
u32 wow_pkt_dropped;
u16 wow_evt_discarded;
s16 noise_floor_calib;
s16 cs_rssi;
s16 cs_ave_beacon_rssi;
u8 cs_ave_beacon_snr;
u8 cs_last_roam_msec;
u8 cs_snr;
u8 wow_host_pkt_wakeups;
u8 wow_host_evt_wakeups;
u32 arp_received;
u32 arp_matched;
u32 arp_replied;
};
struct ath6kl_mbox_info {
u32 htc_addr;
u32 htc_ext_addr;
u32 htc_ext_sz;
u32 block_size;
u32 gmbox_addr;
u32 gmbox_sz;
};
/*
* 802.11i defines an extended IV for use with non-WEP ciphers.
* When the EXTIV bit is set in the key id byte an additional
* 4 bytes immediately follow the IV for TKIP. For CCMP the
* EXTIV bit is likewise set but the 8 bytes represent the
* CCMP header rather than IV+extended-IV.
*/
#define ATH6KL_KEYBUF_SIZE 16
#define ATH6KL_MICBUF_SIZE (8+8) /* space for both tx and rx */
#define ATH6KL_KEY_XMIT 0x01
#define ATH6KL_KEY_RECV 0x02
#define ATH6KL_KEY_DEFAULT 0x80 /* default xmit key */
/*
* WPA/RSN get/set key request. Specify the key/cipher
* type and whether the key is to be used for sending and/or
* receiving. The key index should be set only when working
* with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
* Otherwise a unicast/pairwise key is specified by the bssid
* (on a station) or mac address (on an ap). They key length
* must include any MIC key data; otherwise it should be no
* more than ATH6KL_KEYBUF_SIZE.
*/
struct ath6kl_req_key {
u8 ik_type; /* key/cipher type */
u8 ik_pad;
u16 ik_keyix; /* key index */
u8 ik_keylen; /* key length in bytes */
u8 ik_flags;
u8 ik_macaddr[ETH_ALEN];
u64 ik_keyrsc; /* key receive sequence counter */
u64 ik_keytsc; /* key transmit sequence counter */
u8 ik_keydata[ATH6KL_KEYBUF_SIZE + ATH6KL_MICBUF_SIZE];
};
/* Flag info */
#define WMI_ENABLED 0
#define WMI_READY 1
#define CONNECTED 2
#define STATS_UPDATE_PEND 3
#define CONNECT_PEND 4
#define WMM_ENABLED 5
#define NETQ_STOPPED 6
#define WMI_CTRL_EP_FULL 7
#define DTIM_EXPIRED 8
#define DESTROY_IN_PROGRESS 9
#define NETDEV_REGISTERED 10
#define SKIP_SCAN 11
struct ath6kl {
struct device *dev;
struct net_device *net_dev;
struct ath6kl_bmi bmi;
const struct ath6kl_hif_ops *hif_ops;
struct wmi *wmi;
int tx_pending[ENDPOINT_MAX];
int total_tx_data_pend;
struct htc_target *htc_target;
void *hif_priv;
spinlock_t lock;
struct semaphore sem;
int ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 next_mode;
u8 nw_type;
u8 dot11_auth_mode;
u8 auth_mode;
u8 prwise_crypto;
u8 prwise_crypto_len;
u8 grp_crypto;
u8 grp_crpto_len;
u8 def_txkey_index;
struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
u8 bssid[ETH_ALEN];
u8 req_bssid[ETH_ALEN];
u16 ch_hint;
u16 bss_ch;
u16 listen_intvl_b;
u16 listen_intvl_t;
struct ath6kl_version version;
u32 target_type;
u8 tx_pwr;
struct net_device_stats net_stats;
struct target_stats target_stats;
enum ath6kl_wlan_state wlan_state;
struct ath6kl_node_mapping node_map[MAX_NODE_NUM];
u8 ibss_ps_enable;
u8 node_num;
u8 next_ep_id;
struct ath6kl_cookie *cookie_list;
u32 cookie_count;
enum htc_endpoint_id ac2ep_map[WMM_NUM_AC];
bool ac_stream_active[WMM_NUM_AC];
u8 ac_stream_pri_map[WMM_NUM_AC];
u8 hiac_stream_active_pri;
u8 ep2ac_map[ENDPOINT_MAX];
enum htc_endpoint_id ctrl_ep;
struct htc_credit_state_info credit_state_info;
u32 connect_ctrl_flags;
u32 user_key_ctrl;
u8 usr_bss_filter;
struct ath6kl_sta sta_list[AP_MAX_NUM_STA];
u8 sta_list_index;
struct ath6kl_req_key ap_mode_bkey;
struct sk_buff_head mcastpsq;
spinlock_t mcastpsq_lock;
u8 intra_bss;
struct aggr_info *aggr_cntxt;
struct wmi_ap_mode_stat ap_stats;
u8 ap_country_code[3];
struct list_head amsdu_rx_buffer_queue;
struct timer_list disconnect_timer;
u8 rx_meta_ver;
struct wireless_dev *wdev;
struct cfg80211_scan_request *scan_req;
struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1];
enum sme_state sme_state;
enum wlan_low_pwr_state wlan_pwr_state;
struct wmi_scan_params_cmd sc_params;
#define AR_MCAST_FILTER_MAC_ADDR_SIZE 4
u8 auto_auth_stage;
u16 conf_flags;
wait_queue_head_t event_wq;
struct ath6kl_mbox_info mbox_info;
struct ath6kl_cookie cookie_mem[MAX_COOKIE_NUM];
int reconnect_flag;
unsigned long flag;
u8 *fw_board;
size_t fw_board_len;
u8 *fw_otp;
size_t fw_otp_len;
u8 *fw;
size_t fw_len;
u8 *fw_patch;
size_t fw_patch_len;
struct workqueue_struct *ath6kl_wq;
};
static inline void *ath6kl_priv(struct net_device *dev)
{
return wdev_priv(dev->ieee80211_ptr);
}
static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info
*cred_info,
struct htc_endpoint_credit_dist
*ep_dist, int credits)
{
ep_dist->credits += credits;
ep_dist->cred_assngd += credits;
cred_info->cur_free_credits -= credits;
}
void ath6kl_destroy(struct net_device *dev, unsigned int unregister);
int ath6kl_configure_target(struct ath6kl *ar);
void ath6kl_detect_error(unsigned long ptr);
void disconnect_timer_handler(unsigned long ptr);
void init_netdev(struct net_device *dev);
void ath6kl_cookie_init(struct ath6kl *ar);
void ath6kl_cookie_cleanup(struct ath6kl *ar);
void ath6kl_rx(struct htc_target *target, struct htc_packet *packet);
void ath6kl_tx_complete(void *context, struct list_head *packet_queue);
enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
struct htc_packet *packet);
void ath6kl_stop_txrx(struct ath6kl *ar);
void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar);
int ath6kl_access_datadiag(struct ath6kl *ar, u32 address,
u8 *data, u32 length, bool read);
int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data);
void ath6kl_init_profile_info(struct ath6kl *ar);
void ath6kl_tx_data_cleanup(struct ath6kl *ar);
void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
bool get_dbglogs);
struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar);
void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie);
int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev);
struct aggr_info *aggr_init(struct net_device *dev);
void ath6kl_rx_refill(struct htc_target *target,
enum htc_endpoint_id endpoint);
void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count);
struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target,
enum htc_endpoint_id endpoint,
int len);
void aggr_module_destroy(struct aggr_info *aggr_info);
void aggr_reset_state(struct aggr_info *aggr_info);
struct ath6kl_sta *ath6kl_find_sta(struct ath6kl *ar, u8 * node_addr);
struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid);
void ath6kl_ready_event(void *devt, u8 * datap, u32 sw_ver, u32 abi_ver);
int ath6kl_control_tx(void *devt, struct sk_buff *skb,
enum htc_endpoint_id eid);
void ath6kl_connect_event(struct ath6kl *ar, u16 channel,
u8 *bssid, u16 listen_int,
u16 beacon_int, enum network_type net_type,
u8 beacon_ie_len, u8 assoc_req_len,
u8 assoc_resp_len, u8 *assoc_info);
void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason,
u8 *bssid, u8 assoc_resp_len,
u8 *assoc_info, u16 prot_reason_status);
void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast);
void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr);
void ath6kl_scan_complete_evt(struct ath6kl *ar, int status);
void ath6kl_tgt_stats_event(struct ath6kl *ar, u8 *ptr, u32 len);
void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active);
enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac);
void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid);
void ath6kl_dtimexpiry_event(struct ath6kl *ar);
void ath6kl_disconnect(struct ath6kl *ar);
void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid);
void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no,
u8 win_sz);
void ath6kl_wakeup_event(void *dev);
void ath6kl_target_failure(struct ath6kl *ar);
#endif /* CORE_H */

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#include "core.h"
#include "debug.h"
int ath6kl_printk(const char *level, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
int rtn;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
rtn = printk("%sath6kl: %pV", level, &vaf);
va_end(args);
return rtn;
}
#ifdef CONFIG_ATH6KL_DEBUG
void ath6kl_dump_registers(struct ath6kl_device *dev,
struct ath6kl_irq_proc_registers *irq_proc_reg,
struct ath6kl_irq_enable_reg *irq_enable_reg)
{
ath6kl_dbg(ATH6KL_DBG_ANY, ("<------- Register Table -------->\n"));
if (irq_proc_reg != NULL) {
ath6kl_dbg(ATH6KL_DBG_ANY,
"Host Int status: 0x%x\n",
irq_proc_reg->host_int_status);
ath6kl_dbg(ATH6KL_DBG_ANY,
"CPU Int status: 0x%x\n",
irq_proc_reg->cpu_int_status);
ath6kl_dbg(ATH6KL_DBG_ANY,
"Error Int status: 0x%x\n",
irq_proc_reg->error_int_status);
ath6kl_dbg(ATH6KL_DBG_ANY,
"Counter Int status: 0x%x\n",
irq_proc_reg->counter_int_status);
ath6kl_dbg(ATH6KL_DBG_ANY,
"Mbox Frame: 0x%x\n",
irq_proc_reg->mbox_frame);
ath6kl_dbg(ATH6KL_DBG_ANY,
"Rx Lookahead Valid: 0x%x\n",
irq_proc_reg->rx_lkahd_valid);
ath6kl_dbg(ATH6KL_DBG_ANY,
"Rx Lookahead 0: 0x%x\n",
irq_proc_reg->rx_lkahd[0]);
ath6kl_dbg(ATH6KL_DBG_ANY,
"Rx Lookahead 1: 0x%x\n",
irq_proc_reg->rx_lkahd[1]);
if (dev->ar->mbox_info.gmbox_addr != 0) {
/*
* If the target supports GMBOX hardware, dump some
* additional state.
*/
ath6kl_dbg(ATH6KL_DBG_ANY,
"GMBOX Host Int status 2: 0x%x\n",
irq_proc_reg->host_int_status2);
ath6kl_dbg(ATH6KL_DBG_ANY,
"GMBOX RX Avail: 0x%x\n",
irq_proc_reg->gmbox_rx_avail);
ath6kl_dbg(ATH6KL_DBG_ANY,
"GMBOX lookahead alias 0: 0x%x\n",
irq_proc_reg->rx_gmbox_lkahd_alias[0]);
ath6kl_dbg(ATH6KL_DBG_ANY,
"GMBOX lookahead alias 1: 0x%x\n",
irq_proc_reg->rx_gmbox_lkahd_alias[1]);
}
}
if (irq_enable_reg != NULL) {
ath6kl_dbg(ATH6KL_DBG_ANY,
"Int status Enable: 0x%x\n",
irq_enable_reg->int_status_en);
ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status Enable: 0x%x\n",
irq_enable_reg->cntr_int_status_en);
}
ath6kl_dbg(ATH6KL_DBG_ANY, "<------------------------------->\n");
}
static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist)
{
ath6kl_dbg(ATH6KL_DBG_ANY,
"--- endpoint: %d svc_id: 0x%X ---\n",
ep_dist->endpoint, ep_dist->svc_id);
ath6kl_dbg(ATH6KL_DBG_ANY, " dist_flags : 0x%X\n",
ep_dist->dist_flags);
ath6kl_dbg(ATH6KL_DBG_ANY, " cred_norm : %d\n",
ep_dist->cred_norm);
ath6kl_dbg(ATH6KL_DBG_ANY, " cred_min : %d\n",
ep_dist->cred_min);
ath6kl_dbg(ATH6KL_DBG_ANY, " credits : %d\n",
ep_dist->credits);
ath6kl_dbg(ATH6KL_DBG_ANY, " cred_assngd : %d\n",
ep_dist->cred_assngd);
ath6kl_dbg(ATH6KL_DBG_ANY, " seek_cred : %d\n",
ep_dist->seek_cred);
ath6kl_dbg(ATH6KL_DBG_ANY, " cred_sz : %d\n",
ep_dist->cred_sz);
ath6kl_dbg(ATH6KL_DBG_ANY, " cred_per_msg : %d\n",
ep_dist->cred_per_msg);
ath6kl_dbg(ATH6KL_DBG_ANY, " cred_to_dist : %d\n",
ep_dist->cred_to_dist);
ath6kl_dbg(ATH6KL_DBG_ANY, " txq_depth : %d\n",
get_queue_depth(&((struct htc_endpoint *)
ep_dist->htc_rsvd)->txq));
ath6kl_dbg(ATH6KL_DBG_ANY,
"----------------------------------\n");
}
void dump_cred_dist_stats(struct htc_target *target)
{
struct htc_endpoint_credit_dist *ep_list;
if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_TRC))
return;
list_for_each_entry(ep_list, &target->cred_dist_list, list)
dump_cred_dist(ep_list);
ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:%p dist:%p\n",
target->cred_dist_cntxt, NULL);
ath6kl_dbg(ATH6KL_DBG_TRC, "credit distribution, total : %d, free : %d\n",
target->cred_dist_cntxt->total_avail_credits,
target->cred_dist_cntxt->cur_free_credits);
}
#endif

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef DEBUG_H
#define DEBUG_H
#include "htc_hif.h"
enum ATH6K_DEBUG_MASK {
ATH6KL_DBG_WLAN_CONNECT = BIT(0), /* wlan connect */
ATH6KL_DBG_WLAN_SCAN = BIT(1), /* wlan scan */
ATH6KL_DBG_WLAN_TX = BIT(2), /* wlan tx */
ATH6KL_DBG_WLAN_RX = BIT(3), /* wlan rx */
ATH6KL_DBG_BMI = BIT(4), /* bmi tracing */
ATH6KL_DBG_HTC_SEND = BIT(5), /* htc send */
ATH6KL_DBG_HTC_RECV = BIT(6), /* htc recv */
ATH6KL_DBG_IRQ = BIT(7), /* interrupt processing */
ATH6KL_DBG_PM = BIT(8), /* power management */
ATH6KL_DBG_WLAN_NODE = BIT(9), /* general wlan node tracing */
ATH6KL_DBG_WMI = BIT(10), /* wmi tracing */
ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */
ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */
ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */
ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx and wmi frames */
ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */
};
extern unsigned int debug_mask;
extern int ath6kl_printk(const char *level, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3)));
#define ath6kl_info(fmt, ...) \
ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__)
#define ath6kl_err(fmt, ...) \
ath6kl_printk(KERN_ERR, fmt, ##__VA_ARGS__)
#define ath6kl_warn(fmt, ...) \
ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__)
#define AR_DBG_LVL_CHECK(mask) (debug_mask & mask)
#ifdef CONFIG_ATH6KL_DEBUG
#define ath6kl_dbg(mask, fmt, ...) \
({ \
int rtn; \
if (debug_mask & mask) \
rtn = ath6kl_printk(KERN_DEBUG, fmt, ##__VA_ARGS__); \
else \
rtn = 0; \
\
rtn; \
})
static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
const char *msg, const void *buf,
size_t len)
{
if (debug_mask & mask) {
ath6kl_dbg(mask, "%s\n", msg);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
}
}
void ath6kl_dump_registers(struct ath6kl_device *dev,
struct ath6kl_irq_proc_registers *irq_proc_reg,
struct ath6kl_irq_enable_reg *irq_en_reg);
void dump_cred_dist_stats(struct htc_target *target);
#else
static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
const char *fmt, ...)
{
return 0;
}
static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
const char *msg, const void *buf,
size_t len)
{
}
static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
struct ath6kl_irq_proc_registers *irq_proc_reg,
struct ath6kl_irq_enable_reg *irq_en_reg)
{
}
static inline void dump_cred_dist_stats(struct htc_target *target)
{
}
#endif
#endif

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef HIF_OPS_H
#define HIF_OPS_H
#include "hif.h"
static inline int hif_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
u32 len, u32 request)
{
return ar->hif_ops->read_write_sync(ar, addr, buf, len, request);
}
static inline int hif_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
u32 length, u32 request,
struct htc_packet *packet)
{
return ar->hif_ops->write_async(ar, address, buffer, length,
request, packet);
}
static inline void ath6kl_hif_irq_enable(struct ath6kl *ar)
{
return ar->hif_ops->irq_enable(ar);
}
static inline void ath6kl_hif_irq_disable(struct ath6kl *ar)
{
return ar->hif_ops->irq_disable(ar);
}
static inline struct hif_scatter_req *hif_scatter_req_get(struct ath6kl *ar)
{
return ar->hif_ops->scatter_req_get(ar);
}
static inline void hif_scatter_req_add(struct ath6kl *ar,
struct hif_scatter_req *s_req)
{
return ar->hif_ops->scatter_req_add(ar, s_req);
}
static inline int ath6kl_hif_enable_scatter(struct ath6kl *ar,
struct hif_dev_scat_sup_info *info)
{
return ar->hif_ops->enable_scatter(ar, info);
}
static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar)
{
return ar->hif_ops->cleanup_scatter(ar);
}
#endif

View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef HIF_H
#define HIF_H
#include "common.h"
#include "core.h"
#include <linux/scatterlist.h>
#define BUS_REQUEST_MAX_NUM 64
#define HIF_MBOX_BLOCK_SIZE 128
#define HIF_MBOX0_BLOCK_SIZE 1
#define HIF_DMA_BUFFER_SIZE (32 * 1024)
#define CMD53_FIXED_ADDRESS 1
#define CMD53_INCR_ADDRESS 2
#define MAX_SCATTER_REQUESTS 4
#define MAX_SCATTER_ENTRIES_PER_REQ 16
#define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024)
#define MANUFACTURER_ID_AR6003_BASE 0x300
/* SDIO manufacturer ID and Codes */
#define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00
#define MANUFACTURER_CODE 0x271 /* Atheros */
/* Mailbox address in SDIO address space */
#define HIF_MBOX_BASE_ADDR 0x800
#define HIF_MBOX_WIDTH 0x800
#define HIF_MBOX_END_ADDR (HTC_MAILBOX_NUM_MAX * HIF_MBOX_WIDTH - 1)
/* version 1 of the chip has only a 12K extended mbox range */
#define HIF_MBOX0_EXT_BASE_ADDR 0x4000
#define HIF_MBOX0_EXT_WIDTH (12*1024)
/* GMBOX addresses */
#define HIF_GMBOX_BASE_ADDR 0x7000
#define HIF_GMBOX_WIDTH 0x4000
/* interrupt mode register */
#define CCCR_SDIO_IRQ_MODE_REG 0xF0
/* mode to enable special 4-bit interrupt assertion without clock */
#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ (1 << 0)
struct bus_request {
struct list_head list;
/* request data */
u32 address;
u8 *buffer;
u32 length;
u32 request;
struct htc_packet *packet;
int status;
/* this is a scatter request */
struct hif_scatter_req *scat_req;
};
/* direction of transfer (read/write) */
#define HIF_READ 0x00000001
#define HIF_WRITE 0x00000002
#define HIF_DIR_MASK (HIF_READ | HIF_WRITE)
/*
* emode - This indicates the whether the command is to be executed in a
* blocking or non-blocking fashion (HIF_SYNCHRONOUS/
* HIF_ASYNCHRONOUS). The read/write data paths in HTC have been
* implemented using the asynchronous mode allowing the the bus
* driver to indicate the completion of operation through the
* registered callback routine. The requirement primarily comes
* from the contexts these operations get called from (a driver's
* transmit context or the ISR context in case of receive).
* Support for both of these modes is essential.
*/
#define HIF_SYNCHRONOUS 0x00000010
#define HIF_ASYNCHRONOUS 0x00000020
#define HIF_EMODE_MASK (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS)
/*
* dmode - An interface may support different kinds of commands based on
* the tradeoff between the amount of data it can carry and the
* setup time. Byte and Block modes are supported (HIF_BYTE_BASIS/
* HIF_BLOCK_BASIS). In case of latter, the data is rounded off
* to the nearest block size by padding. The size of the block is
* configurable at compile time using the HIF_BLOCK_SIZE and is
* negotiated with the target during initialization after the
* ATH6KL interrupts are enabled.
*/
#define HIF_BYTE_BASIS 0x00000040
#define HIF_BLOCK_BASIS 0x00000080
#define HIF_DMODE_MASK (HIF_BYTE_BASIS | HIF_BLOCK_BASIS)
/*
* amode - This indicates if the address has to be incremented on ATH6KL
* after every read/write operation (HIF?FIXED_ADDRESS/
* HIF_INCREMENTAL_ADDRESS).
*/
#define HIF_FIXED_ADDRESS 0x00000100
#define HIF_INCREMENTAL_ADDRESS 0x00000200
#define HIF_AMODE_MASK (HIF_FIXED_ADDRESS | HIF_INCREMENTAL_ADDRESS)
#define HIF_WR_ASYNC_BYTE_INC \
(HIF_WRITE | HIF_ASYNCHRONOUS | \
HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
#define HIF_WR_ASYNC_BLOCK_INC \
(HIF_WRITE | HIF_ASYNCHRONOUS | \
HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
#define HIF_WR_SYNC_BYTE_FIX \
(HIF_WRITE | HIF_SYNCHRONOUS | \
HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
#define HIF_WR_SYNC_BYTE_INC \
(HIF_WRITE | HIF_SYNCHRONOUS | \
HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
#define HIF_WR_SYNC_BLOCK_INC \
(HIF_WRITE | HIF_SYNCHRONOUS | \
HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
#define HIF_RD_SYNC_BYTE_INC \
(HIF_READ | HIF_SYNCHRONOUS | \
HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
#define HIF_RD_SYNC_BYTE_FIX \
(HIF_READ | HIF_SYNCHRONOUS | \
HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
#define HIF_RD_ASYNC_BLOCK_FIX \
(HIF_READ | HIF_ASYNCHRONOUS | \
HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
#define HIF_RD_SYNC_BLOCK_FIX \
(HIF_READ | HIF_SYNCHRONOUS | \
HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
struct hif_scatter_item {
u8 *buf;
int len;
struct htc_packet *packet;
};
struct hif_scatter_req {
struct list_head list;
/* address for the read/write operation */
u32 addr;
/* request flags */
u32 req;
/* total length of entire transfer */
u32 len;
u32 flags;
void (*complete) (struct hif_scatter_req *);
int status;
struct htc_endpoint *ep;
int scat_entries;
struct hif_scatter_req_priv *req_priv;
/* bounce buffer for upper layers to copy to/from */
u8 *virt_dma_buf;
struct hif_scatter_item scat_list[1];
};
struct hif_dev_scat_sup_info {
int (*rw_scat_func) (struct ath6kl *ar, struct hif_scatter_req *);
int max_scat_entries;
int max_xfer_szper_scatreq;
};
struct hif_scatter_req_priv {
struct bus_request *busrequest;
struct scatterlist sgentries[MAX_SCATTER_ENTRIES_PER_REQ];
};
struct ath6kl_hif_ops {
int (*read_write_sync)(struct ath6kl *ar, u32 addr, u8 *buf,
u32 len, u32 request);
int (*write_async)(struct ath6kl *ar, u32 address, u8 *buffer,
u32 length, u32 request, struct htc_packet *packet);
void (*irq_enable)(struct ath6kl *ar);
void (*irq_disable)(struct ath6kl *ar);
struct hif_scatter_req *(*scatter_req_get)(struct ath6kl *ar);
void (*scatter_req_add)(struct ath6kl *ar,
struct hif_scatter_req *s_req);
int (*enable_scatter)(struct ath6kl *ar,
struct hif_dev_scat_sup_info *info);
void (*cleanup_scatter)(struct ath6kl *ar);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,596 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef HTC_H
#define HTC_H
#include "common.h"
/* frame header flags */
/* send direction */
#define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0)
#define HTC_FLAGS_SEND_BUNDLE (1 << 1)
/* receive direction */
#define HTC_FLG_RX_UNUSED (1 << 0)
#define HTC_FLG_RX_TRAILER (1 << 1)
/* Bundle count maske and shift */
#define HTC_FLG_RX_BNDL_CNT (0xF0)
#define HTC_FLG_RX_BNDL_CNT_S 4
#define HTC_HDR_LENGTH (sizeof(struct htc_frame_hdr))
#define HTC_MAX_PAYLOAD_LENGTH (4096 - sizeof(struct htc_frame_hdr))
/* HTC control message IDs */
#define HTC_MSG_READY_ID 1
#define HTC_MSG_CONN_SVC_ID 2
#define HTC_MSG_CONN_SVC_RESP_ID 3
#define HTC_MSG_SETUP_COMPLETE_ID 4
#define HTC_MSG_SETUP_COMPLETE_EX_ID 5
#define HTC_MAX_CTRL_MSG_LEN 256
#define HTC_VERSION_2P0 0x00
#define HTC_VERSION_2P1 0x01
#define HTC_SERVICE_META_DATA_MAX_LENGTH 128
#define HTC_CONN_FLGS_THRESH_LVL_QUAT 0x0
#define HTC_CONN_FLGS_THRESH_LVL_HALF 0x1
#define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2
#define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4
#define HTC_CONN_FLGS_THRESH_MASK 0x3
/* connect response status codes */
#define HTC_SERVICE_SUCCESS 0
#define HTC_SERVICE_NOT_FOUND 1
#define HTC_SERVICE_FAILED 2
/* no resources (i.e. no more endpoints) */
#define HTC_SERVICE_NO_RESOURCES 3
/* specific service is not allowing any more endpoints */
#define HTC_SERVICE_NO_MORE_EP 4
/* report record IDs */
#define HTC_RECORD_NULL 0
#define HTC_RECORD_CREDITS 1
#define HTC_RECORD_LOOKAHEAD 2
#define HTC_RECORD_LOOKAHEAD_BUNDLE 3
#define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0)
#define MAKE_SERVICE_ID(group, index) \
(int)(((int)group << 8) | (int)(index))
/* NOTE: service ID of 0x0000 is reserved and should never be used */
#define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1)
#define WMI_CONTROL_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0)
#define WMI_DATA_BE_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1)
#define WMI_DATA_BK_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2)
#define WMI_DATA_VI_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3)
#define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4)
#define WMI_MAX_SERVICES 5
/* reserved and used to flush ALL packets */
#define HTC_TX_PACKET_TAG_ALL 0
#define HTC_SERVICE_TX_PACKET_TAG 1
#define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_SERVICE_TX_PACKET_TAG + 9)
/* more packets on this endpoint are being fetched */
#define HTC_RX_FLAGS_INDICATE_MORE_PKTS (1 << 0)
/* TODO.. for BMI */
#define ENDPOINT1 0
/* TODO -remove me, but we have to fix BMI first */
#define HTC_MAILBOX_NUM_MAX 4
/* enable send bundle padding for this endpoint */
#define HTC_FLGS_TX_BNDL_PAD_EN (1 << 0)
#define HTC_EP_ACTIVE ((u32) (1u << 31))
/* HTC operational parameters */
#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */
#define HTC_TARGET_DEBUG_INTR_MASK 0x01
#define HTC_TARGET_CREDIT_INTR_MASK 0xF0
#define HTC_HOST_MAX_MSG_PER_BUNDLE 8
#define HTC_MIN_HTC_MSGS_TO_BUNDLE 2
/* packet flags */
#define HTC_RX_PKT_IGNORE_LOOKAHEAD (1 << 0)
#define HTC_RX_PKT_REFRESH_HDR (1 << 1)
#define HTC_RX_PKT_PART_OF_BUNDLE (1 << 2)
#define HTC_RX_PKT_NO_RECYCLE (1 << 3)
/* scatter request flags */
#define HTC_SCAT_REQ_FLG_PART_BNDL (1 << 0)
#define NUM_CONTROL_BUFFERS 8
#define NUM_CONTROL_TX_BUFFERS 2
#define NUM_CONTROL_RX_BUFFERS (NUM_CONTROL_BUFFERS - NUM_CONTROL_TX_BUFFERS)
#define HTC_RECV_WAIT_BUFFERS (1 << 0)
#define HTC_OP_STATE_STOPPING (1 << 0)
/*
* The frame header length and message formats defined herein were selected
* to accommodate optimal alignment for target processing. This reduces
* code size and improves performance. Any changes to the header length may
* alter the alignment and cause exceptions on the target. When adding to
* the messagestructures insure that fields are properly aligned.
*/
/* HTC frame header
*
* NOTE: do not remove or re-arrange the fields, these are minimally
* required to take advantage of 4-byte lookaheads in some hardware
* implementations.
*/
struct htc_frame_hdr {
u8 eid;
u8 flags;
/* length of data (including trailer) that follows the header */
__le16 payld_len;
/* end of 4-byte lookahead */
u8 ctrl[2];
} __packed;
/* HTC ready message */
struct htc_ready_msg {
__le16 msg_id;
__le16 cred_cnt;
__le16 cred_sz;
u8 max_ep;
u8 pad;
} __packed;
/* extended HTC ready message */
struct htc_ready_ext_msg {
struct htc_ready_msg ver2_0_info;
u8 htc_ver;
u8 msg_per_htc_bndl;
} __packed;
/* connect service */
struct htc_conn_service_msg {
__le16 msg_id;
__le16 svc_id;
__le16 conn_flags;
u8 svc_meta_len;
u8 pad;
} __packed;
/* connect response */
struct htc_conn_service_resp {
__le16 msg_id;
__le16 svc_id;
u8 status;
u8 eid;
__le16 max_msg_sz;
u8 svc_meta_len;
u8 pad;
} __packed;
struct htc_setup_comp_msg {
__le16 msg_id;
} __packed;
/* extended setup completion message */
struct htc_setup_comp_ext_msg {
__le16 msg_id;
__le32 flags;
u8 msg_per_rxbndl;
u8 Rsvd[3];
} __packed;
struct htc_record_hdr {
u8 rec_id;
u8 len;
} __packed;
struct htc_credit_report {
u8 eid;
u8 credits;
} __packed;
/*
* NOTE: The lk_ahd array is guarded by a pre_valid
* and Post Valid guard bytes. The pre_valid bytes must
* equal the inverse of the post_valid byte.
*/
struct htc_lookahead_report {
u8 pre_valid;
u8 lk_ahd[4];
u8 post_valid;
} __packed;
struct htc_bundle_lkahd_rpt {
u8 lk_ahd[4];
} __packed;
/* Current service IDs */
enum htc_service_grp_ids {
RSVD_SERVICE_GROUP = 0,
WMI_SERVICE_GROUP = 1,
HTC_TEST_GROUP = 254,
HTC_SERVICE_GROUP_LAST = 255
};
/* ------ endpoint IDS ------ */
enum htc_endpoint_id {
ENDPOINT_UNUSED = -1,
ENDPOINT_0 = 0,
ENDPOINT_1 = 1,
ENDPOINT_2 = 2,
ENDPOINT_3,
ENDPOINT_4,
ENDPOINT_5,
ENDPOINT_6,
ENDPOINT_7,
ENDPOINT_8,
ENDPOINT_MAX,
};
struct htc_tx_packet_info {
u16 tag;
int cred_used;
u8 flags;
int seqno;
};
struct htc_rx_packet_info {
u32 exp_hdr;
u32 rx_flags;
u32 indicat_flags;
};
struct htc_target;
/* wrapper around endpoint-specific packets */
struct htc_packet {
struct list_head list;
/* caller's per packet specific context */
void *pkt_cntxt;
/*
* the true buffer start , the caller can store the real
* buffer start here. In receive callbacks, the HTC layer
* sets buf to the start of the payload past the header.
* This field allows the caller to reset buf when it recycles
* receive packets back to HTC.
*/
u8 *buf_start;
/*
* Pointer to the start of the buffer. In the transmit
* direction this points to the start of the payload. In the
* receive direction, however, the buffer when queued up
* points to the start of the HTC header but when returned
* to the caller points to the start of the payload
*/
u8 *buf;
u32 buf_len;
/* actual length of payload */
u32 act_len;
/* endpoint that this packet was sent/recv'd from */
enum htc_endpoint_id endpoint;
/* completion status */
int status;
union {
struct htc_tx_packet_info tx;
struct htc_rx_packet_info rx;
} info;
void (*completion) (struct htc_target *, struct htc_packet *);
struct htc_target *context;
};
enum htc_send_full_action {
HTC_SEND_FULL_KEEP = 0,
HTC_SEND_FULL_DROP = 1,
};
struct htc_ep_callbacks {
void (*rx) (struct htc_target *, struct htc_packet *);
void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint);
enum htc_send_full_action (*tx_full) (struct htc_target *,
struct htc_packet *);
struct htc_packet *(*rx_allocthresh) (struct htc_target *,
enum htc_endpoint_id, int);
int rx_alloc_thresh;
int rx_refill_thresh;
};
/* service connection information */
struct htc_service_connect_req {
u16 svc_id;
u16 conn_flags;
struct htc_ep_callbacks ep_cb;
int max_txq_depth;
u32 flags;
unsigned int max_rxmsg_sz;
};
/* service connection response information */
struct htc_service_connect_resp {
u8 buf_len;
u8 act_len;
enum htc_endpoint_id endpoint;
unsigned int len_max;
u8 resp_code;
};
/* endpoint distributionstructure */
struct htc_endpoint_credit_dist {
struct list_head list;
/* Service ID (set by HTC) */
u16 svc_id;
/* endpoint for this distributionstruct (set by HTC) */
enum htc_endpoint_id endpoint;
u32 dist_flags;
/*
* credits for normal operation, anything above this
* indicates the endpoint is over-subscribed.
*/
int cred_norm;
/* floor for credit distribution */
int cred_min;
int cred_assngd;
/* current credits available */
int credits;
/*
* pending credits to distribute on this endpoint, this
* is set by HTC when credit reports arrive. The credit
* distribution functions sets this to zero when it distributes
* the credits.
*/
int cred_to_dist;
/*
* the number of credits that the current pending TX packet needs
* to transmit. This is set by HTC when endpoint needs credits in
* order to transmit.
*/
int seek_cred;
/* size in bytes of each credit */
int cred_sz;
/* credits required for a maximum sized messages */
int cred_per_msg;
/* reserved for HTC use */
void *htc_rsvd;
/*
* current depth of TX queue , i.e. messages waiting for credits
* This field is valid only when HTC_CREDIT_DIST_ACTIVITY_CHANGE
* or HTC_CREDIT_DIST_SEND_COMPLETE is indicated on an endpoint
* that has non-zero credits to recover.
*/
int txq_depth;
};
/*
* credit distibution code that is passed into the distrbution function,
* there are mandatory and optional codes that must be handled
*/
enum htc_credit_dist_reason {
HTC_CREDIT_DIST_SEND_COMPLETE = 0,
HTC_CREDIT_DIST_ACTIVITY_CHANGE = 1,
HTC_CREDIT_DIST_SEEK_CREDITS,
};
struct htc_credit_state_info {
int total_avail_credits;
int cur_free_credits;
struct list_head lowestpri_ep_dist;
};
/* endpoint statistics */
struct htc_endpoint_stats {
/*
* number of times the host set the credit-low flag in a send
* message on this endpoint
*/
u32 cred_low_indicate;
u32 tx_issued;
u32 tx_pkt_bundled;
u32 tx_bundles;
u32 tx_dropped;
/* running count of total credit reports received for this endpoint */
u32 tx_cred_rpt;
/* credit reports received from this endpoint's RX packets */
u32 cred_rpt_from_rx;
/* credit reports received from RX packets of other endpoints */
u32 cred_rpt_from_other;
/* credit reports received from endpoint 0 RX packets */
u32 cred_rpt_ep0;
/* count of credits received via Rx packets on this endpoint */
u32 cred_from_rx;
/* count of credits received via another endpoint */
u32 cred_from_other;
/* count of credits received via another endpoint */
u32 cred_from_ep0;
/* count of consummed credits */
u32 cred_cosumd;
/* count of credits returned */
u32 cred_retnd;
u32 rx_pkts;
/* count of lookahead records found in Rx msg */
u32 rx_lkahds;
/* count of recv packets received in a bundle */
u32 rx_bundl;
/* count of number of bundled lookaheads */
u32 rx_bundle_lkahd;
/* count of the number of bundle indications from the HTC header */
u32 rx_bundle_from_hdr;
/* the number of times the recv allocation threshold was hit */
u32 rx_alloc_thresh_hit;
/* total number of bytes */
u32 rxalloc_thresh_byte;
};
struct htc_endpoint {
enum htc_endpoint_id eid;
u16 svc_id;
struct list_head txq;
struct list_head rx_bufq;
struct htc_endpoint_credit_dist cred_dist;
struct htc_ep_callbacks ep_cb;
int max_txq_depth;
int len_max;
int tx_proc_cnt;
int rx_proc_cnt;
struct htc_target *target;
u8 seqno;
u32 conn_flags;
struct htc_endpoint_stats ep_st;
};
struct htc_control_buffer {
struct htc_packet packet;
u8 *buf;
};
struct ath6kl_device;
/* our HTC target state */
struct htc_target {
struct htc_endpoint endpoint[ENDPOINT_MAX];
struct list_head cred_dist_list;
struct list_head free_ctrl_txbuf;
struct list_head free_ctrl_rxbuf;
struct htc_credit_state_info *cred_dist_cntxt;
int tgt_creds;
unsigned int tgt_cred_sz;
spinlock_t htc_lock;
spinlock_t rx_lock;
spinlock_t tx_lock;
struct ath6kl_device *dev;
u32 htc_flags;
u32 rx_st_flags;
enum htc_endpoint_id ep_waiting;
u8 htc_tgt_ver;
/* max messages per bundle for HTC */
int msg_per_bndl_max;
bool tx_bndl_enable;
int rx_bndl_enable;
};
void *htc_create(struct ath6kl *ar);
void htc_set_credit_dist(struct htc_target *target,
struct htc_credit_state_info *cred_info,
u16 svc_pri_order[], int len);
int htc_wait_target(struct htc_target *target);
int htc_start(struct htc_target *target);
int htc_conn_service(struct htc_target *target,
struct htc_service_connect_req *req,
struct htc_service_connect_resp *resp);
int htc_tx(struct htc_target *target, struct htc_packet *packet);
void htc_stop(struct htc_target *target);
void htc_cleanup(struct htc_target *target);
void htc_flush_txep(struct htc_target *target,
enum htc_endpoint_id endpoint, u16 tag);
void htc_flush_rx_buf(struct htc_target *target);
void htc_indicate_activity_change(struct htc_target *target,
enum htc_endpoint_id endpoint, bool active);
int htc_get_rxbuf_num(struct htc_target *target, enum htc_endpoint_id endpoint);
int htc_add_rxbuf_multiple(struct htc_target *target, struct list_head *pktq);
static inline void set_htc_pkt_info(struct htc_packet *packet, void *context,
u8 *buf, unsigned int len,
enum htc_endpoint_id eid, u16 tag)
{
packet->pkt_cntxt = context;
packet->buf = buf;
packet->act_len = len;
packet->endpoint = eid;
packet->info.tx.tag = tag;
}
static inline void htc_rxpkt_reset(struct htc_packet *packet)
{
packet->buf = packet->buf_start;
packet->act_len = 0;
}
static inline void set_htc_rxpkt_info(struct htc_packet *packet, void *context,
u8 *buf, unsigned long len,
enum htc_endpoint_id eid)
{
packet->pkt_cntxt = context;
packet->buf = buf;
packet->buf_start = buf;
packet->buf_len = len;
packet->endpoint = eid;
}
static inline int get_queue_depth(struct list_head *queue)
{
struct list_head *tmp_list;
int depth = 0;
list_for_each(tmp_list, queue)
depth++;
return depth;
}
#endif

View File

@ -0,0 +1,811 @@
/*
* Copyright (c) 2007-2011 Atheros Communications Inc.
*
* 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.
*/
#include "core.h"
#include "target.h"
#include "hif-ops.h"
#include "htc_hif.h"
#include "debug.h"
#define MAILBOX_FOR_BLOCK_SIZE 1
#define ATH6KL_TIME_QUANTUM 10 /* in ms */
static void ath6kl_add_io_pkt(struct ath6kl_device *dev,
struct htc_packet *packet)
{
spin_lock_bh(&dev->lock);
list_add_tail(&packet->list, &dev->reg_io);
spin_unlock_bh(&dev->lock);
}
static struct htc_packet *ath6kl_get_io_pkt(struct ath6kl_device *dev)
{
struct htc_packet *packet = NULL;
spin_lock_bh(&dev->lock);
if (!list_empty(&dev->reg_io)) {
packet = list_first_entry(&dev->reg_io,
struct htc_packet, list);
list_del(&packet->list);
}
spin_unlock_bh(&dev->lock);
return packet;
}
static int ath6kldev_cp_scat_dma_buf(struct hif_scatter_req *req, bool from_dma)
{
u8 *buf;
int i;
buf = req->virt_dma_buf;
for (i = 0; i < req->scat_entries; i++) {
if (from_dma)
memcpy(req->scat_list[i].buf, buf,
req->scat_list[i].len);
else
memcpy(buf, req->scat_list[i].buf,
req->scat_list[i].len);
buf += req->scat_list[i].len;
}
return 0;
}
int ath6kldev_rw_comp_handler(void *context, int status)
{
struct htc_packet *packet = context;
ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
"ath6kldev_rw_comp_handler (pkt:0x%p , status: %d\n",
packet, status);
packet->status = status;
packet->completion(packet->context, packet);
return 0;
}
static int ath6kldev_proc_dbg_intr(struct ath6kl_device *dev)
{
u32 dummy;
int status;
ath6kl_err("target debug interrupt\n");
ath6kl_target_failure(dev->ar);
/*
* read counter to clear the interrupt, the debug error interrupt is
* counter 0.
*/
status = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
(u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
if (status)
WARN_ON(1);
return status;
}
/* mailbox recv message polling */
int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
int timeout)
{
struct ath6kl_irq_proc_registers *rg;
int status = 0, i;
u8 htc_mbox = 1 << HTC_MAILBOX;
for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) {
/* this is the standard HIF way, load the reg table */
status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
(u8 *) &dev->irq_proc_reg,
sizeof(dev->irq_proc_reg),
HIF_RD_SYNC_BYTE_INC);
if (status) {
ath6kl_err("failed to read reg table\n");
return status;
}
/* check for MBOX data and valid lookahead */
if (dev->irq_proc_reg.host_int_status & htc_mbox) {
if (dev->irq_proc_reg.rx_lkahd_valid &
htc_mbox) {
/*
* Mailbox has a message and the look ahead
* is valid.
*/
rg = &dev->irq_proc_reg;
*lk_ahd =
le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
break;
}
}
/* delay a little */
mdelay(ATH6KL_TIME_QUANTUM);
ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "retry mbox poll : %d\n", i);
}
if (i == 0) {
ath6kl_err("timeout waiting for recv message\n");
status = -ETIME;
/* check if the target asserted */
if (dev->irq_proc_reg.counter_int_status &
ATH6KL_TARGET_DEBUG_INTR_MASK)
/*
* Target failure handler will be called in case of
* an assert.
*/
ath6kldev_proc_dbg_intr(dev);
}
return status;
}
/*
* Disable packet reception (used in case the host runs out of buffers)
* using the interrupt enable registers through the host I/F
*/
int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx)
{
struct ath6kl_irq_enable_reg regs;
int status = 0;
/* take the lock to protect interrupt enable shadows */
spin_lock_bh(&dev->lock);
if (enable_rx)
dev->irq_en_reg.int_status_en |=
SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
else
dev->irq_en_reg.int_status_en &=
~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
spin_unlock_bh(&dev->lock);
status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
&regs.int_status_en,
sizeof(struct ath6kl_irq_enable_reg),
HIF_WR_SYNC_BYTE_INC);
return status;
}
static void ath6kldev_rw_async_handler(struct htc_target *target,
struct htc_packet *packet)
{
struct ath6kl_device *dev = target->dev;
struct hif_scatter_req *req = packet->pkt_cntxt;
req->status = packet->status;
ath6kl_add_io_pkt(dev, packet);
req->complete(req);
}
static int ath6kldev_rw_scatter(struct ath6kl *ar, struct hif_scatter_req *req)
{
struct ath6kl_device *dev = ar->htc_target->dev;
struct htc_packet *packet = NULL;
int status = 0;
u32 request = req->req;
u8 *virt_dma_buf;
if (!req->len)
return 0;
if (request & HIF_ASYNCHRONOUS) {
/* use an I/O packet to carry this request */
packet = ath6kl_get_io_pkt(dev);
if (!packet) {
status = -ENOMEM;
goto out;
}
packet->pkt_cntxt = req;
packet->completion = ath6kldev_rw_async_handler;
packet->context = ar->htc_target;
}
virt_dma_buf = req->virt_dma_buf;
if (request & HIF_ASYNCHRONOUS)
status = hif_write_async(dev->ar, req->addr, virt_dma_buf,
req->len, request, packet);
else
status = hif_read_write_sync(dev->ar, req->addr, virt_dma_buf,
req->len, request);
out:
if (status)
if (request & HIF_ASYNCHRONOUS) {
if (packet != NULL)
ath6kl_add_io_pkt(dev, packet);
req->status = status;
req->complete(req);
status = 0;
}
return status;
}
int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
struct hif_scatter_req *scat_req, bool read)
{
int status = 0;
if (read) {
scat_req->req = HIF_RD_SYNC_BLOCK_FIX;
scat_req->addr = dev->ar->mbox_info.htc_addr;
} else {
scat_req->req = HIF_WR_ASYNC_BLOCK_INC;
scat_req->addr =
(scat_req->len > HIF_MBOX_WIDTH) ?
dev->ar->mbox_info.htc_ext_addr :
dev->ar->mbox_info.htc_addr;
}
ath6kl_dbg((ATH6KL_DBG_HTC_RECV | ATH6KL_DBG_HTC_SEND),
"ath6kldev_submit_scat_req, entries: %d, total len: %d mbox:0x%X (mode: %s : %s)\n",
scat_req->scat_entries, scat_req->len,
scat_req->addr, !read ? "async" : "sync",
(read) ? "rd" : "wr");
if (!read && dev->virt_scat)
status = ath6kldev_cp_scat_dma_buf(scat_req, false);
if (status) {
if (!read) {
scat_req->status = status;
scat_req->complete(scat_req);
return 0;
}
return status;
}
status = dev->hif_scat_info.rw_scat_func(dev->ar, scat_req);
if (read) {
/* in sync mode, we can touch the scatter request */
scat_req->status = status;
if (!status && dev->virt_scat)
scat_req->status =
ath6kldev_cp_scat_dma_buf(scat_req, true);
}
return status;
}
/*
* function to set up virtual scatter support if HIF
* layer has not implemented the interface.
*/
static int ath6kldev_setup_virt_scat_sup(struct ath6kl_device *dev)
{
struct hif_scatter_req *scat_req;
int buf_sz, scat_req_sz, scat_list_sz;
int i, status = 0;
u8 *virt_dma_buf;
buf_sz = 2 * L1_CACHE_BYTES + ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
scat_list_sz = (ATH6KL_SCATTER_ENTRIES_PER_REQ - 1) *
sizeof(struct hif_scatter_item);
scat_req_sz = sizeof(*scat_req) + scat_list_sz;
for (i = 0; i < ATH6KL_SCATTER_REQS; i++) {
scat_req = kzalloc(scat_req_sz, GFP_KERNEL);
if (!scat_req) {
status = -ENOMEM;
break;
}
virt_dma_buf = kzalloc(buf_sz, GFP_KERNEL);
if (!virt_dma_buf) {
kfree(scat_req);
status = -ENOMEM;
break;
}
scat_req->virt_dma_buf =
(u8 *)L1_CACHE_ALIGN((unsigned long)virt_dma_buf);
/* we emulate a DMA bounce interface */
hif_scatter_req_add(dev->ar, scat_req);
}
if (status)
ath6kl_hif_cleanup_scatter(dev->ar);
else {
dev->hif_scat_info.rw_scat_func = ath6kldev_rw_scatter;
dev->hif_scat_info.max_scat_entries =
ATH6KL_SCATTER_ENTRIES_PER_REQ;
dev->hif_scat_info.max_xfer_szper_scatreq =
ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
dev->virt_scat = true;
}
return status;
}
int ath6kldev_setup_msg_bndl(struct ath6kl_device *dev, int max_msg_per_trans)
{
int status;
status = ath6kl_hif_enable_scatter(dev->ar, &dev->hif_scat_info);
if (status) {
ath6kl_warn("hif does not support scatter requests (%d)\n",
status);
/* we can try to use a virtual DMA scatter mechanism */
status = ath6kldev_setup_virt_scat_sup(dev);
}
if (!status)
ath6kl_dbg(ATH6KL_DBG_ANY, "max scatter items:%d: maxlen:%d\n",
dev->hif_scat_info.max_scat_entries,
dev->hif_scat_info.max_xfer_szper_scatreq);
return status;
}
static int ath6kldev_proc_counter_intr(struct ath6kl_device *dev)
{
u8 counter_int_status;
ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n");
counter_int_status = dev->irq_proc_reg.counter_int_status &
dev->irq_en_reg.cntr_int_status_en;
ath6kl_dbg(ATH6KL_DBG_IRQ,
"valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
counter_int_status);
/*
* NOTE: other modules like GMBOX may use the counter interrupt for
* credit flow control on other counters, we only need to check for
* the debug assertion counter interrupt.
*/
if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK)
return ath6kldev_proc_dbg_intr(dev);
return 0;
}
static int ath6kldev_proc_err_intr(struct ath6kl_device *dev)
{
int status;
u8 error_int_status;
u8 reg_buf[4];
ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n");
error_int_status = dev->irq_proc_reg.error_int_status & 0x0F;
if (!error_int_status) {
WARN_ON(1);
return -EIO;
}
ath6kl_dbg(ATH6KL_DBG_IRQ,
"valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
error_int_status);
if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status))
ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n");
if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status))
ath6kl_err("rx underflow\n");
if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status))
ath6kl_err("tx overflow\n");
/* Clear the interrupt */
dev->irq_proc_reg.error_int_status &= ~error_int_status;
/* set W1C value to clear the interrupt, this hits the register first */
reg_buf[0] = error_int_status;
reg_buf[1] = 0;
reg_buf[2] = 0;
reg_buf[3] = 0;
status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS,
reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
if (status)
WARN_ON(1);
return status;
}
static int ath6kldev_proc_cpu_intr(struct ath6kl_device *dev)
{
int status;
u8 cpu_int_status;
u8 reg_buf[4];
ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n");
cpu_int_status = dev->irq_proc_reg.cpu_int_status &
dev->irq_en_reg.cpu_int_status_en;
if (!cpu_int_status) {
WARN_ON(1);
return -EIO;
}
ath6kl_dbg(ATH6KL_DBG_IRQ,
"valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
cpu_int_status);
/* Clear the interrupt */
dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status;
/*
* Set up the register transfer buffer to hit the register 4 times ,
* this is done to make the access 4-byte aligned to mitigate issues
* with host bus interconnects that restrict bus transfer lengths to
* be a multiple of 4-bytes.
*/
/* set W1C value to clear the interrupt, this hits the register first */
reg_buf[0] = cpu_int_status;
/* the remaining are set to zero which have no-effect */
reg_buf[1] = 0;
reg_buf[2] = 0;
reg_buf[3] = 0;
status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS,
reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
if (status)
WARN_ON(1);
return status;
}
/* process pending interrupts synchronously */
static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
{
struct ath6kl_irq_proc_registers *rg;
int status = 0;
u8 host_int_status = 0;
u32 lk_ahd = 0;
u8 htc_mbox = 1 << HTC_MAILBOX;
ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev);
/*
* NOTE: HIF implementation guarantees that the context of this
* call allows us to perform SYNCHRONOUS I/O, that is we can block,
* sleep or call any API that can block or switch thread/task
* contexts. This is a fully schedulable context.
*/
/*
* Process pending intr only when int_status_en is clear, it may
* result in unnecessary bus transaction otherwise. Target may be
* unresponsive at the time.
*/
if (dev->irq_en_reg.int_status_en) {
/*
* Read the first 28 bytes of the HTC register table. This
* will yield us the value of different int status
* registers and the lookahead registers.
*
* length = sizeof(int_status) + sizeof(cpu_int_status)
* + sizeof(error_int_status) +
* sizeof(counter_int_status) +
* sizeof(mbox_frame) + sizeof(rx_lkahd_valid)
* + sizeof(hole) + sizeof(rx_lkahd) +
* sizeof(int_status_en) +
* sizeof(cpu_int_status_en) +
* sizeof(err_int_status_en) +
* sizeof(cntr_int_status_en);
*/
status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
(u8 *) &dev->irq_proc_reg,
sizeof(dev->irq_proc_reg),
HIF_RD_SYNC_BYTE_INC);
if (status)
goto out;
if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ))
ath6kl_dump_registers(dev, &dev->irq_proc_reg,
&dev->irq_en_reg);
/* Update only those registers that are enabled */
host_int_status = dev->irq_proc_reg.host_int_status &
dev->irq_en_reg.int_status_en;
/* Look at mbox status */
if (host_int_status & htc_mbox) {
/*
* Mask out pending mbox value, we use "lookAhead as
* the real flag for mbox processing.
*/
host_int_status &= ~htc_mbox;
if (dev->irq_proc_reg.rx_lkahd_valid &
htc_mbox) {
rg = &dev->irq_proc_reg;
lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
if (!lk_ahd)
ath6kl_err("lookAhead is zero!\n");
}
}
}
if (!host_int_status && !lk_ahd) {
*done = true;
goto out;
}
if (lk_ahd) {
int fetched = 0;
ath6kl_dbg(ATH6KL_DBG_IRQ,
"pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd);
/*
* Mailbox Interrupt, the HTC layer may issue async
* requests to empty the mailbox. When emptying the recv
* mailbox we use the async handler above called from the
* completion routine of the callers read request. This can
* improve performance by reducing context switching when
* we rapidly pull packets.
*/
status = dev->msg_pending(dev->htc_cnxt, &lk_ahd, &fetched);
if (status)
goto out;
if (!fetched)
/*
* HTC could not pull any messages out due to lack
* of resources.
*/
dev->chk_irq_status_cnt = 0;
}
/* now handle the rest of them */
ath6kl_dbg(ATH6KL_DBG_IRQ,
"valid interrupt source(s) for other interrupts: 0x%x\n",
host_int_status);
if (MS(HOST_INT_STATUS_CPU, host_int_status)) {
/* CPU Interrupt */
status = ath6kldev_proc_cpu_intr(dev);
if (status)
goto out;
}
if (MS(HOST_INT_STATUS_ERROR, host_int_status)) {
/* Error Interrupt */
status = ath6kldev_proc_err_intr(dev);
if (status)
goto out;
}
if (MS(HOST_INT_STATUS_COUNTER, host_int_status))
/* Counter Interrupt */
status = ath6kldev_proc_counter_intr(dev);
out:
/*
* An optimization to bypass reading the IRQ status registers
* unecessarily which can re-wake the target, if upper layers
* determine that we are in a low-throughput mode, we can rely on
* taking another interrupt rather than re-checking the status
* registers which can re-wake the target.
*
* NOTE : for host interfaces that makes use of detecting pending
* mbox messages at hif can not use this optimization due to
* possible side effects, SPI requires the host to drain all
* messages from the mailbox before exiting the ISR routine.
*/
ath6kl_dbg(ATH6KL_DBG_IRQ,
"bypassing irq status re-check, forcing done\n");
*done = true;
ath6kl_dbg(ATH6KL_DBG_IRQ,
"proc_pending_irqs: (done:%d, status=%d\n", *done, status);
return status;
}
/* interrupt handler, kicks off all interrupt processing */
int ath6kldev_intr_bh_handler(struct ath6kl *ar)
{
struct ath6kl_device *dev = ar->htc_target->dev;
int status = 0;
bool done = false;
/*
* Reset counter used to flag a re-scan of IRQ status registers on
* the target.
*/
dev->chk_irq_status_cnt = 0;
/*
* IRQ processing is synchronous, interrupt status registers can be
* re-read.
*/
while (!done) {
status = proc_pending_irqs(dev, &done);
if (status)
break;
}
return status;
}
static int ath6kldev_enable_intrs(struct ath6kl_device *dev)
{
struct ath6kl_irq_enable_reg regs;
int status;
spin_lock_bh(&dev->lock);
/* Enable all but ATH6KL CPU interrupts */
dev->irq_en_reg.int_status_en =
SM(INT_STATUS_ENABLE_ERROR, 0x01) |
SM(INT_STATUS_ENABLE_CPU, 0x01) |
SM(INT_STATUS_ENABLE_COUNTER, 0x01);
/*
* NOTE: There are some cases where HIF can do detection of
* pending mbox messages which is disabled now.
*/
dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
/* Set up the CPU Interrupt status Register */
dev->irq_en_reg.cpu_int_status_en = 0;
/* Set up the Error Interrupt status Register */
dev->irq_en_reg.err_int_status_en =
SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) |
SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1);
/*
* Enable Counter interrupt status register to get fatal errors for
* debugging.
*/
dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT,
ATH6KL_TARGET_DEBUG_INTR_MASK);
memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
spin_unlock_bh(&dev->lock);
status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
&regs.int_status_en, sizeof(regs),
HIF_WR_SYNC_BYTE_INC);
if (status)
ath6kl_err("failed to update interrupt ctl reg err: %d\n",
status);
return status;
}
int ath6kldev_disable_intrs(struct ath6kl_device *dev)
{
struct ath6kl_irq_enable_reg regs;
spin_lock_bh(&dev->lock);
/* Disable all interrupts */
dev->irq_en_reg.int_status_en = 0;
dev->irq_en_reg.cpu_int_status_en = 0;
dev->irq_en_reg.err_int_status_en = 0;
dev->irq_en_reg.cntr_int_status_en = 0;
memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
spin_unlock_bh(&dev->lock);
return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
&regs.int_status_en, sizeof(regs),
HIF_WR_SYNC_BYTE_INC);
}
/* enable device interrupts */
int ath6kldev_unmask_intrs(struct ath6kl_device *dev)
{
int status = 0;
/*
* Make sure interrupt are disabled before unmasking at the HIF
* layer. The rationale here is that between device insertion
* (where we clear the interrupts the first time) and when HTC
* is finally ready to handle interrupts, other software can perform
* target "soft" resets. The ATH6KL interrupt enables reset back to an
* "enabled" state when this happens.
*/
ath6kldev_disable_intrs(dev);
/* unmask the host controller interrupts */
ath6kl_hif_irq_enable(dev->ar);
status = ath6kldev_enable_intrs(dev);
return status;
}
/* disable all device interrupts */
int ath6kldev_mask_intrs(struct ath6kl_device *dev)
{
/*
* Mask the interrupt at the HIF layer to avoid any stray interrupt
* taken while we zero out our shadow registers in
* ath6kldev_disable_intrs().
*/
ath6kl_hif_irq_disable(dev->ar);
return ath6kldev_disable_intrs(dev);
}
int ath6kldev_setup(struct ath6kl_device *dev)
{
int status = 0;
int i;
struct htc_packet *packet;
/* initialize our free list of IO packets */
INIT_LIST_HEAD(&dev->reg_io);
spin_lock_init(&dev->lock);
/* carve up register I/O packets (these are for ASYNC register I/O ) */
for (i = 0; i < ATH6KL_MAX_REG_IO_BUFFERS; i++) {
packet = &dev->reg_io_buf[i].packet;
set_htc_rxpkt_info(packet, dev, dev->reg_io_buf[i].buf,
ATH6KL_REG_IO_BUFFER_SIZE, 0);
ath6kl_add_io_pkt(dev, packet);
}
/*
* NOTE: we actually get the block size of a mailbox other than 0,
* for SDIO the block size on mailbox 0 is artificially set to 1.
* So we use the block size that is set for the other 3 mailboxes.
*/
dev->block_sz = dev->ar->mbox_info.block_size;
/* must be a power of 2 */
if ((dev->block_sz & (dev->block_sz - 1)) != 0) {
WARN_ON(1);
goto fail_setup;
}
/* assemble mask, used for padding to a block */
dev->block_mask = dev->block_sz - 1;
ath6kl_dbg(ATH6KL_DBG_TRC, "block size: %d, mbox addr:0x%X\n",
dev->block_sz, dev->ar->mbox_info.htc_addr);
ath6kl_dbg(ATH6KL_DBG_TRC,
"hif interrupt processing is sync only\n");
status = ath6kldev_disable_intrs(dev);
fail_setup:
return status;
}

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2007-2011 Atheros Communications Inc.
*
* 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.
*/
#ifndef HTC_HIF_H
#define HTC_HIF_H
#include "htc.h"
#include "hif.h"
#define ATH6KL_MAILBOXES 4
/* HTC runs over mailbox 0 */
#define HTC_MAILBOX 0
#define ATH6KL_TARGET_DEBUG_INTR_MASK 0x01
#define OTHER_INTS_ENABLED (INT_STATUS_ENABLE_ERROR_MASK | \
INT_STATUS_ENABLE_CPU_MASK | \
INT_STATUS_ENABLE_COUNTER_MASK)
#define ATH6KL_REG_IO_BUFFER_SIZE 32
#define ATH6KL_MAX_REG_IO_BUFFERS 8
#define ATH6KL_SCATTER_ENTRIES_PER_REQ 16
#define ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER (16 * 1024)
#define ATH6KL_SCATTER_REQS 4
#ifndef A_CACHE_LINE_PAD
#define A_CACHE_LINE_PAD 128
#endif
#define ATH6KL_MIN_SCATTER_ENTRIES_PER_REQ 2
#define ATH6KL_MIN_TRANSFER_SIZE_PER_SCATTER (4 * 1024)
struct ath6kl_irq_proc_registers {
u8 host_int_status;
u8 cpu_int_status;
u8 error_int_status;
u8 counter_int_status;
u8 mbox_frame;
u8 rx_lkahd_valid;
u8 host_int_status2;
u8 gmbox_rx_avail;
__le32 rx_lkahd[2];
__le32 rx_gmbox_lkahd_alias[2];
} __packed;
struct ath6kl_irq_enable_reg {
u8 int_status_en;
u8 cpu_int_status_en;
u8 err_int_status_en;
u8 cntr_int_status_en;
} __packed;
/* buffers for ASYNC I/O */
struct ath6kl_async_reg_io_buffer {
struct htc_packet packet;
u8 pad1[A_CACHE_LINE_PAD];
/* cache-line safe with pads around */
u8 buf[ATH6KL_REG_IO_BUFFER_SIZE];
u8 pad2[A_CACHE_LINE_PAD];
};
struct ath6kl_device {
spinlock_t lock;
u8 pad1[A_CACHE_LINE_PAD];
struct ath6kl_irq_proc_registers irq_proc_reg;
u8 pad2[A_CACHE_LINE_PAD];
struct ath6kl_irq_enable_reg irq_en_reg;
u8 pad3[A_CACHE_LINE_PAD];
u32 block_sz;
u32 block_mask;
struct htc_target *htc_cnxt;
struct list_head reg_io;
struct ath6kl_async_reg_io_buffer reg_io_buf[ATH6KL_MAX_REG_IO_BUFFERS];
int (*msg_pending) (struct htc_target *target, u32 lk_ahds[],
int *npkts_fetched);
struct hif_dev_scat_sup_info hif_scat_info;
bool virt_scat;
int max_rx_bndl_sz;
int max_tx_bndl_sz;
int chk_irq_status_cnt;
struct ath6kl *ar;
};
int ath6kldev_setup(struct ath6kl_device *dev);
int ath6kldev_unmask_intrs(struct ath6kl_device *dev);
int ath6kldev_mask_intrs(struct ath6kl_device *dev);
int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev,
u32 *lk_ahd, int timeout);
int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx);
int ath6kldev_disable_intrs(struct ath6kl_device *dev);
int ath6kldev_rw_comp_handler(void *context, int status);
int ath6kldev_intr_bh_handler(struct ath6kl *ar);
/* Scatter Function and Definitions */
int ath6kldev_setup_msg_bndl(struct ath6kl_device *dev, int max_msg_per_xfer);
int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
struct hif_scatter_req *scat_req, bool read);
#endif /*ATH6KL_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,238 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#include "htc.h"
#include "wmi.h"
#include "debug.h"
struct bss *wlan_node_alloc(int wh_size)
{
struct bss *ni;
ni = kzalloc(sizeof(struct bss), GFP_ATOMIC);
if ((ni != NULL) && wh_size) {
ni->ni_buf = kmalloc(wh_size, GFP_ATOMIC);
if (ni->ni_buf == NULL) {
kfree(ni);
return NULL;
}
}
return ni;
}
void wlan_node_free(struct bss *ni)
{
kfree(ni->ni_buf);
kfree(ni);
}
void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
const u8 *mac_addr)
{
int hash;
memcpy(ni->ni_macaddr, mac_addr, ETH_ALEN);
hash = ATH6KL_NODE_HASH(mac_addr);
ni->ni_refcnt = 1;
ni->ni_tstamp = jiffies_to_msecs(jiffies);
ni->ni_actcnt = WLAN_NODE_INACT_CNT;
spin_lock_bh(&nt->nt_nodelock);
/* insert at the end of the node list */
ni->ni_list_next = NULL;
ni->ni_list_prev = nt->nt_node_last;
if (nt->nt_node_last != NULL)
nt->nt_node_last->ni_list_next = ni;
nt->nt_node_last = ni;
if (nt->nt_node_first == NULL)
nt->nt_node_first = ni;
/* insert into the hash list */
ni->ni_hash_next = nt->nt_hash[hash];
if (ni->ni_hash_next != NULL)
nt->nt_hash[hash]->ni_hash_prev = ni;
ni->ni_hash_prev = NULL;
nt->nt_hash[hash] = ni;
spin_unlock_bh(&nt->nt_nodelock);
}
struct bss *wlan_find_node(struct ath6kl_node_table *nt,
const u8 *mac_addr)
{
struct bss *ni, *found_ni = NULL;
int hash;
spin_lock_bh(&nt->nt_nodelock);
hash = ATH6KL_NODE_HASH(mac_addr);
for (ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
if (memcmp(ni->ni_macaddr, mac_addr, ETH_ALEN) == 0) {
ni->ni_refcnt++;
found_ni = ni;
break;
}
}
spin_unlock_bh(&nt->nt_nodelock);
return found_ni;
}
void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni)
{
int hash;
spin_lock_bh(&nt->nt_nodelock);
if (ni->ni_list_prev == NULL)
/* fix list head */
nt->nt_node_first = ni->ni_list_next;
else
ni->ni_list_prev->ni_list_next = ni->ni_list_next;
if (ni->ni_list_next == NULL)
/* fix list tail */
nt->nt_node_last = ni->ni_list_prev;
else
ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
if (ni->ni_hash_prev == NULL) {
/* first in list so fix the list head */
hash = ATH6KL_NODE_HASH(ni->ni_macaddr);
nt->nt_hash[hash] = ni->ni_hash_next;
} else {
ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
}
if (ni->ni_hash_next != NULL)
ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
wlan_node_free(ni);
spin_unlock_bh(&nt->nt_nodelock);
}
static void wlan_node_dec_free(struct bss *ni)
{
if ((ni->ni_refcnt--) == 1)
wlan_node_free(ni);
}
void wlan_free_allnodes(struct ath6kl_node_table *nt)
{
struct bss *ni;
while ((ni = nt->nt_node_first) != NULL)
wlan_node_reclaim(nt, ni);
}
void wlan_iterate_nodes(struct ath6kl_node_table *nt,
void (*f) (void *arg, struct bss *), void *arg)
{
struct bss *ni;
spin_lock_bh(&nt->nt_nodelock);
for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
ni->ni_refcnt++;
(*f) (arg, ni);
wlan_node_dec_free(ni);
}
spin_unlock_bh(&nt->nt_nodelock);
}
void wlan_node_table_init(void *wmi, struct ath6kl_node_table *nt)
{
ath6kl_dbg(ATH6KL_DBG_WLAN_NODE, "node table = 0x%lx\n",
(unsigned long)nt);
memset(nt, 0, sizeof(struct ath6kl_node_table));
spin_lock_init(&nt->nt_nodelock);
nt->nt_wmi = wmi;
nt->nt_node_age = WLAN_NODE_INACT_TIMEOUT_MSEC;
}
void wlan_refresh_inactive_nodes(struct ath6kl_node_table *nt)
{
struct bss *bss;
u8 my_bssid[ETH_ALEN];
u32 now;
ath6kl_wmi_get_current_bssid(nt->nt_wmi, my_bssid);
now = jiffies_to_msecs(jiffies);
bss = nt->nt_node_first;
while (bss != NULL) {
/* refresh all nodes except the current bss */
if (memcmp(my_bssid, bss->ni_macaddr, sizeof(my_bssid)) != 0) {
if (((now - bss->ni_tstamp) > nt->nt_node_age)
|| --bss->ni_actcnt == 0) {
wlan_node_reclaim(nt, bss);
}
}
bss = bss->ni_list_next;
}
}
void wlan_node_table_cleanup(struct ath6kl_node_table *nt)
{
wlan_free_allnodes(nt);
}
struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 * ssid,
u32 ssid_len, bool is_wpa2, bool match_ssid)
{
struct bss *ni, *found_ni = NULL;
u8 *ie_ssid;
spin_lock_bh(&nt->nt_nodelock);
for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
ie_ssid = ni->ni_cie.ie_ssid;
if ((ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
(memcmp(ssid, &ie_ssid[2], ssid_len) == 0)) {
if (match_ssid ||
(is_wpa2 && ni->ni_cie.ie_rsn != NULL) ||
(!is_wpa2 && ni->ni_cie.ie_wpa != NULL)) {
ni->ni_refcnt++;
found_ni = ni;
break;
}
}
}
spin_unlock_bh(&nt->nt_nodelock);
return found_ni;
}
void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni)
{
spin_lock_bh(&nt->nt_nodelock);
wlan_node_dec_free(ni);
spin_unlock_bh(&nt->nt_nodelock);
}

View File

@ -0,0 +1,853 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* 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.
*/
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sd.h>
#include "htc_hif.h"
#include "hif-ops.h"
#include "target.h"
#include "debug.h"
struct ath6kl_sdio {
struct sdio_func *func;
spinlock_t lock;
/* free list */
struct list_head bus_req_freeq;
/* available bus requests */
struct bus_request bus_req[BUS_REQUEST_MAX_NUM];
struct ath6kl *ar;
u8 *dma_buffer;
/* scatter request list head */
struct list_head scat_req;
spinlock_t scat_lock;
bool is_disabled;
atomic_t irq_handling;
const struct sdio_device_id *id;
struct work_struct wr_async_work;
struct list_head wr_asyncq;
spinlock_t wr_async_lock;
};
#define CMD53_ARG_READ 0
#define CMD53_ARG_WRITE 1
#define CMD53_ARG_BLOCK_BASIS 1
#define CMD53_ARG_FIXED_ADDRESS 0
#define CMD53_ARG_INCR_ADDRESS 1
static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar)
{
return ar->hif_priv;
}
/*
* Macro to check if DMA buffer is WORD-aligned and DMA-able.
* Most host controllers assume the buffer is DMA'able and will
* bug-check otherwise (i.e. buffers on the stack). virt_addr_valid
* check fails on stack memory.
*/
static inline bool buf_needs_bounce(u8 *buf)
{
return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf);
}
static void ath6kl_sdio_set_mbox_info(struct ath6kl *ar)
{
struct ath6kl_mbox_info *mbox_info = &ar->mbox_info;
/* EP1 has an extended range */
mbox_info->htc_addr = HIF_MBOX_BASE_ADDR;
mbox_info->htc_ext_addr = HIF_MBOX0_EXT_BASE_ADDR;
mbox_info->htc_ext_sz = HIF_MBOX0_EXT_WIDTH;
mbox_info->block_size = HIF_MBOX_BLOCK_SIZE;
mbox_info->gmbox_addr = HIF_GMBOX_BASE_ADDR;
mbox_info->gmbox_sz = HIF_GMBOX_WIDTH;
}
static inline void ath6kl_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func,
u8 mode, u8 opcode, u32 addr,
u16 blksz)
{
*arg = (((rw & 1) << 31) |
((func & 0x7) << 28) |
((mode & 1) << 27) |
((opcode & 1) << 26) |
((addr & 0x1FFFF) << 9) |
(blksz & 0x1FF));
}
static inline void ath6kl_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw,
unsigned int address,
unsigned char val)
{
const u8 func = 0;
*arg = ((write & 1) << 31) |
((func & 0x7) << 28) |
((raw & 1) << 27) |
(1 << 26) |
((address & 0x1FFFF) << 9) |
(1 << 8) |
(val & 0xFF);
}
static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card,
unsigned int address,
unsigned char byte)
{
struct mmc_command io_cmd;
memset(&io_cmd, 0, sizeof(io_cmd));
ath6kl_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
io_cmd.opcode = SD_IO_RW_DIRECT;
io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
return mmc_wait_for_cmd(card->host, &io_cmd, 0);
}
static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio)
{
struct bus_request *bus_req;
unsigned long flag;
spin_lock_irqsave(&ar_sdio->lock, flag);
if (list_empty(&ar_sdio->bus_req_freeq)) {
spin_unlock_irqrestore(&ar_sdio->lock, flag);
return NULL;
}
bus_req = list_first_entry(&ar_sdio->bus_req_freeq,
struct bus_request, list);
list_del(&bus_req->list);
spin_unlock_irqrestore(&ar_sdio->lock, flag);
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req);
return bus_req;
}
static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio,
struct bus_request *bus_req)
{
unsigned long flag;
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req);
spin_lock_irqsave(&ar_sdio->lock, flag);
list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
spin_unlock_irqrestore(&ar_sdio->lock, flag);
}
static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req,
struct hif_scatter_req_priv *s_req_priv,
struct mmc_data *data)
{
struct scatterlist *sg;
int i;
data->blksz = HIF_MBOX_BLOCK_SIZE;
data->blocks = scat_req->len / HIF_MBOX_BLOCK_SIZE;
ath6kl_dbg(ATH6KL_DBG_SCATTER,
"hif-scatter: (%s) addr: 0x%X, (block len: %d, block count: %d) , (tot:%d,sg:%d)\n",
(scat_req->req & HIF_WRITE) ? "WR" : "RD", scat_req->addr,
data->blksz, data->blocks, scat_req->len,
scat_req->scat_entries);
data->flags = (scat_req->req & HIF_WRITE) ? MMC_DATA_WRITE :
MMC_DATA_READ;
/* fill SG entries */
sg = s_req_priv->sgentries;
sg_init_table(sg, scat_req->scat_entries);
/* assemble SG list */
for (i = 0; i < scat_req->scat_entries; i++, sg++) {
if ((unsigned long)scat_req->scat_list[i].buf & 0x3)
/*
* Some scatter engines can handle unaligned
* buffers, print this as informational only.
*/
ath6kl_dbg(ATH6KL_DBG_SCATTER,
"(%s) scatter buffer is unaligned 0x%p\n",
scat_req->req & HIF_WRITE ? "WR" : "RD",
scat_req->scat_list[i].buf);
ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n",
i, scat_req->scat_list[i].buf,
scat_req->scat_list[i].len);
sg_set_buf(sg, scat_req->scat_list[i].buf,
scat_req->scat_list[i].len);
}
/* set scatter-gather table for request */
data->sg = s_req_priv->sgentries;
data->sg_len = scat_req->scat_entries;
}
static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio,
struct bus_request *req)
{
struct mmc_request mmc_req;
struct mmc_command cmd;
struct mmc_data data;
struct hif_scatter_req *scat_req;
u8 opcode, rw;
int status;
scat_req = req->scat_req;
memset(&mmc_req, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
ath6kl_sdio_setup_scat_data(scat_req, scat_req->req_priv, &data);
opcode = (scat_req->req & HIF_FIXED_ADDRESS) ?
CMD53_ARG_FIXED_ADDRESS : CMD53_ARG_INCR_ADDRESS;
rw = (scat_req->req & HIF_WRITE) ? CMD53_ARG_WRITE : CMD53_ARG_READ;
/* Fixup the address so that the last byte will fall on MBOX EOM */
if (scat_req->req & HIF_WRITE) {
if (scat_req->addr == HIF_MBOX_BASE_ADDR)
scat_req->addr += HIF_MBOX_WIDTH - scat_req->len;
else
/* Uses extended address range */
scat_req->addr += HIF_MBOX0_EXT_WIDTH - scat_req->len;
}
/* set command argument */
ath6kl_sdio_set_cmd53_arg(&cmd.arg, rw, ar_sdio->func->num,
CMD53_ARG_BLOCK_BASIS, opcode, scat_req->addr,
data.blocks);
cmd.opcode = SD_IO_RW_EXTENDED;
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
mmc_req.cmd = &cmd;
mmc_req.data = &data;
mmc_set_data_timeout(&data, ar_sdio->func->card);
/* synchronous call to process request */
mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req);
status = cmd.error ? cmd.error : data.error;
scat_req->status = status;
if (scat_req->status)
ath6kl_err("Scatter write request failed:%d\n",
scat_req->status);
if (scat_req->req & HIF_ASYNCHRONOUS)
scat_req->complete(scat_req);
return status;
}
/* callback to issue a read-write scatter request */
static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar,
struct hif_scatter_req *scat_req)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
struct hif_scatter_req_priv *req_priv = scat_req->req_priv;
u32 request = scat_req->req;
int status = 0;
unsigned long flags;
if (!scat_req->len)
return -EINVAL;
ath6kl_dbg(ATH6KL_DBG_SCATTER,
"hif-scatter: total len: %d scatter entries: %d\n",
scat_req->len, scat_req->scat_entries);
if (request & HIF_SYNCHRONOUS) {
sdio_claim_host(ar_sdio->func);
status = ath6kl_sdio_scat_rw(ar_sdio, req_priv->busrequest);
sdio_release_host(ar_sdio->func);
} else {
spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
list_add_tail(&req_priv->busrequest->list, &ar_sdio->wr_asyncq);
spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
}
return status;
}
/* clean up scatter support */
static void ath6kl_sdio_cleanup_scat_resource(struct ath6kl_sdio *ar_sdio)
{
struct hif_scatter_req *s_req, *tmp_req;
unsigned long flag;
/* empty the free list */
spin_lock_irqsave(&ar_sdio->scat_lock, flag);
list_for_each_entry_safe(s_req, tmp_req, &ar_sdio->scat_req, list) {
list_del(&s_req->list);
spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
if (s_req->req_priv && s_req->req_priv->busrequest)
ath6kl_sdio_free_bus_req(ar_sdio,
s_req->req_priv->busrequest);
kfree(s_req->virt_dma_buf);
kfree(s_req->req_priv);
kfree(s_req);
spin_lock_irqsave(&ar_sdio->scat_lock, flag);
}
spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
}
/* setup of HIF scatter resources */
static int ath6kl_sdio_setup_scat_resource(struct ath6kl_sdio *ar_sdio,
struct hif_dev_scat_sup_info *pinfo)
{
struct hif_scatter_req *s_req;
struct bus_request *bus_req;
int i, scat_req_sz, scat_list_sz;
/* check if host supports scatter and it meets our requirements */
if (ar_sdio->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) {
ath6kl_err("hif-scatter: host only supports scatter of : %d entries, need: %d\n",
ar_sdio->func->card->host->max_segs,
MAX_SCATTER_ENTRIES_PER_REQ);
return -EINVAL;
}
ath6kl_dbg(ATH6KL_DBG_ANY,
"hif-scatter enabled: max scatter req : %d entries: %d\n",
MAX_SCATTER_REQUESTS, MAX_SCATTER_ENTRIES_PER_REQ);
scat_list_sz = (MAX_SCATTER_ENTRIES_PER_REQ - 1) *
sizeof(struct hif_scatter_item);
scat_req_sz = sizeof(*s_req) + scat_list_sz;
for (i = 0; i < MAX_SCATTER_REQUESTS; i++) {
/* allocate the scatter request */
s_req = kzalloc(scat_req_sz, GFP_KERNEL);
if (!s_req)
goto fail_setup_scat;
/* allocate the private request blob */
s_req->req_priv = kzalloc(sizeof(*s_req->req_priv), GFP_KERNEL);
if (!s_req->req_priv) {
kfree(s_req);
goto fail_setup_scat;
}
/* allocate a bus request for this scatter request */
bus_req = ath6kl_sdio_alloc_busreq(ar_sdio);
if (!bus_req) {
kfree(s_req->req_priv);
kfree(s_req);
goto fail_setup_scat;
}
/* assign the scatter request to this bus request */
bus_req->scat_req = s_req;
s_req->req_priv->busrequest = bus_req;
/* add it to the scatter pool */
hif_scatter_req_add(ar_sdio->ar, s_req);
}
/* set scatter function pointers */
pinfo->rw_scat_func = ath6kl_sdio_async_rw_scatter;
pinfo->max_scat_entries = MAX_SCATTER_ENTRIES_PER_REQ;
pinfo->max_xfer_szper_scatreq = MAX_SCATTER_REQ_TRANSFER_SIZE;
return 0;
fail_setup_scat:
ath6kl_err("hif-scatter: failed to alloc scatter resources !\n");
ath6kl_sdio_cleanup_scat_resource(ar_sdio);
return -ENOMEM;
}
static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
u32 len, u32 request)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
u8 *tbuf = NULL;
int ret;
bool bounced = false;
if (request & HIF_BLOCK_BASIS)
len = round_down(len, HIF_MBOX_BLOCK_SIZE);
if (buf_needs_bounce(buf)) {
if (!ar_sdio->dma_buffer)
return -ENOMEM;
tbuf = ar_sdio->dma_buffer;
memcpy(tbuf, buf, len);
bounced = true;
} else
tbuf = buf;
sdio_claim_host(ar_sdio->func);
if (request & HIF_WRITE) {
if (addr >= HIF_MBOX_BASE_ADDR &&
addr <= HIF_MBOX_END_ADDR)
addr += (HIF_MBOX_WIDTH - len);
if (addr == HIF_MBOX0_EXT_BASE_ADDR)
addr += HIF_MBOX0_EXT_WIDTH - len;
if (request & HIF_FIXED_ADDRESS)
ret = sdio_writesb(ar_sdio->func, addr, tbuf, len);
else
ret = sdio_memcpy_toio(ar_sdio->func, addr, tbuf, len);
} else {
if (request & HIF_FIXED_ADDRESS)
ret = sdio_readsb(ar_sdio->func, tbuf, addr, len);
else
ret = sdio_memcpy_fromio(ar_sdio->func, tbuf,
addr, len);
if (bounced)
memcpy(buf, tbuf, len);
}
sdio_release_host(ar_sdio->func);
return ret;
}
static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio,
struct bus_request *req)
{
if (req->scat_req)
ath6kl_sdio_scat_rw(ar_sdio, req);
else {
void *context;
int status;
status = ath6kl_sdio_read_write_sync(ar_sdio->ar, req->address,
req->buffer, req->length,
req->request);
context = req->packet;
ath6kl_sdio_free_bus_req(ar_sdio, req);
ath6kldev_rw_comp_handler(context, status);
}
}
static void ath6kl_sdio_write_async_work(struct work_struct *work)
{
struct ath6kl_sdio *ar_sdio;
unsigned long flags;
struct bus_request *req, *tmp_req;
ar_sdio = container_of(work, struct ath6kl_sdio, wr_async_work);
sdio_claim_host(ar_sdio->func);
spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
list_del(&req->list);
spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
__ath6kl_sdio_write_async(ar_sdio, req);
spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
}
spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
sdio_release_host(ar_sdio->func);
}
static void ath6kl_sdio_irq_handler(struct sdio_func *func)
{
int status;
struct ath6kl_sdio *ar_sdio;
ar_sdio = sdio_get_drvdata(func);
atomic_set(&ar_sdio->irq_handling, 1);
/*
* Release the host during interrups so we can pick it back up when
* we process commands.
*/
sdio_release_host(ar_sdio->func);
status = ath6kldev_intr_bh_handler(ar_sdio->ar);
sdio_claim_host(ar_sdio->func);
atomic_set(&ar_sdio->irq_handling, 0);
WARN_ON(status && status != -ECANCELED);
}
static int ath6kl_sdio_power_on(struct ath6kl_sdio *ar_sdio)
{
struct sdio_func *func = ar_sdio->func;
int ret = 0;
if (!ar_sdio->is_disabled)
return 0;
sdio_claim_host(func);
ret = sdio_enable_func(func);
if (ret) {
ath6kl_err("Unable to enable sdio func: %d)\n", ret);
sdio_release_host(func);
return ret;
}
sdio_release_host(func);
/*
* Wait for hardware to initialise. It should take a lot less than
* 10 ms but let's be conservative here.
*/
msleep(10);
ar_sdio->is_disabled = false;
return ret;
}
static int ath6kl_sdio_power_off(struct ath6kl_sdio *ar_sdio)
{
int ret;
if (ar_sdio->is_disabled)
return 0;
/* Disable the card */
sdio_claim_host(ar_sdio->func);
ret = sdio_disable_func(ar_sdio->func);
sdio_release_host(ar_sdio->func);
if (ret)
return ret;
ar_sdio->is_disabled = true;
return ret;
}
static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
u32 length, u32 request,
struct htc_packet *packet)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
struct bus_request *bus_req;
unsigned long flags;
bus_req = ath6kl_sdio_alloc_busreq(ar_sdio);
if (!bus_req)
return -ENOMEM;
bus_req->address = address;
bus_req->buffer = buffer;
bus_req->length = length;
bus_req->request = request;
bus_req->packet = packet;
spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
return 0;
}
static void ath6kl_sdio_irq_enable(struct ath6kl *ar)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
int ret;
sdio_claim_host(ar_sdio->func);
/* Register the isr */
ret = sdio_claim_irq(ar_sdio->func, ath6kl_sdio_irq_handler);
if (ret)
ath6kl_err("Failed to claim sdio irq: %d\n", ret);
sdio_release_host(ar_sdio->func);
}
static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
int ret;
sdio_claim_host(ar_sdio->func);
/* Mask our function IRQ */
while (atomic_read(&ar_sdio->irq_handling)) {
sdio_release_host(ar_sdio->func);
schedule_timeout(HZ / 10);
sdio_claim_host(ar_sdio->func);
}
ret = sdio_release_irq(ar_sdio->func);
if (ret)
ath6kl_err("Failed to release sdio irq: %d\n", ret);
sdio_release_host(ar_sdio->func);
}
static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
struct hif_scatter_req *node = NULL;
unsigned long flag;
spin_lock_irqsave(&ar_sdio->scat_lock, flag);
if (!list_empty(&ar_sdio->scat_req)) {
node = list_first_entry(&ar_sdio->scat_req,
struct hif_scatter_req, list);
list_del(&node->list);
}
spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
return node;
}
static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar,
struct hif_scatter_req *s_req)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
unsigned long flag;
spin_lock_irqsave(&ar_sdio->scat_lock, flag);
list_add_tail(&s_req->list, &ar_sdio->scat_req);
spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
}
static int ath6kl_sdio_enable_scatter(struct ath6kl *ar,
struct hif_dev_scat_sup_info *info)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
int ret;
ret = ath6kl_sdio_setup_scat_resource(ar_sdio, info);
return ret;
}
static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
ath6kl_sdio_cleanup_scat_resource(ar_sdio);
}
static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
.read_write_sync = ath6kl_sdio_read_write_sync,
.write_async = ath6kl_sdio_write_async,
.irq_enable = ath6kl_sdio_irq_enable,
.irq_disable = ath6kl_sdio_irq_disable,
.scatter_req_get = ath6kl_sdio_scatter_req_get,
.scatter_req_add = ath6kl_sdio_scatter_req_add,
.enable_scatter = ath6kl_sdio_enable_scatter,
.cleanup_scatter = ath6kl_sdio_cleanup_scatter,
};
static int ath6kl_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
int ret;
struct ath6kl_sdio *ar_sdio;
struct ath6kl *ar;
int count;
ath6kl_dbg(ATH6KL_DBG_TRC,
"%s: func: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n",
__func__, func->num, func->vendor,
func->device, func->max_blksize, func->cur_blksize);
ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL);
if (!ar_sdio)
return -ENOMEM;
ar_sdio->dma_buffer = kzalloc(HIF_DMA_BUFFER_SIZE, GFP_KERNEL);
if (!ar_sdio->dma_buffer) {
ret = -ENOMEM;
goto err_hif;
}
ar_sdio->func = func;
sdio_set_drvdata(func, ar_sdio);
ar_sdio->id = id;
ar_sdio->is_disabled = true;
spin_lock_init(&ar_sdio->lock);
spin_lock_init(&ar_sdio->scat_lock);
spin_lock_init(&ar_sdio->wr_async_lock);
INIT_LIST_HEAD(&ar_sdio->scat_req);
INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
INIT_LIST_HEAD(&ar_sdio->wr_asyncq);
INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work);
for (count = 0; count < BUS_REQUEST_MAX_NUM; count++)
ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]);
ar = ath6kl_core_alloc(&ar_sdio->func->dev);
if (!ar) {
ath6kl_err("Failed to alloc ath6kl core\n");
ret = -ENOMEM;
goto err_dma;
}
ar_sdio->ar = ar;
ar->hif_priv = ar_sdio;
ar->hif_ops = &ath6kl_sdio_ops;
ath6kl_sdio_set_mbox_info(ar);
sdio_claim_host(func);
if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >=
MANUFACTURER_ID_AR6003_BASE) {
/* enable 4-bit ASYNC interrupt on AR6003 or later */
ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
CCCR_SDIO_IRQ_MODE_REG,
SDIO_IRQ_MODE_ASYNC_4BIT_IRQ);
if (ret) {
ath6kl_err("Failed to enable 4-bit async irq mode %d\n",
ret);
sdio_release_host(func);
goto err_dma;
}
ath6kl_dbg(ATH6KL_DBG_TRC, "4-bit async irq mode enabled\n");
}
/* give us some time to enable, in ms */
func->enable_timeout = 100;
sdio_release_host(func);
ret = ath6kl_sdio_power_on(ar_sdio);
if (ret)
goto err_dma;
sdio_claim_host(func);
ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE);
if (ret) {
ath6kl_err("Set sdio block size %d failed: %d)\n",
HIF_MBOX_BLOCK_SIZE, ret);
sdio_release_host(func);
goto err_off;
}
sdio_release_host(func);
ret = ath6kl_core_init(ar);
if (ret) {
ath6kl_err("Failed to init ath6kl core\n");
goto err_off;
}
return ret;
err_off:
ath6kl_sdio_power_off(ar_sdio);
err_dma:
kfree(ar_sdio->dma_buffer);
err_hif:
kfree(ar_sdio);
return ret;
}
static void ath6kl_sdio_remove(struct sdio_func *func)
{
struct ath6kl_sdio *ar_sdio;
ar_sdio = sdio_get_drvdata(func);
ath6kl_stop_txrx(ar_sdio->ar);
cancel_work_sync(&ar_sdio->wr_async_work);
ath6kl_unavail_ev(ar_sdio->ar);
ath6kl_sdio_power_off(ar_sdio);
kfree(ar_sdio->dma_buffer);
kfree(ar_sdio);
}
static const struct sdio_device_id ath6kl_sdio_devices[] = {
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
{},
};
MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices);
static struct sdio_driver ath6kl_sdio_driver = {
.name = "ath6kl_sdio",
.id_table = ath6kl_sdio_devices,
.probe = ath6kl_sdio_probe,
.remove = ath6kl_sdio_remove,
};
static int __init ath6kl_sdio_init(void)
{
int ret;
ret = sdio_register_driver(&ath6kl_sdio_driver);
if (ret)
ath6kl_err("sdio driver registration failed: %d\n", ret);
return ret;
}
static void __exit ath6kl_sdio_exit(void)
{
sdio_unregister_driver(&ath6kl_sdio_driver);
}
module_init(ath6kl_sdio_init);
module_exit(ath6kl_sdio_exit);
MODULE_AUTHOR("Atheros Communications, Inc.");
MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_FIRMWARE(AR6003_REV2_OTP_FILE);
MODULE_FIRMWARE(AR6003_REV2_FIRMWARE_FILE);
MODULE_FIRMWARE(AR6003_REV2_PATCH_FILE);
MODULE_FIRMWARE(AR6003_REV2_BOARD_DATA_FILE);
MODULE_FIRMWARE(AR6003_REV2_DEFAULT_BOARD_DATA_FILE);
MODULE_FIRMWARE(AR6003_REV3_OTP_FILE);
MODULE_FIRMWARE(AR6003_REV3_FIRMWARE_FILE);
MODULE_FIRMWARE(AR6003_REV3_PATCH_FILE);
MODULE_FIRMWARE(AR6003_REV3_BOARD_DATA_FILE);
MODULE_FIRMWARE(AR6003_REV3_DEFAULT_BOARD_DATA_FILE);

View File

@ -0,0 +1,331 @@
/*
* Copyright (c) 2004-2010 Atheros Communications Inc.
*
* 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.
*/
#ifndef TARGET_H
#define TARGET_H
#define AR6003_BOARD_DATA_SZ 1024
#define AR6003_BOARD_EXT_DATA_SZ 768
#define RESET_CONTROL_ADDRESS 0x00000000
#define RESET_CONTROL_COLD_RST 0x00000100
#define RESET_CONTROL_MBOX_RST 0x00000004
#define CPU_CLOCK_STANDARD_S 0
#define CPU_CLOCK_STANDARD 0x00000003
#define CPU_CLOCK_ADDRESS 0x00000020
#define CLOCK_CONTROL_ADDRESS 0x00000028
#define CLOCK_CONTROL_LF_CLK32_S 2
#define CLOCK_CONTROL_LF_CLK32 0x00000004
#define SYSTEM_SLEEP_ADDRESS 0x000000c4
#define SYSTEM_SLEEP_DISABLE_S 0
#define SYSTEM_SLEEP_DISABLE 0x00000001
#define LPO_CAL_ADDRESS 0x000000e0
#define LPO_CAL_ENABLE_S 20
#define LPO_CAL_ENABLE 0x00100000
#define GPIO_PIN10_ADDRESS 0x00000050
#define GPIO_PIN11_ADDRESS 0x00000054
#define GPIO_PIN12_ADDRESS 0x00000058
#define GPIO_PIN13_ADDRESS 0x0000005c
#define HOST_INT_STATUS_ADDRESS 0x00000400
#define HOST_INT_STATUS_ERROR_S 7
#define HOST_INT_STATUS_ERROR 0x00000080
#define HOST_INT_STATUS_CPU_S 6
#define HOST_INT_STATUS_CPU 0x00000040
#define HOST_INT_STATUS_COUNTER_S 4
#define HOST_INT_STATUS_COUNTER 0x00000010
#define CPU_INT_STATUS_ADDRESS 0x00000401
#define ERROR_INT_STATUS_ADDRESS 0x00000402
#define ERROR_INT_STATUS_WAKEUP_S 2
#define ERROR_INT_STATUS_WAKEUP 0x00000004
#define ERROR_INT_STATUS_RX_UNDERFLOW_S 1
#define ERROR_INT_STATUS_RX_UNDERFLOW 0x00000002
#define ERROR_INT_STATUS_TX_OVERFLOW_S 0
#define ERROR_INT_STATUS_TX_OVERFLOW 0x00000001
#define COUNTER_INT_STATUS_ADDRESS 0x00000403
#define COUNTER_INT_STATUS_COUNTER_S 0
#define COUNTER_INT_STATUS_COUNTER 0x000000ff
#define RX_LOOKAHEAD_VALID_ADDRESS 0x00000405
#define INT_STATUS_ENABLE_ADDRESS 0x00000418
#define INT_STATUS_ENABLE_ERROR_S 7
#define INT_STATUS_ENABLE_ERROR 0x00000080
#define INT_STATUS_ENABLE_CPU_S 6
#define INT_STATUS_ENABLE_CPU 0x00000040
#define INT_STATUS_ENABLE_INT_S 5
#define INT_STATUS_ENABLE_INT 0x00000020
#define INT_STATUS_ENABLE_COUNTER_S 4
#define INT_STATUS_ENABLE_COUNTER 0x00000010
#define INT_STATUS_ENABLE_MBOX_DATA_S 0
#define INT_STATUS_ENABLE_MBOX_DATA 0x0000000f
#define CPU_INT_STATUS_ENABLE_ADDRESS 0x00000419
#define CPU_INT_STATUS_ENABLE_BIT_S 0
#define CPU_INT_STATUS_ENABLE_BIT 0x000000ff
#define ERROR_STATUS_ENABLE_ADDRESS 0x0000041a
#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_S 1
#define ERROR_STATUS_ENABLE_RX_UNDERFLOW 0x00000002
#define ERROR_STATUS_ENABLE_TX_OVERFLOW_S 0
#define ERROR_STATUS_ENABLE_TX_OVERFLOW 0x00000001
#define COUNTER_INT_STATUS_ENABLE_ADDRESS 0x0000041b
#define COUNTER_INT_STATUS_ENABLE_BIT_S 0
#define COUNTER_INT_STATUS_ENABLE_BIT 0x000000ff
#define COUNT_ADDRESS 0x00000420
#define COUNT_DEC_ADDRESS 0x00000440
#define WINDOW_DATA_ADDRESS 0x00000474
#define WINDOW_WRITE_ADDR_ADDRESS 0x00000478
#define WINDOW_READ_ADDR_ADDRESS 0x0000047c
#define CPU_DBG_SEL_ADDRESS 0x00000483
#define CPU_DBG_ADDRESS 0x00000484
#define LOCAL_SCRATCH_ADDRESS 0x000000c0
#define ATH6KL_OPTION_SLEEP_DISABLE 0x08
#define RTC_BASE_ADDRESS 0x00004000
#define GPIO_BASE_ADDRESS 0x00014000
#define MBOX_BASE_ADDRESS 0x00018000
#define ANALOG_INTF_BASE_ADDRESS 0x0001c000
/* real name of the register is unknown */
#define ATH6KL_ANALOG_PLL_REGISTER (ANALOG_INTF_BASE_ADDRESS + 0x284)
#define SM(f, v) (((v) << f##_S) & f)
#define MS(f, v) (((v) & f) >> f##_S)
/*
* xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
* host_interest structure.
*
* Host Interest is shared between Host and Target in order to coordinate
* between the two, and is intended to remain constant (with additions only
* at the end).
*/
#define ATH6KL_HI_START_ADDR 0x00540600
/*
* These are items that the Host may need to access
* via BMI or via the Diagnostic Window. The position
* of items in this structure must remain constant.
* across firmware revisions!
*
* Types for each item must be fixed size across target and host platforms.
* The structure is used only to calculate offset for each register with
* HI_ITEM() macro, no values are stored to it.
*
* More items may be added at the end.
*/
struct host_interest {
/*
* Pointer to application-defined area, if any.
* Set by Target application during startup.
*/
u32 hi_app_host_interest; /* 0x00 */
/* Pointer to register dump area, valid after Target crash. */
u32 hi_failure_state; /* 0x04 */
/* Pointer to debug logging header */
u32 hi_dbglog_hdr; /* 0x08 */
u32 hi_unused1; /* 0x0c */
/*
* General-purpose flag bits, similar to ATH6KL_OPTION_* flags.
* Can be used by application rather than by OS.
*/
u32 hi_option_flag; /* 0x10 */
/*
* Boolean that determines whether or not to
* display messages on the serial port.
*/
u32 hi_serial_enable; /* 0x14 */
/* Start address of DataSet index, if any */
u32 hi_dset_list_head; /* 0x18 */
/* Override Target application start address */
u32 hi_app_start; /* 0x1c */
/* Clock and voltage tuning */
u32 hi_skip_clock_init; /* 0x20 */
u32 hi_core_clock_setting; /* 0x24 */
u32 hi_cpu_clock_setting; /* 0x28 */
u32 hi_system_sleep_setting; /* 0x2c */
u32 hi_xtal_control_setting; /* 0x30 */
u32 hi_pll_ctrl_setting_24ghz; /* 0x34 */
u32 hi_pll_ctrl_setting_5ghz; /* 0x38 */
u32 hi_ref_voltage_trim_setting; /* 0x3c */
u32 hi_clock_info; /* 0x40 */
/*
* Flash configuration overrides, used only
* when firmware is not executing from flash.
* (When using flash, modify the global variables
* with equivalent names.)
*/
u32 hi_bank0_addr_value; /* 0x44 */
u32 hi_bank0_read_value; /* 0x48 */
u32 hi_bank0_write_value; /* 0x4c */
u32 hi_bank0_config_value; /* 0x50 */
/* Pointer to Board Data */
u32 hi_board_data; /* 0x54 */
u32 hi_board_data_initialized; /* 0x58 */
u32 hi_dset_ram_index_tbl; /* 0x5c */
u32 hi_desired_baud_rate; /* 0x60 */
u32 hi_dbglog_config; /* 0x64 */
u32 hi_end_ram_reserve_sz; /* 0x68 */
u32 hi_mbox_io_block_sz; /* 0x6c */
u32 hi_num_bpatch_streams; /* 0x70 -- unused */
u32 hi_mbox_isr_yield_limit; /* 0x74 */
u32 hi_refclk_hz; /* 0x78 */
u32 hi_ext_clk_detected; /* 0x7c */
u32 hi_dbg_uart_txpin; /* 0x80 */
u32 hi_dbg_uart_rxpin; /* 0x84 */
u32 hi_hci_uart_baud; /* 0x88 */
u32 hi_hci_uart_pin_assignments; /* 0x8C */
/*
* NOTE: byte [0] = tx pin, [1] = rx pin, [2] = rts pin, [3] = cts
* pin
*/
u32 hi_hci_uart_baud_scale_val; /* 0x90 */
u32 hi_hci_uart_baud_step_val; /* 0x94 */
u32 hi_allocram_start; /* 0x98 */
u32 hi_allocram_sz; /* 0x9c */
u32 hi_hci_bridge_flags; /* 0xa0 */
u32 hi_hci_uart_support_pins; /* 0xa4 */
/*
* NOTE: byte [0] = RESET pin (bit 7 is polarity),
* bytes[1]..bytes[3] are for future use
*/
u32 hi_hci_uart_pwr_mgmt_params; /* 0xa8 */
/*
* 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high
* [31:16]: wakeup timeout in ms
*/
/* Pointer to extended board data */
u32 hi_board_ext_data; /* 0xac */
u32 hi_board_ext_data_config; /* 0xb0 */
/*
* Bit [0] : valid
* Bit[31:16: size
*/
/*
* hi_reset_flag is used to do some stuff when target reset.
* such as restore app_start after warm reset or
* preserve host Interest area, or preserve ROM data, literals etc.
*/
u32 hi_reset_flag; /* 0xb4 */
/* indicate hi_reset_flag is valid */
u32 hi_reset_flag_valid; /* 0xb8 */
u32 hi_hci_uart_pwr_mgmt_params_ext; /* 0xbc */
/*
* 0xbc - [31:0]: idle timeout in ms
*/
/* ACS flags */
u32 hi_acs_flags; /* 0xc0 */
u32 hi_console_flags; /* 0xc4 */
u32 hi_nvram_state; /* 0xc8 */
u32 hi_option_flag2; /* 0xcc */
/* If non-zero, override values sent to Host in WMI_READY event. */
u32 hi_sw_version_override; /* 0xd0 */
u32 hi_abi_version_override; /* 0xd4 */
/*
* Percentage of high priority RX traffic to total expected RX traffic -
* applicable only to ar6004
*/
u32 hi_hp_rx_traffic_ratio; /* 0xd8 */
/* test applications flags */
u32 hi_test_apps_related ; /* 0xdc */
/* location of test script */
u32 hi_ota_testscript; /* 0xe0 */
/* location of CAL data */
u32 hi_cal_data; /* 0xe4 */
/* Number of packet log buffers */
u32 hi_pktlog_num_buffers; /* 0xe8 */
} __packed;
#define HI_ITEM(item) offsetof(struct host_interest, item)
#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3
#define HI_OPTION_FW_MODE_IBSS 0x0
#define HI_OPTION_FW_MODE_BSS_STA 0x1
#define HI_OPTION_FW_MODE_AP 0x2
#define HI_OPTION_NUM_DEV_SHIFT 0x9
#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
/* Fw Mode/SubMode Mask
|------------------------------------------------------------------------------|
| SUB | SUB | SUB | SUB | | | |
| MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0|
| (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2)
|------------------------------------------------------------------------------|
*/
#define HI_OPTION_FW_MODE_SHIFT 0xC
/* Convert a Target virtual address into a Target physical address */
#define TARG_VTOP(vaddr) (vaddr & 0x001fffff)
#define AR6003_REV2_APP_START_OVERRIDE 0x944C00
#define AR6003_REV2_APP_LOAD_ADDRESS 0x543180
#define AR6003_REV2_BOARD_EXT_DATA_ADDRESS 0x57E500
#define AR6003_REV2_DATASET_PATCH_ADDRESS 0x57e884
#define AR6003_REV2_RAM_RESERVE_SIZE 6912
#define AR6003_REV3_APP_START_OVERRIDE 0x945d00
#define AR6003_REV3_APP_LOAD_ADDRESS 0x545000
#define AR6003_REV3_BOARD_EXT_DATA_ADDRESS 0x542330
#define AR6003_REV3_DATASET_PATCH_ADDRESS 0x57FF74
#define AR6003_REV3_RAM_RESERVE_SIZE 512
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff