You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
7.0 KiB
255 lines
7.0 KiB
/* |
|
* carl9170 firmware - used by the ar9170 wireless device |
|
* |
|
* DMA descriptor handling functions |
|
* |
|
* Copyright (c) 2000-2005 ZyDAS Technology Corporation |
|
* Copyright (c) 2007-2009 Atheros Communications, Inc. |
|
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net> |
|
* Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License along |
|
* with this program; if not, write to the Free Software Foundation, Inc., |
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|
*/ |
|
|
|
#include "carl9170.h" |
|
#include "wl.h" |
|
#include "printf.h" |
|
|
|
struct ar9170_dma_memory dma_mem __section(sram); |
|
|
|
static void copy_dma_desc(struct dma_desc *dst, |
|
struct dma_desc *src) |
|
{ |
|
memcpy(dst, src, sizeof(struct dma_desc)); |
|
} |
|
|
|
static void clear_descriptor(struct dma_desc *d) |
|
{ |
|
d->status = AR9170_OWN_BITS_SW; |
|
d->ctrl = 0; |
|
d->dataSize = 0; |
|
d->totalLen = 0; |
|
d->lastAddr = d; |
|
d->dataAddr = NULL; |
|
d->nextAddr = d; |
|
} |
|
|
|
static void fill_descriptor(struct dma_desc *d, uint16_t size, uint8_t *data) |
|
{ |
|
d->status = AR9170_OWN_BITS_SW; |
|
d->ctrl = 0; |
|
d->dataSize = size; |
|
d->totalLen = 0; |
|
d->lastAddr = d; |
|
d->dataAddr = data; |
|
d->nextAddr = NULL; |
|
} |
|
|
|
static void init_queue(struct dma_queue *q, struct dma_desc *d) |
|
{ |
|
q->head = q->terminator = d; |
|
} |
|
|
|
/* |
|
* - Init up_queue, down_queue, tx_queue[5], rx_queue. |
|
* - Setup descriptors and data buffer address. |
|
* - Ring descriptors rx_queue and down_queue by dma_reclaim(). |
|
* |
|
* NOTE: LastAddr tempary point (same) to nextAddr after initialize. |
|
* Because LastAddr is don't care in function dma_reclaim(). |
|
*/ |
|
void dma_init_descriptors(void) |
|
{ |
|
unsigned int i, j; |
|
|
|
for (i = 0; i < ARRAY_SIZE(dma_mem.terminator); i++) |
|
clear_descriptor(&dma_mem.terminator[i]); |
|
|
|
/* Assign terminators to DMA queues */ |
|
i = 0; |
|
init_queue(&fw.pta.up_queue, &dma_mem.terminator[i++]); |
|
init_queue(&fw.pta.down_queue, &dma_mem.terminator[i++]); |
|
for (j = 0; j < __AR9170_NUM_TX_QUEUES; j++) |
|
init_queue(&fw.wlan.tx_queue[j], &dma_mem.terminator[i++]); |
|
init_queue(&fw.wlan.tx_retry, &dma_mem.terminator[i++]); |
|
init_queue(&fw.wlan.rx_queue, &dma_mem.terminator[i++]); |
|
fw.usb.int_desc = &dma_mem.terminator[i++]; |
|
fw.wlan.fw_desc = &dma_mem.terminator[i++]; |
|
|
|
#ifdef CONFIG_CARL9170FW_CAB_QUEUE |
|
for (j = 0; j < CARL9170_INTF_NUM; j++) |
|
init_queue(&fw.wlan.cab_queue[j], &dma_mem.terminator[i++]); |
|
#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ |
|
|
|
BUG_ON(AR9170_TERMINATOR_NUMBER != i); |
|
|
|
DBG("Blocks:%d [tx:%d, rx:%d] Terminators:%d/%d\n", |
|
AR9170_BLOCK_NUMBER, AR9170_TX_BLOCK_NUMBER, |
|
AR9170_RX_BLOCK_NUMBER, AR9170_TERMINATOR_NUMBER, i); |
|
|
|
/* Init descriptors and memory blocks */ |
|
for (i = 0; i < AR9170_BLOCK_NUMBER; i++) { |
|
fill_descriptor(&dma_mem.block[i], AR9170_BLOCK_SIZE, dma_mem.data[i].data); |
|
|
|
if (i < AR9170_TX_BLOCK_NUMBER) |
|
dma_reclaim(&fw.pta.down_queue, &dma_mem.block[i]); |
|
else |
|
dma_reclaim(&fw.wlan.rx_queue, &dma_mem.block[i]); |
|
} |
|
|
|
/* Set DMA address registers */ |
|
set(AR9170_PTA_REG_DN_DMA_ADDRH, (uint32_t) fw.pta.down_queue.head >> 16); |
|
set(AR9170_PTA_REG_DN_DMA_ADDRL, (uint32_t) fw.pta.down_queue.head & 0xffff); |
|
set(AR9170_PTA_REG_UP_DMA_ADDRH, (uint32_t) fw.pta.up_queue.head >> 16); |
|
set(AR9170_PTA_REG_UP_DMA_ADDRL, (uint32_t) fw.pta.up_queue.head & 0xffff); |
|
|
|
for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) |
|
set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head); |
|
|
|
set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head); |
|
fw.usb.int_desc->dataSize = AR9170_BLOCK_SIZE; |
|
fw.usb.int_desc->dataAddr = (void *) &dma_mem.reserved.rsp; |
|
|
|
memset(DESC_PAYLOAD(fw.usb.int_desc), 0xff, |
|
AR9170_INT_MAGIC_HEADER_SIZE); |
|
memset(DESC_PAYLOAD_OFF(fw.usb.int_desc, AR9170_INT_MAGIC_HEADER_SIZE), |
|
0, AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE); |
|
|
|
/* rsp is now available for use */ |
|
fw.usb.int_desc_available = 1; |
|
|
|
memset(DESC_PAYLOAD(fw.wlan.fw_desc), 0, 128); |
|
fw.wlan.fw_desc_available = 1; |
|
} |
|
|
|
/* |
|
* Free descriptor. |
|
* |
|
* Exchange the terminator and the first descriptor of the packet |
|
* for hardware ascy... |
|
*/ |
|
void dma_reclaim(struct dma_queue *q, struct dma_desc *desc) |
|
{ |
|
struct dma_desc *tmpDesc, *last; |
|
struct dma_desc tdesc; |
|
|
|
/* 1. Set OWN bit to HW for all TDs to be added, clear ctrl and size */ |
|
tmpDesc = desc; |
|
last = desc->lastAddr; |
|
|
|
while (1) { |
|
tmpDesc->status = AR9170_OWN_BITS_HW; |
|
tmpDesc->ctrl = 0; |
|
tmpDesc->totalLen = 0; |
|
tmpDesc->dataSize = AR9170_BLOCK_SIZE; |
|
|
|
/* TODO : Exception handle */ |
|
|
|
tmpDesc->lastAddr = tmpDesc; |
|
|
|
if (tmpDesc == last) |
|
break; |
|
|
|
tmpDesc = tmpDesc->nextAddr; |
|
} |
|
|
|
/* 2. Next address of Last TD to be added = first TD */ |
|
tmpDesc->nextAddr = desc; |
|
|
|
/* Link first TD to self */ |
|
desc->lastAddr = q->terminator; |
|
|
|
/* 3. Copy first TD to be added to TTD */ |
|
copy_dma_desc(&tdesc, desc); |
|
|
|
/* 4. Initialize new terminator */ |
|
clear_descriptor(desc); |
|
|
|
/* 5. Copy TTD to last TD */ |
|
tdesc.status = 0; |
|
copy_dma_desc((void *)q->terminator, (void *)&tdesc); |
|
q->terminator->status |= AR9170_OWN_BITS_HW; |
|
|
|
/* Update terminator pointer */ |
|
q->terminator = desc; |
|
} |
|
|
|
/* |
|
* Put a complete packet into the tail of the Queue q. |
|
* Exchange the terminator and the first descriptor of the packet |
|
* for hardware ascy... |
|
*/ |
|
void dma_put(struct dma_queue *q, struct dma_desc *desc) |
|
{ |
|
struct dma_desc *tmpDesc; |
|
struct dma_desc tdesc; |
|
|
|
tmpDesc = desc; |
|
|
|
while (1) { |
|
/* update totalLen */ |
|
tmpDesc->totalLen = desc->totalLen; |
|
|
|
/* 1. Set OWN bit to HW for all TDs to be added */ |
|
tmpDesc->status = AR9170_OWN_BITS_HW; |
|
/* TODO : Exception handle */ |
|
|
|
tmpDesc->lastAddr = desc->lastAddr; |
|
|
|
if (desc->lastAddr == tmpDesc) |
|
break; |
|
|
|
tmpDesc = tmpDesc->nextAddr; |
|
} |
|
|
|
/* 2. Next address of Last TD to be added = first TD */ |
|
desc->lastAddr->nextAddr = desc; |
|
|
|
/* If there is only one descriptor, update pointer of last descriptor */ |
|
if (desc->lastAddr == desc) |
|
desc->lastAddr = q->terminator; |
|
|
|
/* 3. Copy first TD to be added to TTD */ |
|
copy_dma_desc(&tdesc, desc); |
|
|
|
/* 4. Initialize new terminator */ |
|
clear_descriptor(desc); |
|
|
|
/* 5. Copy TTD to last TD */ |
|
tdesc.status &= (~AR9170_OWN_BITS); |
|
copy_dma_desc((void *)q->terminator, (void *)&tdesc); |
|
q->terminator->status |= AR9170_OWN_BITS_HW; |
|
|
|
/* Update terminator pointer */ |
|
q->terminator = desc; |
|
} |
|
|
|
struct dma_desc *dma_unlink_head(struct dma_queue *queue) |
|
{ |
|
struct dma_desc *desc; |
|
|
|
if (queue_empty(queue)) |
|
return NULL; |
|
|
|
desc = queue->head; |
|
|
|
queue->head = desc->lastAddr->nextAddr; |
|
|
|
/* poison nextAddr address */ |
|
desc->lastAddr->nextAddr = desc->lastAddr; |
|
desc->lastAddr->lastAddr = desc->lastAddr; |
|
|
|
return desc; |
|
}
|
|
|