/*
* File: drivers/net/bfin_mac.c
* Based on:
* Maintainer:
* Bryan Wu <bryan.wu@analog.com>
*
* Original author:
* Luke Yang <luke.yang@analog.com>
*
* Created:
* Description:
*
* Modified:
* Copyright 2004-2006 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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, 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 ; see the file COPYING.
* If not, write to the Free Software Foundation,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/crc32.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
#include <asm/blackfin.h>
#include <asm/cacheflush.h>
#include <asm/portmux.h>
#include "bfin_mac.h"
#define DRV_NAME "bfin_mac"
#define DRV_VERSION "1.1"
#define DRV_AUTHOR "Bryan Wu, Luke Yang"
#define DRV_DESC "Blackfin BF53[67] on-chip Ethernet MAC driver"
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRV_DESC);
#if defined(CONFIG_BFIN_MAC_USE_L1)
# define bfin_mac_alloc(dma_handle, size) l1_data_sram_zalloc(size)
# define bfin_mac_free(dma_handle, ptr) l1_data_sram_free(ptr)
#else
# define bfin_mac_alloc(dma_handle, size) \
dma_alloc_coherent(NULL, size, dma_handle, GFP_KERNEL)
# define bfin_mac_free(dma_handle, ptr) \
dma_free_coherent(NULL, sizeof(*ptr), ptr, dma_handle)
#endif
#define PKT_BUF_SZ 1580
#define MAX_TIMEOUT_CNT 500
/* pointers to maintain transmit list */
static struct net_dma_desc_tx *tx_list_head;
static struct net_dma_desc_tx *tx_list_tail;
static struct net_dma_desc_rx *rx_list_head;
static struct net_dma_desc_rx *rx_list_tail;
static struct net_dma_desc_rx *current_rx_ptr;
static struct net_dma_desc_tx *current_tx_ptr;
static struct net_dma_desc_tx *tx_desc;
static struct net_dma_desc_rx *rx_desc;
static void desc_list_free(void)
{
struct net_dma_desc_rx *r;
struct net_dma_desc_tx *t;
int i;
#if !defined(CONFIG_BFIN_MAC_USE_L1)
dma_addr_t dma_handle = 0;
#endif
if (tx_desc) {
t = tx_list_head;
for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
if (t) {
if (t->skb) {
dev_kfree_skb(t->skb);
t->skb = NULL;
}
t = t->next;
}
}
bfin_mac_free(dma_handle, tx_desc);
}
if (rx_desc) {
r = rx_list_head;
for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
if (r) {
if (r->skb) {
dev_kfree_skb(r->skb);
r->skb = NULL;
}
r = r->next;
}
}
bfin_mac_free(dma_handle, rx_desc);
}
}
static int desc_list_init(void)
{
int i;
struct sk_buff *new_skb;
#if !defined(CONFIG_BFIN_MAC_USE_L1)
/*
* This dma_handle is useless in Blackfin dma_alloc_coherent().
* The real dma handler is the return value of dma_alloc_coherent().
*/
dma_addr_t dma_handle;
#endif
tx_desc = bfin_mac_alloc(&dma_handle,
sizeof(struct net_dma_desc_tx) *
CONFIG_BFIN_TX_DESC_NUM);
if (tx_desc == NULL)
goto init_error;
rx_desc = bfin_mac_alloc(&dma_handle,
sizeof(struct net_dma_desc_rx) *
CONFIG_BFIN_RX_DESC_NUM);
if (rx_desc == NULL)
goto init_error;
/* init tx_list */
tx_list_head = tx_list_tail = tx_desc;
for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
struct net_dma_desc_tx *t = tx_desc + i;
struct dma_descriptor *a = &(t->desc_a);
struct dma_descriptor *b = &(t->desc_b);
/*
* disable DMA
* read from memory WNR = 0
* wordsize is 32 bits
* 6 half words is desc size
* large desc flow
*/
a->config = WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
a->start_addr = (unsigned long)t->packet;
a->x_count = 0;
a->next_dma_desc = b;
/*
* enabled DMA
* write to memory WNR = 1
* wordsize is 32 bits
* disable interrupt
* 6 half words is desc size
* large desc flow
*/
b->config = DMAEN | WNR | WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
b->start_addr = (unsigne