/*
* Copyright (c) 2008-2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/gfp.h>
#define DRV_MODULE_NAME "w90p910-emc"
#define DRV_MODULE_VERSION "0.1"
/* Ethernet MAC Registers */
#define REG_CAMCMR 0x00
#define REG_CAMEN 0x04
#define REG_CAMM_BASE 0x08
#define REG_CAML_BASE 0x0c
#define REG_TXDLSA 0x88
#define REG_RXDLSA 0x8C
#define REG_MCMDR 0x90
#define REG_MIID 0x94
#define REG_MIIDA 0x98
#define REG_FFTCR 0x9C
#define REG_TSDR 0xa0
#define REG_RSDR 0xa4
#define REG_DMARFC 0xa8
#define REG_MIEN 0xac
#define REG_MISTA 0xb0
#define REG_CTXDSA 0xcc
#define REG_CTXBSA 0xd0
#define REG_CRXDSA 0xd4
#define REG_CRXBSA 0xd8
/* mac controller bit */
#define MCMDR_RXON 0x01
#define MCMDR_ACP (0x01 << 3)
#define MCMDR_SPCRC (0x01 << 5)
#define MCMDR_TXON (0x01 << 8)
#define MCMDR_FDUP (0x01 << 18)
#define MCMDR_ENMDC (0x01 << 19)
#define MCMDR_OPMOD (0x01 << 20)
#define SWR (0x01 << 24)
/* cam command regiser */
#define CAMCMR_AUP 0x01
#define CAMCMR_AMP (0x01 << 1)
#define CAMCMR_ABP (0x01 << 2)
#define CAMCMR_CCAM (0x01 << 3)
#define CAMCMR_ECMP (0x01 << 4)
#define CAM0EN 0x01
/* mac mii controller bit */
#define MDCCR (0x0a << 20)
#define PHYAD (0x01 << 8)
#define PHYWR (0x01 << 16)
#define PHYBUSY (0x01 << 17)
#define PHYPRESP (0x01 << 18)
#define CAM_ENTRY_SIZE 0x08
/* rx and tx status */
#define TXDS_TXCP (0x01 << 19)
#define RXDS_CRCE (0x01 << 17)
#define RXDS_PTLE (0x01 << 19)
#define RXDS_RXGD (0x01 << 20)
#define RXDS_ALIE (0x01 << 21)
#define RXDS_RP (0x01 << 22)
/* mac interrupt status*/
#define MISTA_EXDEF (0x01 << 19)
#define MISTA_TXBERR (0x01 << 24)
#define MISTA_TDU (0x01 << 23)
#define MISTA_RDU (0x01 << 10)
#define MISTA_RXBERR (0x01 << 11)
#define ENSTART 0x01
#define ENRXINTR 0x01
#define ENRXGD (0x01 << 4)
#define ENRXBERR (0x01 << 11)
#define ENTXINTR (0x01 << 16)
#define ENTXCP (0x01 << 18)
#define ENTXABT (0x01 << 21)
#define ENTXBERR (0x01 << 24)
#define ENMDC (0x01 << 19)
#define PHYBUSY (0x01 << 17)
#define MDCCR_VAL 0xa00000
/* rx and tx owner bit */
#define RX_OWEN_DMA (0x01 << 31)
#define RX_OWEN_CPU (~(0x03 << 30))
#define TX_OWEN_DMA (0x01 << 31)
#define TX_OWEN_CPU (~(0x01 << 31))
/* tx frame desc controller bit */
#define MACTXINTEN 0x04
#define CRCMODE 0x02
#define PADDINGMODE 0x01
/* fftcr controller bit */
#define TXTHD (0x03 << 8)
#define BLENGTH (0x01 << 20)
/* global setting for driver */
#define RX_DESC_SIZE 50
#define TX_DESC_SIZE 10
#define MAX_RBUFF_SZ 0x600
#define MAX_TBUFF_SZ 0x600
#define TX_TIMEOUT (HZ/2)
#define DELAY 1000
#define CAM0 0x0
static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg);
struct w90p910_rxbd {
unsigned int sl;
unsigned int buffer;
unsigned int reserved;
unsigned int next;
};
struct w90p910_txbd {
unsigned int mode;
unsigned int buffer;
unsigned int sl;
unsigned int next;
};
struct recv_pdesc {
struct w90p910_rxbd desclist[RX_DESC_SIZE];
char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ];
};
struct tran_pdesc {
struct w90p910_txbd desclist[TX_DESC_SIZE];
char tran_buf[TX_DESC_SIZE][MAX_TBUFF_SZ];
};
struct w90p910_ether {
struct recv_pdesc *rdesc;
struct tran_pdesc *tdesc;
dma_addr_t rdesc_phys;
dma_addr_t tdesc_phys;
struct platform_device *pdev;
struct resource *res;
struct sk_buff *skb;
struct clk *clk;
struct clk *rmiiclk;
struct mii_if_info mii;
struct timer_list check_timer;
void __iomem *reg;
int rxirq;
int txirq;
unsigned int cur_tx;
unsigned int cur_rx;
unsigned int finish_tx;
unsigned int rx_packets;
unsigned int rx_bytes;
unsigned int start_tx_ptr;
unsigned int start_rx_ptr;
unsigned int linkflag;
};
static void update_linkspeed_register(struct net_device *dev,
unsigned int speed, unsigned int duplex)
{
struct w90p910_ether *ether = netdev_priv(dev);
unsigned int val;
val = __raw_readl(ether->reg + REG_MCMDR);
if (speed == SPEED_100) {
/* 100 full/half duplex */
if (duplex == DUPLEX_FULL) {
val |= (MCMDR_OPMOD | MCMDR_FDUP);
} else {
val |= MCMDR_OPMOD;
val &= ~MCMDR_FDUP;
}
} else {
/* 10 full/half duplex */
if (duplex == DUPLEX_FULL) {
val |= MCMDR_FDUP;
val &= ~MCMDR_OPMOD;
} else {
val &= ~(MCMDR_FDUP | MCMDR_OPMOD);
}
}
__raw_writel(val, ether->reg + REG_MCMDR);
}
static void update_linkspeed(struct net_device *dev)
{
struct w90p910_ether *ether = netdev_priv(dev);
struct platform_device *pdev;
unsigned int bmsr, bmcr, lpa, speed, duplex;
pdev = ether->pdev;
if (!mii_link_ok(ðer->mii)) {
ether->linkflag = 0x0;
netif_carrier_off(dev);
dev_warn(&pdev->dev, "%s: Link down.\n", dev->name);
return;
}
if (ether->linkflag == 1)
return;
bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR);
bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR);
if (bmcr & BMCR_ANENABLE) {
if<