/**
* Airgo MIMO wireless driver
*
* Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com>
* Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer
* works and published the SPECS at http://airgo.wdwconsulting.net/mymoin
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/pci.h>
#include <linux/delay.h>
#include "agnx.h"
#include "debug.h"
#include "phy.h"
unsigned int rx_frame_cnt;
/* unsigned int local_tx_sent_cnt = 0; */
static inline void disable_rx_engine(struct agnx_priv *priv)
{
void __iomem *ctl = priv->ctl;
iowrite32(0x100, ctl + AGNX_CIR_RXCTL);
/* Wait for RX Control to have the Disable Rx Interrupt (0x100) set */
ioread32(ctl + AGNX_CIR_RXCTL);
}
static inline void enable_rx_engine(struct agnx_priv *priv)
{
void __iomem *ctl = priv->ctl;
iowrite32(0x80, ctl + AGNX_CIR_RXCTL);
ioread32(ctl + AGNX_CIR_RXCTL);
}
inline void disable_rx_interrupt(struct agnx_priv *priv)
{
void __iomem *ctl = priv->ctl;
u32 reg;
disable_rx_engine(priv);
reg = ioread32(ctl + AGNX_CIR_RXCFG);
reg &= ~0x20;
iowrite32(reg, ctl + AGNX_CIR_RXCFG);
ioread32(ctl + AGNX_CIR_RXCFG);
}
inline void enable_rx_interrupt(struct agnx_priv *priv)
{
void __iomem *ctl = priv->ctl;
u32 reg;
reg = ioread32(ctl + AGNX_CIR_RXCFG);
reg |= 0x20;
iowrite32(reg, ctl + AGNX_CIR_RXCFG);
ioread32(ctl + AGNX_CIR_RXCFG);
enable_rx_engine(priv);
}
static inline void rx_desc_init(struct agnx_priv *priv, unsigned int idx)
{
struct agnx_desc *desc = priv->rx.desc + idx;
struct agnx_info *info = priv->rx.info + idx;
memset(info, 0, sizeof(*info));
info->dma_len = IEEE80211_MAX_RTS_THRESHOLD + sizeof(struct agnx_hdr);
info->skb = dev_alloc_skb(info->dma_len);
if (info->skb == NULL)
agnx_bug("refill err");
info->mapping = pci_map_single(priv->pdev, skb_tail_pointer(info->skb),
info->dma_len, PCI_DMA_FROMDEVICE);
memset(desc, 0, sizeof(*desc));
desc->dma_addr = cpu_to_be32(info->mapping);
/* Set the owner to the card */
desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER);
}
static inline void rx_desc_reinit(struct agnx_priv *priv, unsigned int idx)
{
struct agnx_info *info = priv->rx.info + idx;
/* Cause ieee80211 will free the skb buffer, so we needn't to free it again?! */
pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE);
rx_desc_init(priv, idx);
}
static inline void rx_desc_reusing(struct agnx_priv *priv, unsigned int idx)
{
struct agnx_desc *desc = priv->rx.desc + idx;
struct agnx_info *info = priv->rx.info + idx;
memset(desc, 0, sizeof(*desc));
desc->dma_addr = cpu_to_be32(info->mapping);
/* Set the owner to the card */
desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER);
}
static void rx_desc_free(struct agnx_priv *priv, unsigned int idx)
{
struct agnx_desc *desc = priv->rx.desc + idx;
struct agnx_info *info = priv->rx.info + idx;
BUG_ON(!desc || !info);
if (info->mapping)
pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE);
if (info->skb)
dev_kfree_skb(info->skb);
memset(info, 0, sizeof(*info));
memset(desc, 0, sizeof(*desc));
}
static inline void __tx_desc_free(struct agnx_priv *priv,
struct agnx_desc *desc, struct agnx_info *info)
{
BUG_ON(!desc || !info);
/* TODO make sure mapping, skb and len are consistency */
if (info->mapping)
pci_unmap_single(priv->pdev, info->mapping,
info->dma_len, PCI_DMA_TODEVICE);
if (info->type == PACKET)
dev_kfree_skb(info->skb);
memset(info, 0, sizeof(*info));
memset(desc, 0, sizeof(*desc));
}
static void txm_desc_free(struct agnx_priv *priv, unsigned int idx)
{
struct agnx_desc *desc = priv->txm.desc + idx;
struct agnx_info *info = priv->txm.info + idx;
__tx_desc_free(priv, desc, info);
}
static void txd_desc_free(struct agnx_priv *priv, unsigned int idx)
{
struct agnx_desc *desc = priv->txd.desc + idx