mirror of
https://github.com/torvalds/linux.git
synced 2024-12-15 23:51:46 +00:00
719655a149
This is one step in allowing phylib to make use of link_mode bitmaps, instead of u32 for supported and advertised features. Convert the phy drivers to use bitmaps to indicates the features they support. Build bitmap equivalents of the u32 values at runtime, and have the drivers point to the appropriate bitmap. These bitmaps are shared, and we don't want a driver to modify them. So mark them __ro_after_init. Within phylib, the features bitmap is currently turned back into a u32. This will be removed once the whole of phylib, and the drivers are converted to use bitmaps. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
1618 lines
41 KiB
C
1618 lines
41 KiB
C
/*
|
|
* PXA168 ethernet driver.
|
|
* Most of the code is derived from mv643xx ethernet driver.
|
|
*
|
|
* Copyright (C) 2010 Marvell International Ltd.
|
|
* Sachin Sanap <ssanap@marvell.com>
|
|
* Zhangfei Gao <zgao6@marvell.com>
|
|
* Philip Rakity <prakity@marvell.com>
|
|
* Mark Brown <markb@marvell.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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/in.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pxa168_eth.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/types.h>
|
|
#include <linux/udp.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
#include <asm/cacheflush.h>
|
|
|
|
#define DRIVER_NAME "pxa168-eth"
|
|
#define DRIVER_VERSION "0.3"
|
|
|
|
/*
|
|
* Registers
|
|
*/
|
|
|
|
#define PHY_ADDRESS 0x0000
|
|
#define SMI 0x0010
|
|
#define PORT_CONFIG 0x0400
|
|
#define PORT_CONFIG_EXT 0x0408
|
|
#define PORT_COMMAND 0x0410
|
|
#define PORT_STATUS 0x0418
|
|
#define HTPR 0x0428
|
|
#define MAC_ADDR_LOW 0x0430
|
|
#define MAC_ADDR_HIGH 0x0438
|
|
#define SDMA_CONFIG 0x0440
|
|
#define SDMA_CMD 0x0448
|
|
#define INT_CAUSE 0x0450
|
|
#define INT_W_CLEAR 0x0454
|
|
#define INT_MASK 0x0458
|
|
#define ETH_F_RX_DESC_0 0x0480
|
|
#define ETH_C_RX_DESC_0 0x04A0
|
|
#define ETH_C_TX_DESC_1 0x04E4
|
|
|
|
/* smi register */
|
|
#define SMI_BUSY (1 << 28) /* 0 - Write, 1 - Read */
|
|
#define SMI_R_VALID (1 << 27) /* 0 - Write, 1 - Read */
|
|
#define SMI_OP_W (0 << 26) /* Write operation */
|
|
#define SMI_OP_R (1 << 26) /* Read operation */
|
|
|
|
#define PHY_WAIT_ITERATIONS 10
|
|
|
|
#define PXA168_ETH_PHY_ADDR_DEFAULT 0
|
|
/* RX & TX descriptor command */
|
|
#define BUF_OWNED_BY_DMA (1 << 31)
|
|
|
|
/* RX descriptor status */
|
|
#define RX_EN_INT (1 << 23)
|
|
#define RX_FIRST_DESC (1 << 17)
|
|
#define RX_LAST_DESC (1 << 16)
|
|
#define RX_ERROR (1 << 15)
|
|
|
|
/* TX descriptor command */
|
|
#define TX_EN_INT (1 << 23)
|
|
#define TX_GEN_CRC (1 << 22)
|
|
#define TX_ZERO_PADDING (1 << 18)
|
|
#define TX_FIRST_DESC (1 << 17)
|
|
#define TX_LAST_DESC (1 << 16)
|
|
#define TX_ERROR (1 << 15)
|
|
|
|
/* SDMA_CMD */
|
|
#define SDMA_CMD_AT (1 << 31)
|
|
#define SDMA_CMD_TXDL (1 << 24)
|
|
#define SDMA_CMD_TXDH (1 << 23)
|
|
#define SDMA_CMD_AR (1 << 15)
|
|
#define SDMA_CMD_ERD (1 << 7)
|
|
|
|
/* Bit definitions of the Port Config Reg */
|
|
#define PCR_DUPLEX_FULL (1 << 15)
|
|
#define PCR_HS (1 << 12)
|
|
#define PCR_EN (1 << 7)
|
|
#define PCR_PM (1 << 0)
|
|
|
|
/* Bit definitions of the Port Config Extend Reg */
|
|
#define PCXR_2BSM (1 << 28)
|
|
#define PCXR_DSCP_EN (1 << 21)
|
|
#define PCXR_RMII_EN (1 << 20)
|
|
#define PCXR_AN_SPEED_DIS (1 << 19)
|
|
#define PCXR_SPEED_100 (1 << 18)
|
|
#define PCXR_MFL_1518 (0 << 14)
|
|
#define PCXR_MFL_1536 (1 << 14)
|
|
#define PCXR_MFL_2048 (2 << 14)
|
|
#define PCXR_MFL_64K (3 << 14)
|
|
#define PCXR_FLOWCTL_DIS (1 << 12)
|
|
#define PCXR_FLP (1 << 11)
|
|
#define PCXR_AN_FLOWCTL_DIS (1 << 10)
|
|
#define PCXR_AN_DUPLEX_DIS (1 << 9)
|
|
#define PCXR_PRIO_TX_OFF 3
|
|
#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
|
|
|
|
/* Bit definitions of the SDMA Config Reg */
|
|
#define SDCR_BSZ_OFF 12
|
|
#define SDCR_BSZ8 (3 << SDCR_BSZ_OFF)
|
|
#define SDCR_BSZ4 (2 << SDCR_BSZ_OFF)
|
|
#define SDCR_BSZ2 (1 << SDCR_BSZ_OFF)
|
|
#define SDCR_BSZ1 (0 << SDCR_BSZ_OFF)
|
|
#define SDCR_BLMR (1 << 6)
|
|
#define SDCR_BLMT (1 << 7)
|
|
#define SDCR_RIFB (1 << 9)
|
|
#define SDCR_RC_OFF 2
|
|
#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
|
|
|
|
/*
|
|
* Bit definitions of the Interrupt Cause Reg
|
|
* and Interrupt MASK Reg is the same
|
|
*/
|
|
#define ICR_RXBUF (1 << 0)
|
|
#define ICR_TXBUF_H (1 << 2)
|
|
#define ICR_TXBUF_L (1 << 3)
|
|
#define ICR_TXEND_H (1 << 6)
|
|
#define ICR_TXEND_L (1 << 7)
|
|
#define ICR_RXERR (1 << 8)
|
|
#define ICR_TXERR_H (1 << 10)
|
|
#define ICR_TXERR_L (1 << 11)
|
|
#define ICR_TX_UDR (1 << 13)
|
|
#define ICR_MII_CH (1 << 28)
|
|
|
|
#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
|
|
ICR_TXERR_H | ICR_TXERR_L |\
|
|
ICR_TXEND_H | ICR_TXEND_L |\
|
|
ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
|
|
|
|
#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
|
|
|
|
#define NUM_RX_DESCS 64
|
|
#define NUM_TX_DESCS 64
|
|
|
|
#define HASH_ADD 0
|
|
#define HASH_DELETE 1
|
|
#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS == 1) */
|
|
#define HOP_NUMBER 12
|
|
|
|
/* Bit definitions for Port status */
|
|
#define PORT_SPEED_100 (1 << 0)
|
|
#define FULL_DUPLEX (1 << 1)
|
|
#define FLOW_CONTROL_DISABLED (1 << 2)
|
|
#define LINK_UP (1 << 3)
|
|
|
|
/* Bit definitions for work to be done */
|
|
#define WORK_TX_DONE (1 << 1)
|
|
|
|
/*
|
|
* Misc definitions.
|
|
*/
|
|
#define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
|
|
|
|
struct rx_desc {
|
|
u32 cmd_sts; /* Descriptor command status */
|
|
u16 byte_cnt; /* Descriptor buffer byte count */
|
|
u16 buf_size; /* Buffer size */
|
|
u32 buf_ptr; /* Descriptor buffer pointer */
|
|
u32 next_desc_ptr; /* Next descriptor pointer */
|
|
};
|
|
|
|
struct tx_desc {
|
|
u32 cmd_sts; /* Command/status field */
|
|
u16 reserved;
|
|
u16 byte_cnt; /* buffer byte count */
|
|
u32 buf_ptr; /* pointer to buffer for this descriptor */
|
|
u32 next_desc_ptr; /* Pointer to next descriptor */
|
|
};
|
|
|
|
struct pxa168_eth_private {
|
|
int port_num; /* User Ethernet port number */
|
|
int phy_addr;
|
|
int phy_speed;
|
|
int phy_duplex;
|
|
phy_interface_t phy_intf;
|
|
|
|
int rx_resource_err; /* Rx ring resource error flag */
|
|
|
|
/* Next available and first returning Rx resource */
|
|
int rx_curr_desc_q, rx_used_desc_q;
|
|
|
|
/* Next available and first returning Tx resource */
|
|
int tx_curr_desc_q, tx_used_desc_q;
|
|
|
|
struct rx_desc *p_rx_desc_area;
|
|
dma_addr_t rx_desc_dma;
|
|
int rx_desc_area_size;
|
|
struct sk_buff **rx_skb;
|
|
|
|
struct tx_desc *p_tx_desc_area;
|
|
dma_addr_t tx_desc_dma;
|
|
int tx_desc_area_size;
|
|
struct sk_buff **tx_skb;
|
|
|
|
struct work_struct tx_timeout_task;
|
|
|
|
struct net_device *dev;
|
|
struct napi_struct napi;
|
|
u8 work_todo;
|
|
int skb_size;
|
|
|
|
/* Size of Tx Ring per queue */
|
|
int tx_ring_size;
|
|
/* Number of tx descriptors in use */
|
|
int tx_desc_count;
|
|
/* Size of Rx Ring per queue */
|
|
int rx_ring_size;
|
|
/* Number of rx descriptors in use */
|
|
int rx_desc_count;
|
|
|
|
/*
|
|
* Used in case RX Ring is empty, which can occur when
|
|
* system does not have resources (skb's)
|
|
*/
|
|
struct timer_list timeout;
|
|
struct mii_bus *smi_bus;
|
|
|
|
/* clock */
|
|
struct clk *clk;
|
|
struct pxa168_eth_platform_data *pd;
|
|
/*
|
|
* Ethernet controller base address.
|
|
*/
|
|
void __iomem *base;
|
|
|
|
/* Pointer to the hardware address filter table */
|
|
void *htpr;
|
|
dma_addr_t htpr_dma;
|
|
};
|
|
|
|
struct addr_table_entry {
|
|
__le32 lo;
|
|
__le32 hi;
|
|
};
|
|
|
|
/* Bit fields of a Hash Table Entry */
|
|
enum hash_table_entry {
|
|
HASH_ENTRY_VALID = 1,
|
|
SKIP = 2,
|
|
HASH_ENTRY_RECEIVE_DISCARD = 4,
|
|
HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
|
|
};
|
|
|
|
static int pxa168_init_hw(struct pxa168_eth_private *pep);
|
|
static int pxa168_init_phy(struct net_device *dev);
|
|
static void eth_port_reset(struct net_device *dev);
|
|
static void eth_port_start(struct net_device *dev);
|
|
static int pxa168_eth_open(struct net_device *dev);
|
|
static int pxa168_eth_stop(struct net_device *dev);
|
|
|
|
static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
|
|
{
|
|
return readl_relaxed(pep->base + offset);
|
|
}
|
|
|
|
static inline void wrl(struct pxa168_eth_private *pep, int offset, u32 data)
|
|
{
|
|
writel_relaxed(data, pep->base + offset);
|
|
}
|
|
|
|
static void abort_dma(struct pxa168_eth_private *pep)
|
|
{
|
|
int delay;
|
|
int max_retries = 40;
|
|
|
|
do {
|
|
wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
|
|
udelay(100);
|
|
|
|
delay = 10;
|
|
while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
|
|
&& delay-- > 0) {
|
|
udelay(10);
|
|
}
|
|
} while (max_retries-- > 0 && delay <= 0);
|
|
|
|
if (max_retries <= 0)
|
|
netdev_err(pep->dev, "%s : DMA Stuck\n", __func__);
|
|
}
|
|
|
|
static void rxq_refill(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct sk_buff *skb;
|
|
struct rx_desc *p_used_rx_desc;
|
|
int used_rx_desc;
|
|
|
|
while (pep->rx_desc_count < pep->rx_ring_size) {
|
|
int size;
|
|
|
|
skb = netdev_alloc_skb(dev, pep->skb_size);
|
|
if (!skb)
|
|
break;
|
|
if (SKB_DMA_REALIGN)
|
|
skb_reserve(skb, SKB_DMA_REALIGN);
|
|
pep->rx_desc_count++;
|
|
/* Get 'used' Rx descriptor */
|
|
used_rx_desc = pep->rx_used_desc_q;
|
|
p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
|
|
size = skb_end_pointer(skb) - skb->data;
|
|
p_used_rx_desc->buf_ptr = dma_map_single(NULL,
|
|
skb->data,
|
|
size,
|
|
DMA_FROM_DEVICE);
|
|
p_used_rx_desc->buf_size = size;
|
|
pep->rx_skb[used_rx_desc] = skb;
|
|
|
|
/* Return the descriptor to DMA ownership */
|
|
dma_wmb();
|
|
p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
|
|
dma_wmb();
|
|
|
|
/* Move the used descriptor pointer to the next descriptor */
|
|
pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
|
|
|
|
/* Any Rx return cancels the Rx resource error status */
|
|
pep->rx_resource_err = 0;
|
|
|
|
skb_reserve(skb, ETH_HW_IP_ALIGN);
|
|
}
|
|
|
|
/*
|
|
* If RX ring is empty of SKB, set a timer to try allocating
|
|
* again at a later time.
|
|
*/
|
|
if (pep->rx_desc_count == 0) {
|
|
pep->timeout.expires = jiffies + (HZ / 10);
|
|
add_timer(&pep->timeout);
|
|
}
|
|
}
|
|
|
|
static inline void rxq_refill_timer_wrapper(struct timer_list *t)
|
|
{
|
|
struct pxa168_eth_private *pep = from_timer(pep, t, timeout);
|
|
napi_schedule(&pep->napi);
|
|
}
|
|
|
|
static inline u8 flip_8_bits(u8 x)
|
|
{
|
|
return (((x) & 0x01) << 3) | (((x) & 0x02) << 1)
|
|
| (((x) & 0x04) >> 1) | (((x) & 0x08) >> 3)
|
|
| (((x) & 0x10) << 3) | (((x) & 0x20) << 1)
|
|
| (((x) & 0x40) >> 1) | (((x) & 0x80) >> 3);
|
|
}
|
|
|
|
static void nibble_swap_every_byte(unsigned char *mac_addr)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ETH_ALEN; i++) {
|
|
mac_addr[i] = ((mac_addr[i] & 0x0f) << 4) |
|
|
((mac_addr[i] & 0xf0) >> 4);
|
|
}
|
|
}
|
|
|
|
static void inverse_every_nibble(unsigned char *mac_addr)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ETH_ALEN; i++)
|
|
mac_addr[i] = flip_8_bits(mac_addr[i]);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* This function will calculate the hash function of the address.
|
|
* Inputs
|
|
* mac_addr_orig - MAC address.
|
|
* Outputs
|
|
* return the calculated entry.
|
|
*/
|
|
static u32 hash_function(unsigned char *mac_addr_orig)
|
|
{
|
|
u32 hash_result;
|
|
u32 addr0;
|
|
u32 addr1;
|
|
u32 addr2;
|
|
u32 addr3;
|
|
unsigned char mac_addr[ETH_ALEN];
|
|
|
|
/* Make a copy of MAC address since we are going to performe bit
|
|
* operations on it
|
|
*/
|
|
memcpy(mac_addr, mac_addr_orig, ETH_ALEN);
|
|
|
|
nibble_swap_every_byte(mac_addr);
|
|
inverse_every_nibble(mac_addr);
|
|
|
|
addr0 = (mac_addr[5] >> 2) & 0x3f;
|
|
addr1 = (mac_addr[5] & 0x03) | (((mac_addr[4] & 0x7f)) << 2);
|
|
addr2 = ((mac_addr[4] & 0x80) >> 7) | mac_addr[3] << 1;
|
|
addr3 = (mac_addr[2] & 0xff) | ((mac_addr[1] & 1) << 8);
|
|
|
|
hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
|
|
hash_result = hash_result & 0x07ff;
|
|
return hash_result;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* This function will add/del an entry to the address table.
|
|
* Inputs
|
|
* pep - ETHERNET .
|
|
* mac_addr - MAC address.
|
|
* skip - if 1, skip this address.Used in case of deleting an entry which is a
|
|
* part of chain in the hash table.We can't just delete the entry since
|
|
* that will break the chain.We need to defragment the tables time to
|
|
* time.
|
|
* rd - 0 Discard packet upon match.
|
|
* - 1 Receive packet upon match.
|
|
* Outputs
|
|
* address table entry is added/deleted.
|
|
* 0 if success.
|
|
* -ENOSPC if table full
|
|
*/
|
|
static int add_del_hash_entry(struct pxa168_eth_private *pep,
|
|
unsigned char *mac_addr,
|
|
u32 rd, u32 skip, int del)
|
|
{
|
|
struct addr_table_entry *entry, *start;
|
|
u32 new_high;
|
|
u32 new_low;
|
|
u32 i;
|
|
|
|
new_low = (((mac_addr[1] >> 4) & 0xf) << 15)
|
|
| (((mac_addr[1] >> 0) & 0xf) << 11)
|
|
| (((mac_addr[0] >> 4) & 0xf) << 7)
|
|
| (((mac_addr[0] >> 0) & 0xf) << 3)
|
|
| (((mac_addr[3] >> 4) & 0x1) << 31)
|
|
| (((mac_addr[3] >> 0) & 0xf) << 27)
|
|
| (((mac_addr[2] >> 4) & 0xf) << 23)
|
|
| (((mac_addr[2] >> 0) & 0xf) << 19)
|
|
| (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
|
|
| HASH_ENTRY_VALID;
|
|
|
|
new_high = (((mac_addr[5] >> 4) & 0xf) << 15)
|
|
| (((mac_addr[5] >> 0) & 0xf) << 11)
|
|
| (((mac_addr[4] >> 4) & 0xf) << 7)
|
|
| (((mac_addr[4] >> 0) & 0xf) << 3)
|
|
| (((mac_addr[3] >> 5) & 0x7) << 0);
|
|
|
|
/*
|
|
* Pick the appropriate table, start scanning for free/reusable
|
|
* entries at the index obtained by hashing the specified MAC address
|
|
*/
|
|
start = pep->htpr;
|
|
entry = start + hash_function(mac_addr);
|
|
for (i = 0; i < HOP_NUMBER; i++) {
|
|
if (!(le32_to_cpu(entry->lo) & HASH_ENTRY_VALID)) {
|
|
break;
|
|
} else {
|
|
/* if same address put in same position */
|
|
if (((le32_to_cpu(entry->lo) & 0xfffffff8) ==
|
|
(new_low & 0xfffffff8)) &&
|
|
(le32_to_cpu(entry->hi) == new_high)) {
|
|
break;
|
|
}
|
|
}
|
|
if (entry == start + 0x7ff)
|
|
entry = start;
|
|
else
|
|
entry++;
|
|
}
|
|
|
|
if (((le32_to_cpu(entry->lo) & 0xfffffff8) != (new_low & 0xfffffff8)) &&
|
|
(le32_to_cpu(entry->hi) != new_high) && del)
|
|
return 0;
|
|
|
|
if (i == HOP_NUMBER) {
|
|
if (!del) {
|
|
netdev_info(pep->dev,
|
|
"%s: table section is full, need to "
|
|
"move to 16kB implementation?\n",
|
|
__FILE__);
|
|
return -ENOSPC;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Update the selected entry
|
|
*/
|
|
if (del) {
|
|
entry->hi = 0;
|
|
entry->lo = 0;
|
|
} else {
|
|
entry->hi = cpu_to_le32(new_high);
|
|
entry->lo = cpu_to_le32(new_low);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* Create an addressTable entry from MAC address info
|
|
* found in the specifed net_device struct
|
|
*
|
|
* Input : pointer to ethernet interface network device structure
|
|
* Output : N/A
|
|
*/
|
|
static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
|
|
unsigned char *oaddr,
|
|
unsigned char *addr)
|
|
{
|
|
/* Delete old entry */
|
|
if (oaddr)
|
|
add_del_hash_entry(pep, oaddr, 1, 0, HASH_DELETE);
|
|
/* Add new entry */
|
|
add_del_hash_entry(pep, addr, 1, 0, HASH_ADD);
|
|
}
|
|
|
|
static int init_hash_table(struct pxa168_eth_private *pep)
|
|
{
|
|
/*
|
|
* Hardware expects CPU to build a hash table based on a predefined
|
|
* hash function and populate it based on hardware address. The
|
|
* location of the hash table is identified by 32-bit pointer stored
|
|
* in HTPR internal register. Two possible sizes exists for the hash
|
|
* table 8kB (256kB of DRAM required (4 x 64 kB banks)) and 1/2kB
|
|
* (16kB of DRAM required (4 x 4 kB banks)).We currently only support
|
|
* 1/2kB.
|
|
*/
|
|
/* TODO: Add support for 8kB hash table and alternative hash
|
|
* function.Driver can dynamically switch to them if the 1/2kB hash
|
|
* table is full.
|
|
*/
|
|
if (!pep->htpr) {
|
|
pep->htpr = dma_zalloc_coherent(pep->dev->dev.parent,
|
|
HASH_ADDR_TABLE_SIZE,
|
|
&pep->htpr_dma, GFP_KERNEL);
|
|
if (!pep->htpr)
|
|
return -ENOMEM;
|
|
} else {
|
|
memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
|
|
}
|
|
wrl(pep, HTPR, pep->htpr_dma);
|
|
return 0;
|
|
}
|
|
|
|
static void pxa168_eth_set_rx_mode(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct netdev_hw_addr *ha;
|
|
u32 val;
|
|
|
|
val = rdl(pep, PORT_CONFIG);
|
|
if (dev->flags & IFF_PROMISC)
|
|
val |= PCR_PM;
|
|
else
|
|
val &= ~PCR_PM;
|
|
wrl(pep, PORT_CONFIG, val);
|
|
|
|
/*
|
|
* Remove the old list of MAC address and add dev->addr
|
|
* and multicast address.
|
|
*/
|
|
memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
|
|
update_hash_table_mac_address(pep, NULL, dev->dev_addr);
|
|
|
|
netdev_for_each_mc_addr(ha, dev)
|
|
update_hash_table_mac_address(pep, NULL, ha->addr);
|
|
}
|
|
|
|
static void pxa168_eth_get_mac_address(struct net_device *dev,
|
|
unsigned char *addr)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
unsigned int mac_h = rdl(pep, MAC_ADDR_HIGH);
|
|
unsigned int mac_l = rdl(pep, MAC_ADDR_LOW);
|
|
|
|
addr[0] = (mac_h >> 24) & 0xff;
|
|
addr[1] = (mac_h >> 16) & 0xff;
|
|
addr[2] = (mac_h >> 8) & 0xff;
|
|
addr[3] = mac_h & 0xff;
|
|
addr[4] = (mac_l >> 8) & 0xff;
|
|
addr[5] = mac_l & 0xff;
|
|
}
|
|
|
|
static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
|
|
{
|
|
struct sockaddr *sa = addr;
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
unsigned char oldMac[ETH_ALEN];
|
|
u32 mac_h, mac_l;
|
|
|
|
if (!is_valid_ether_addr(sa->sa_data))
|
|
return -EADDRNOTAVAIL;
|
|
memcpy(oldMac, dev->dev_addr, ETH_ALEN);
|
|
memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
|
|
|
|
mac_h = dev->dev_addr[0] << 24;
|
|
mac_h |= dev->dev_addr[1] << 16;
|
|
mac_h |= dev->dev_addr[2] << 8;
|
|
mac_h |= dev->dev_addr[3];
|
|
mac_l = dev->dev_addr[4] << 8;
|
|
mac_l |= dev->dev_addr[5];
|
|
wrl(pep, MAC_ADDR_HIGH, mac_h);
|
|
wrl(pep, MAC_ADDR_LOW, mac_l);
|
|
|
|
netif_addr_lock_bh(dev);
|
|
update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
|
|
netif_addr_unlock_bh(dev);
|
|
return 0;
|
|
}
|
|
|
|
static void eth_port_start(struct net_device *dev)
|
|
{
|
|
unsigned int val = 0;
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
int tx_curr_desc, rx_curr_desc;
|
|
|
|
phy_start(dev->phydev);
|
|
|
|
/* Assignment of Tx CTRP of given queue */
|
|
tx_curr_desc = pep->tx_curr_desc_q;
|
|
wrl(pep, ETH_C_TX_DESC_1,
|
|
(u32) (pep->tx_desc_dma + tx_curr_desc * sizeof(struct tx_desc)));
|
|
|
|
/* Assignment of Rx CRDP of given queue */
|
|
rx_curr_desc = pep->rx_curr_desc_q;
|
|
wrl(pep, ETH_C_RX_DESC_0,
|
|
(u32) (pep->rx_desc_dma + rx_curr_desc * sizeof(struct rx_desc)));
|
|
|
|
wrl(pep, ETH_F_RX_DESC_0,
|
|
(u32) (pep->rx_desc_dma + rx_curr_desc * sizeof(struct rx_desc)));
|
|
|
|
/* Clear all interrupts */
|
|
wrl(pep, INT_CAUSE, 0);
|
|
|
|
/* Enable all interrupts for receive, transmit and error. */
|
|
wrl(pep, INT_MASK, ALL_INTS);
|
|
|
|
val = rdl(pep, PORT_CONFIG);
|
|
val |= PCR_EN;
|
|
wrl(pep, PORT_CONFIG, val);
|
|
|
|
/* Start RX DMA engine */
|
|
val = rdl(pep, SDMA_CMD);
|
|
val |= SDMA_CMD_ERD;
|
|
wrl(pep, SDMA_CMD, val);
|
|
}
|
|
|
|
static void eth_port_reset(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
unsigned int val = 0;
|
|
|
|
/* Stop all interrupts for receive, transmit and error. */
|
|
wrl(pep, INT_MASK, 0);
|
|
|
|
/* Clear all interrupts */
|
|
wrl(pep, INT_CAUSE, 0);
|
|
|
|
/* Stop RX DMA */
|
|
val = rdl(pep, SDMA_CMD);
|
|
val &= ~SDMA_CMD_ERD; /* abort dma command */
|
|
|
|
/* Abort any transmit and receive operations and put DMA
|
|
* in idle state.
|
|
*/
|
|
abort_dma(pep);
|
|
|
|
/* Disable port */
|
|
val = rdl(pep, PORT_CONFIG);
|
|
val &= ~PCR_EN;
|
|
wrl(pep, PORT_CONFIG, val);
|
|
|
|
phy_stop(dev->phydev);
|
|
}
|
|
|
|
/*
|
|
* txq_reclaim - Free the tx desc data for completed descriptors
|
|
* If force is non-zero, frees uncompleted descriptors as well
|
|
*/
|
|
static int txq_reclaim(struct net_device *dev, int force)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct tx_desc *desc;
|
|
u32 cmd_sts;
|
|
struct sk_buff *skb;
|
|
int tx_index;
|
|
dma_addr_t addr;
|
|
int count;
|
|
int released = 0;
|
|
|
|
netif_tx_lock(dev);
|
|
|
|
pep->work_todo &= ~WORK_TX_DONE;
|
|
while (pep->tx_desc_count > 0) {
|
|
tx_index = pep->tx_used_desc_q;
|
|
desc = &pep->p_tx_desc_area[tx_index];
|
|
cmd_sts = desc->cmd_sts;
|
|
if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
|
|
if (released > 0) {
|
|
goto txq_reclaim_end;
|
|
} else {
|
|
released = -1;
|
|
goto txq_reclaim_end;
|
|
}
|
|
}
|
|
pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
|
|
pep->tx_desc_count--;
|
|
addr = desc->buf_ptr;
|
|
count = desc->byte_cnt;
|
|
skb = pep->tx_skb[tx_index];
|
|
if (skb)
|
|
pep->tx_skb[tx_index] = NULL;
|
|
|
|
if (cmd_sts & TX_ERROR) {
|
|
if (net_ratelimit())
|
|
netdev_err(dev, "Error in TX\n");
|
|
dev->stats.tx_errors++;
|
|
}
|
|
dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
|
|
if (skb)
|
|
dev_kfree_skb_irq(skb);
|
|
released++;
|
|
}
|
|
txq_reclaim_end:
|
|
netif_tx_unlock(dev);
|
|
return released;
|
|
}
|
|
|
|
static void pxa168_eth_tx_timeout(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
|
|
netdev_info(dev, "TX timeout desc_count %d\n", pep->tx_desc_count);
|
|
|
|
schedule_work(&pep->tx_timeout_task);
|
|
}
|
|
|
|
static void pxa168_eth_tx_timeout_task(struct work_struct *work)
|
|
{
|
|
struct pxa168_eth_private *pep = container_of(work,
|
|
struct pxa168_eth_private,
|
|
tx_timeout_task);
|
|
struct net_device *dev = pep->dev;
|
|
pxa168_eth_stop(dev);
|
|
pxa168_eth_open(dev);
|
|
}
|
|
|
|
static int rxq_process(struct net_device *dev, int budget)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct net_device_stats *stats = &dev->stats;
|
|
unsigned int received_packets = 0;
|
|
struct sk_buff *skb;
|
|
|
|
while (budget-- > 0) {
|
|
int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
|
|
struct rx_desc *rx_desc;
|
|
unsigned int cmd_sts;
|
|
|
|
/* Do not process Rx ring in case of Rx ring resource error */
|
|
if (pep->rx_resource_err)
|
|
break;
|
|
rx_curr_desc = pep->rx_curr_desc_q;
|
|
rx_used_desc = pep->rx_used_desc_q;
|
|
rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
|
|
cmd_sts = rx_desc->cmd_sts;
|
|
dma_rmb();
|
|
if (cmd_sts & (BUF_OWNED_BY_DMA))
|
|
break;
|
|
skb = pep->rx_skb[rx_curr_desc];
|
|
pep->rx_skb[rx_curr_desc] = NULL;
|
|
|
|
rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
|
|
pep->rx_curr_desc_q = rx_next_curr_desc;
|
|
|
|
/* Rx descriptors exhausted. */
|
|
/* Set the Rx ring resource error flag */
|
|
if (rx_next_curr_desc == rx_used_desc)
|
|
pep->rx_resource_err = 1;
|
|
pep->rx_desc_count--;
|
|
dma_unmap_single(NULL, rx_desc->buf_ptr,
|
|
rx_desc->buf_size,
|
|
DMA_FROM_DEVICE);
|
|
received_packets++;
|
|
/*
|
|
* Update statistics.
|
|
* Note byte count includes 4 byte CRC count
|
|
*/
|
|
stats->rx_packets++;
|
|
stats->rx_bytes += rx_desc->byte_cnt;
|
|
/*
|
|
* In case received a packet without first / last bits on OR
|
|
* the error summary bit is on, the packets needs to be droped.
|
|
*/
|
|
if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
|
|
(RX_FIRST_DESC | RX_LAST_DESC))
|
|
|| (cmd_sts & RX_ERROR)) {
|
|
|
|
stats->rx_dropped++;
|
|
if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
|
|
(RX_FIRST_DESC | RX_LAST_DESC)) {
|
|
if (net_ratelimit())
|
|
netdev_err(dev,
|
|
"Rx pkt on multiple desc\n");
|
|
}
|
|
if (cmd_sts & RX_ERROR)
|
|
stats->rx_errors++;
|
|
dev_kfree_skb_irq(skb);
|
|
} else {
|
|
/*
|
|
* The -4 is for the CRC in the trailer of the
|
|
* received packet
|
|
*/
|
|
skb_put(skb, rx_desc->byte_cnt - 4);
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
netif_receive_skb(skb);
|
|
}
|
|
}
|
|
/* Fill RX ring with skb's */
|
|
rxq_refill(dev);
|
|
return received_packets;
|
|
}
|
|
|
|
static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
|
|
struct net_device *dev)
|
|
{
|
|
u32 icr;
|
|
int ret = 0;
|
|
|
|
icr = rdl(pep, INT_CAUSE);
|
|
if (icr == 0)
|
|
return IRQ_NONE;
|
|
|
|
wrl(pep, INT_CAUSE, ~icr);
|
|
if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
|
|
pep->work_todo |= WORK_TX_DONE;
|
|
ret = 1;
|
|
}
|
|
if (icr & ICR_RXBUF)
|
|
ret = 1;
|
|
return ret;
|
|
}
|
|
|
|
static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
|
|
{
|
|
struct net_device *dev = (struct net_device *)dev_id;
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
|
|
if (unlikely(!pxa168_eth_collect_events(pep, dev)))
|
|
return IRQ_NONE;
|
|
/* Disable interrupts */
|
|
wrl(pep, INT_MASK, 0);
|
|
napi_schedule(&pep->napi);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void pxa168_eth_recalc_skb_size(struct pxa168_eth_private *pep)
|
|
{
|
|
int skb_size;
|
|
|
|
/*
|
|
* Reserve 2+14 bytes for an ethernet header (the hardware
|
|
* automatically prepends 2 bytes of dummy data to each
|
|
* received packet), 16 bytes for up to four VLAN tags, and
|
|
* 4 bytes for the trailing FCS -- 36 bytes total.
|
|
*/
|
|
skb_size = pep->dev->mtu + 36;
|
|
|
|
/*
|
|
* Make sure that the skb size is a multiple of 8 bytes, as
|
|
* the lower three bits of the receive descriptor's buffer
|
|
* size field are ignored by the hardware.
|
|
*/
|
|
pep->skb_size = (skb_size + 7) & ~7;
|
|
|
|
/*
|
|
* If NET_SKB_PAD is smaller than a cache line,
|
|
* netdev_alloc_skb() will cause skb->data to be misaligned
|
|
* to a cache line boundary. If this is the case, include
|
|
* some extra space to allow re-aligning the data area.
|
|
*/
|
|
pep->skb_size += SKB_DMA_REALIGN;
|
|
|
|
}
|
|
|
|
static int set_port_config_ext(struct pxa168_eth_private *pep)
|
|
{
|
|
int skb_size;
|
|
|
|
pxa168_eth_recalc_skb_size(pep);
|
|
if (pep->skb_size <= 1518)
|
|
skb_size = PCXR_MFL_1518;
|
|
else if (pep->skb_size <= 1536)
|
|
skb_size = PCXR_MFL_1536;
|
|
else if (pep->skb_size <= 2048)
|
|
skb_size = PCXR_MFL_2048;
|
|
else
|
|
skb_size = PCXR_MFL_64K;
|
|
|
|
/* Extended Port Configuration */
|
|
wrl(pep, PORT_CONFIG_EXT,
|
|
PCXR_AN_SPEED_DIS | /* Disable HW AN */
|
|
PCXR_AN_DUPLEX_DIS |
|
|
PCXR_AN_FLOWCTL_DIS |
|
|
PCXR_2BSM | /* Two byte prefix aligns IP hdr */
|
|
PCXR_DSCP_EN | /* Enable DSCP in IP */
|
|
skb_size | PCXR_FLP | /* do not force link pass */
|
|
PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pxa168_eth_adjust_link(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct phy_device *phy = dev->phydev;
|
|
u32 cfg, cfg_o = rdl(pep, PORT_CONFIG);
|
|
u32 cfgext, cfgext_o = rdl(pep, PORT_CONFIG_EXT);
|
|
|
|
cfg = cfg_o & ~PCR_DUPLEX_FULL;
|
|
cfgext = cfgext_o & ~(PCXR_SPEED_100 | PCXR_FLOWCTL_DIS | PCXR_RMII_EN);
|
|
|
|
if (phy->interface == PHY_INTERFACE_MODE_RMII)
|
|
cfgext |= PCXR_RMII_EN;
|
|
if (phy->speed == SPEED_100)
|
|
cfgext |= PCXR_SPEED_100;
|
|
if (phy->duplex)
|
|
cfg |= PCR_DUPLEX_FULL;
|
|
if (!phy->pause)
|
|
cfgext |= PCXR_FLOWCTL_DIS;
|
|
|
|
/* Bail out if there has nothing changed */
|
|
if (cfg == cfg_o && cfgext == cfgext_o)
|
|
return;
|
|
|
|
wrl(pep, PORT_CONFIG, cfg);
|
|
wrl(pep, PORT_CONFIG_EXT, cfgext);
|
|
|
|
phy_print_status(phy);
|
|
}
|
|
|
|
static int pxa168_init_phy(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct ethtool_link_ksettings cmd;
|
|
struct phy_device *phy = NULL;
|
|
int err;
|
|
|
|
if (dev->phydev)
|
|
return 0;
|
|
|
|
phy = mdiobus_scan(pep->smi_bus, pep->phy_addr);
|
|
if (IS_ERR(phy))
|
|
return PTR_ERR(phy);
|
|
|
|
err = phy_connect_direct(dev, phy, pxa168_eth_adjust_link,
|
|
pep->phy_intf);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.base.phy_address = pep->phy_addr;
|
|
cmd.base.speed = pep->phy_speed;
|
|
cmd.base.duplex = pep->phy_duplex;
|
|
bitmap_copy(cmd.link_modes.advertising, PHY_BASIC_FEATURES,
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
cmd.base.autoneg = AUTONEG_ENABLE;
|
|
|
|
if (cmd.base.speed != 0)
|
|
cmd.base.autoneg = AUTONEG_DISABLE;
|
|
|
|
return phy_ethtool_set_link_ksettings(dev, &cmd);
|
|
}
|
|
|
|
static int pxa168_init_hw(struct pxa168_eth_private *pep)
|
|
{
|
|
int err = 0;
|
|
|
|
/* Disable interrupts */
|
|
wrl(pep, INT_MASK, 0);
|
|
wrl(pep, INT_CAUSE, 0);
|
|
/* Write to ICR to clear interrupts. */
|
|
wrl(pep, INT_W_CLEAR, 0);
|
|
/* Abort any transmit and receive operations and put DMA
|
|
* in idle state.
|
|
*/
|
|
abort_dma(pep);
|
|
/* Initialize address hash table */
|
|
err = init_hash_table(pep);
|
|
if (err)
|
|
return err;
|
|
/* SDMA configuration */
|
|
wrl(pep, SDMA_CONFIG, SDCR_BSZ8 | /* Burst size = 32 bytes */
|
|
SDCR_RIFB | /* Rx interrupt on frame */
|
|
SDCR_BLMT | /* Little endian transmit */
|
|
SDCR_BLMR | /* Little endian receive */
|
|
SDCR_RC_MAX_RETRANS); /* Max retransmit count */
|
|
/* Port Configuration */
|
|
wrl(pep, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */
|
|
set_port_config_ext(pep);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int rxq_init(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct rx_desc *p_rx_desc;
|
|
int size = 0, i = 0;
|
|
int rx_desc_num = pep->rx_ring_size;
|
|
|
|
/* Allocate RX skb rings */
|
|
pep->rx_skb = kcalloc(rx_desc_num, sizeof(*pep->rx_skb), GFP_KERNEL);
|
|
if (!pep->rx_skb)
|
|
return -ENOMEM;
|
|
|
|
/* Allocate RX ring */
|
|
pep->rx_desc_count = 0;
|
|
size = pep->rx_ring_size * sizeof(struct rx_desc);
|
|
pep->rx_desc_area_size = size;
|
|
pep->p_rx_desc_area = dma_zalloc_coherent(pep->dev->dev.parent, size,
|
|
&pep->rx_desc_dma,
|
|
GFP_KERNEL);
|
|
if (!pep->p_rx_desc_area)
|
|
goto out;
|
|
|
|
/* initialize the next_desc_ptr links in the Rx descriptors ring */
|
|
p_rx_desc = pep->p_rx_desc_area;
|
|
for (i = 0; i < rx_desc_num; i++) {
|
|
p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
|
|
((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
|
|
}
|
|
/* Save Rx desc pointer to driver struct. */
|
|
pep->rx_curr_desc_q = 0;
|
|
pep->rx_used_desc_q = 0;
|
|
pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
|
|
return 0;
|
|
out:
|
|
kfree(pep->rx_skb);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void rxq_deinit(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
int curr;
|
|
|
|
/* Free preallocated skb's on RX rings */
|
|
for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size; curr++) {
|
|
if (pep->rx_skb[curr]) {
|
|
dev_kfree_skb(pep->rx_skb[curr]);
|
|
pep->rx_desc_count--;
|
|
}
|
|
}
|
|
if (pep->rx_desc_count)
|
|
netdev_err(dev, "Error in freeing Rx Ring. %d skb's still\n",
|
|
pep->rx_desc_count);
|
|
/* Free RX ring */
|
|
if (pep->p_rx_desc_area)
|
|
dma_free_coherent(pep->dev->dev.parent, pep->rx_desc_area_size,
|
|
pep->p_rx_desc_area, pep->rx_desc_dma);
|
|
kfree(pep->rx_skb);
|
|
}
|
|
|
|
static int txq_init(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct tx_desc *p_tx_desc;
|
|
int size = 0, i = 0;
|
|
int tx_desc_num = pep->tx_ring_size;
|
|
|
|
pep->tx_skb = kcalloc(tx_desc_num, sizeof(*pep->tx_skb), GFP_KERNEL);
|
|
if (!pep->tx_skb)
|
|
return -ENOMEM;
|
|
|
|
/* Allocate TX ring */
|
|
pep->tx_desc_count = 0;
|
|
size = pep->tx_ring_size * sizeof(struct tx_desc);
|
|
pep->tx_desc_area_size = size;
|
|
pep->p_tx_desc_area = dma_zalloc_coherent(pep->dev->dev.parent, size,
|
|
&pep->tx_desc_dma,
|
|
GFP_KERNEL);
|
|
if (!pep->p_tx_desc_area)
|
|
goto out;
|
|
/* Initialize the next_desc_ptr links in the Tx descriptors ring */
|
|
p_tx_desc = pep->p_tx_desc_area;
|
|
for (i = 0; i < tx_desc_num; i++) {
|
|
p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
|
|
((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
|
|
}
|
|
pep->tx_curr_desc_q = 0;
|
|
pep->tx_used_desc_q = 0;
|
|
pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
|
|
return 0;
|
|
out:
|
|
kfree(pep->tx_skb);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void txq_deinit(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
|
|
/* Free outstanding skb's on TX ring */
|
|
txq_reclaim(dev, 1);
|
|
BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
|
|
/* Free TX ring */
|
|
if (pep->p_tx_desc_area)
|
|
dma_free_coherent(pep->dev->dev.parent, pep->tx_desc_area_size,
|
|
pep->p_tx_desc_area, pep->tx_desc_dma);
|
|
kfree(pep->tx_skb);
|
|
}
|
|
|
|
static int pxa168_eth_open(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
int err;
|
|
|
|
err = pxa168_init_phy(dev);
|
|
if (err)
|
|
return err;
|
|
|
|
err = request_irq(dev->irq, pxa168_eth_int_handler, 0, dev->name, dev);
|
|
if (err) {
|
|
dev_err(&dev->dev, "can't assign irq\n");
|
|
return -EAGAIN;
|
|
}
|
|
pep->rx_resource_err = 0;
|
|
err = rxq_init(dev);
|
|
if (err != 0)
|
|
goto out_free_irq;
|
|
err = txq_init(dev);
|
|
if (err != 0)
|
|
goto out_free_rx_skb;
|
|
pep->rx_used_desc_q = 0;
|
|
pep->rx_curr_desc_q = 0;
|
|
|
|
/* Fill RX ring with skb's */
|
|
rxq_refill(dev);
|
|
pep->rx_used_desc_q = 0;
|
|
pep->rx_curr_desc_q = 0;
|
|
netif_carrier_off(dev);
|
|
napi_enable(&pep->napi);
|
|
eth_port_start(dev);
|
|
return 0;
|
|
out_free_rx_skb:
|
|
rxq_deinit(dev);
|
|
out_free_irq:
|
|
free_irq(dev->irq, dev);
|
|
return err;
|
|
}
|
|
|
|
static int pxa168_eth_stop(struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
eth_port_reset(dev);
|
|
|
|
/* Disable interrupts */
|
|
wrl(pep, INT_MASK, 0);
|
|
wrl(pep, INT_CAUSE, 0);
|
|
/* Write to ICR to clear interrupts. */
|
|
wrl(pep, INT_W_CLEAR, 0);
|
|
napi_disable(&pep->napi);
|
|
del_timer_sync(&pep->timeout);
|
|
netif_carrier_off(dev);
|
|
free_irq(dev->irq, dev);
|
|
rxq_deinit(dev);
|
|
txq_deinit(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
|
|
{
|
|
int retval;
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
|
|
dev->mtu = mtu;
|
|
retval = set_port_config_ext(pep);
|
|
|
|
if (!netif_running(dev))
|
|
return 0;
|
|
|
|
/*
|
|
* Stop and then re-open the interface. This will allocate RX
|
|
* skbs of the new MTU.
|
|
* There is a possible danger that the open will not succeed,
|
|
* due to memory being full.
|
|
*/
|
|
pxa168_eth_stop(dev);
|
|
if (pxa168_eth_open(dev)) {
|
|
dev_err(&dev->dev,
|
|
"fatal error on re-opening device after MTU change\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
|
|
{
|
|
int tx_desc_curr;
|
|
|
|
tx_desc_curr = pep->tx_curr_desc_q;
|
|
pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
|
|
BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
|
|
pep->tx_desc_count++;
|
|
|
|
return tx_desc_curr;
|
|
}
|
|
|
|
static int pxa168_rx_poll(struct napi_struct *napi, int budget)
|
|
{
|
|
struct pxa168_eth_private *pep =
|
|
container_of(napi, struct pxa168_eth_private, napi);
|
|
struct net_device *dev = pep->dev;
|
|
int work_done = 0;
|
|
|
|
/*
|
|
* We call txq_reclaim every time since in NAPI interupts are disabled
|
|
* and due to this we miss the TX_DONE interrupt,which is not updated in
|
|
* interrupt status register.
|
|
*/
|
|
txq_reclaim(dev, 0);
|
|
if (netif_queue_stopped(dev)
|
|
&& pep->tx_ring_size - pep->tx_desc_count > 1) {
|
|
netif_wake_queue(dev);
|
|
}
|
|
work_done = rxq_process(dev, budget);
|
|
if (work_done < budget) {
|
|
napi_complete_done(napi, work_done);
|
|
wrl(pep, INT_MASK, ALL_INTS);
|
|
}
|
|
|
|
return work_done;
|
|
}
|
|
|
|
static netdev_tx_t
|
|
pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
struct net_device_stats *stats = &dev->stats;
|
|
struct tx_desc *desc;
|
|
int tx_index;
|
|
int length;
|
|
|
|
tx_index = eth_alloc_tx_desc_index(pep);
|
|
desc = &pep->p_tx_desc_area[tx_index];
|
|
length = skb->len;
|
|
pep->tx_skb[tx_index] = skb;
|
|
desc->byte_cnt = length;
|
|
desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
|
|
|
|
skb_tx_timestamp(skb);
|
|
|
|
dma_wmb();
|
|
desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
|
|
TX_ZERO_PADDING | TX_LAST_DESC | TX_EN_INT;
|
|
wmb();
|
|
wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
|
|
|
|
stats->tx_bytes += length;
|
|
stats->tx_packets++;
|
|
netif_trans_update(dev);
|
|
if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
|
|
/* We handled the current skb, but now we are out of space.*/
|
|
netif_stop_queue(dev);
|
|
}
|
|
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
static int smi_wait_ready(struct pxa168_eth_private *pep)
|
|
{
|
|
int i = 0;
|
|
|
|
/* wait for the SMI register to become available */
|
|
for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
|
|
if (i == PHY_WAIT_ITERATIONS)
|
|
return -ETIMEDOUT;
|
|
msleep(10);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
|
|
{
|
|
struct pxa168_eth_private *pep = bus->priv;
|
|
int i = 0;
|
|
int val;
|
|
|
|
if (smi_wait_ready(pep)) {
|
|
netdev_warn(pep->dev, "pxa168_eth: SMI bus busy timeout\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
|
|
/* now wait for the data to be valid */
|
|
for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
|
|
if (i == PHY_WAIT_ITERATIONS) {
|
|
netdev_warn(pep->dev,
|
|
"pxa168_eth: SMI bus read not valid\n");
|
|
return -ENODEV;
|
|
}
|
|
msleep(10);
|
|
}
|
|
|
|
return val & 0xffff;
|
|
}
|
|
|
|
static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
|
|
u16 value)
|
|
{
|
|
struct pxa168_eth_private *pep = bus->priv;
|
|
|
|
if (smi_wait_ready(pep)) {
|
|
netdev_warn(pep->dev, "pxa168_eth: SMI bus busy timeout\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
|
|
SMI_OP_W | (value & 0xffff));
|
|
|
|
if (smi_wait_ready(pep)) {
|
|
netdev_err(pep->dev, "pxa168_eth: SMI bus busy timeout\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
|
|
int cmd)
|
|
{
|
|
if (dev->phydev)
|
|
return phy_mii_ioctl(dev->phydev, ifr, cmd);
|
|
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
static void pxa168_eth_netpoll(struct net_device *dev)
|
|
{
|
|
disable_irq(dev->irq);
|
|
pxa168_eth_int_handler(dev->irq, dev);
|
|
enable_irq(dev->irq);
|
|
}
|
|
#endif
|
|
|
|
static void pxa168_get_drvinfo(struct net_device *dev,
|
|
struct ethtool_drvinfo *info)
|
|
{
|
|
strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
|
|
strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
|
|
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
|
|
strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
|
|
}
|
|
|
|
static const struct ethtool_ops pxa168_ethtool_ops = {
|
|
.get_drvinfo = pxa168_get_drvinfo,
|
|
.nway_reset = phy_ethtool_nway_reset,
|
|
.get_link = ethtool_op_get_link,
|
|
.get_ts_info = ethtool_op_get_ts_info,
|
|
.get_link_ksettings = phy_ethtool_get_link_ksettings,
|
|
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
|
};
|
|
|
|
static const struct net_device_ops pxa168_eth_netdev_ops = {
|
|
.ndo_open = pxa168_eth_open,
|
|
.ndo_stop = pxa168_eth_stop,
|
|
.ndo_start_xmit = pxa168_eth_start_xmit,
|
|
.ndo_set_rx_mode = pxa168_eth_set_rx_mode,
|
|
.ndo_set_mac_address = pxa168_eth_set_mac_address,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_do_ioctl = pxa168_eth_do_ioctl,
|
|
.ndo_change_mtu = pxa168_eth_change_mtu,
|
|
.ndo_tx_timeout = pxa168_eth_tx_timeout,
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
.ndo_poll_controller = pxa168_eth_netpoll,
|
|
#endif
|
|
};
|
|
|
|
static int pxa168_eth_probe(struct platform_device *pdev)
|
|
{
|
|
struct pxa168_eth_private *pep = NULL;
|
|
struct net_device *dev = NULL;
|
|
struct resource *res;
|
|
struct clk *clk;
|
|
struct device_node *np;
|
|
const unsigned char *mac_addr = NULL;
|
|
int err;
|
|
|
|
printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
|
|
|
|
clk = devm_clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(clk)) {
|
|
dev_err(&pdev->dev, "Fast Ethernet failed to get clock\n");
|
|
return -ENODEV;
|
|
}
|
|
clk_prepare_enable(clk);
|
|
|
|
dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
|
|
if (!dev) {
|
|
err = -ENOMEM;
|
|
goto err_clk;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, dev);
|
|
pep = netdev_priv(dev);
|
|
pep->dev = dev;
|
|
pep->clk = clk;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
pep->base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(pep->base)) {
|
|
err = -ENOMEM;
|
|
goto err_netdev;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
BUG_ON(!res);
|
|
dev->irq = res->start;
|
|
dev->netdev_ops = &pxa168_eth_netdev_ops;
|
|
dev->watchdog_timeo = 2 * HZ;
|
|
dev->base_addr = 0;
|
|
dev->ethtool_ops = &pxa168_ethtool_ops;
|
|
|
|
/* MTU range: 68 - 9500 */
|
|
dev->min_mtu = ETH_MIN_MTU;
|
|
dev->max_mtu = 9500;
|
|
|
|
INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
|
|
|
|
if (pdev->dev.of_node)
|
|
mac_addr = of_get_mac_address(pdev->dev.of_node);
|
|
|
|
if (mac_addr && is_valid_ether_addr(mac_addr)) {
|
|
ether_addr_copy(dev->dev_addr, mac_addr);
|
|
} else {
|
|
/* try reading the mac address, if set by the bootloader */
|
|
pxa168_eth_get_mac_address(dev, dev->dev_addr);
|
|
if (!is_valid_ether_addr(dev->dev_addr)) {
|
|
dev_info(&pdev->dev, "Using random mac address\n");
|
|
eth_hw_addr_random(dev);
|
|
}
|
|
}
|
|
|
|
pep->rx_ring_size = NUM_RX_DESCS;
|
|
pep->tx_ring_size = NUM_TX_DESCS;
|
|
|
|
pep->pd = dev_get_platdata(&pdev->dev);
|
|
if (pep->pd) {
|
|
if (pep->pd->rx_queue_size)
|
|
pep->rx_ring_size = pep->pd->rx_queue_size;
|
|
|
|
if (pep->pd->tx_queue_size)
|
|
pep->tx_ring_size = pep->pd->tx_queue_size;
|
|
|
|
pep->port_num = pep->pd->port_number;
|
|
pep->phy_addr = pep->pd->phy_addr;
|
|
pep->phy_speed = pep->pd->speed;
|
|
pep->phy_duplex = pep->pd->duplex;
|
|
pep->phy_intf = pep->pd->intf;
|
|
|
|
if (pep->pd->init)
|
|
pep->pd->init();
|
|
} else if (pdev->dev.of_node) {
|
|
of_property_read_u32(pdev->dev.of_node, "port-id",
|
|
&pep->port_num);
|
|
|
|
np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
|
|
if (!np) {
|
|
dev_err(&pdev->dev, "missing phy-handle\n");
|
|
err = -EINVAL;
|
|
goto err_netdev;
|
|
}
|
|
of_property_read_u32(np, "reg", &pep->phy_addr);
|
|
pep->phy_intf = of_get_phy_mode(pdev->dev.of_node);
|
|
of_node_put(np);
|
|
}
|
|
|
|
/* Hardware supports only 3 ports */
|
|
BUG_ON(pep->port_num > 2);
|
|
netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
|
|
|
|
memset(&pep->timeout, 0, sizeof(struct timer_list));
|
|
timer_setup(&pep->timeout, rxq_refill_timer_wrapper, 0);
|
|
|
|
pep->smi_bus = mdiobus_alloc();
|
|
if (!pep->smi_bus) {
|
|
err = -ENOMEM;
|
|
goto err_netdev;
|
|
}
|
|
pep->smi_bus->priv = pep;
|
|
pep->smi_bus->name = "pxa168_eth smi";
|
|
pep->smi_bus->read = pxa168_smi_read;
|
|
pep->smi_bus->write = pxa168_smi_write;
|
|
snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%s-%d",
|
|
pdev->name, pdev->id);
|
|
pep->smi_bus->parent = &pdev->dev;
|
|
pep->smi_bus->phy_mask = 0xffffffff;
|
|
err = mdiobus_register(pep->smi_bus);
|
|
if (err)
|
|
goto err_free_mdio;
|
|
|
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
|
pxa168_init_hw(pep);
|
|
err = register_netdev(dev);
|
|
if (err)
|
|
goto err_mdiobus;
|
|
return 0;
|
|
|
|
err_mdiobus:
|
|
mdiobus_unregister(pep->smi_bus);
|
|
err_free_mdio:
|
|
mdiobus_free(pep->smi_bus);
|
|
err_netdev:
|
|
free_netdev(dev);
|
|
err_clk:
|
|
clk_disable_unprepare(clk);
|
|
return err;
|
|
}
|
|
|
|
static int pxa168_eth_remove(struct platform_device *pdev)
|
|
{
|
|
struct net_device *dev = platform_get_drvdata(pdev);
|
|
struct pxa168_eth_private *pep = netdev_priv(dev);
|
|
|
|
if (pep->htpr) {
|
|
dma_free_coherent(pep->dev->dev.parent, HASH_ADDR_TABLE_SIZE,
|
|
pep->htpr, pep->htpr_dma);
|
|
pep->htpr = NULL;
|
|
}
|
|
if (dev->phydev)
|
|
phy_disconnect(dev->phydev);
|
|
if (pep->clk) {
|
|
clk_disable_unprepare(pep->clk);
|
|
}
|
|
|
|
mdiobus_unregister(pep->smi_bus);
|
|
mdiobus_free(pep->smi_bus);
|
|
unregister_netdev(dev);
|
|
cancel_work_sync(&pep->tx_timeout_task);
|
|
free_netdev(dev);
|
|
return 0;
|
|
}
|
|
|
|
static void pxa168_eth_shutdown(struct platform_device *pdev)
|
|
{
|
|
struct net_device *dev = platform_get_drvdata(pdev);
|
|
eth_port_reset(dev);
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int pxa168_eth_resume(struct platform_device *pdev)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
#else
|
|
#define pxa168_eth_resume NULL
|
|
#define pxa168_eth_suspend NULL
|
|
#endif
|
|
|
|
static const struct of_device_id pxa168_eth_of_match[] = {
|
|
{ .compatible = "marvell,pxa168-eth" },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(of, pxa168_eth_of_match);
|
|
|
|
static struct platform_driver pxa168_eth_driver = {
|
|
.probe = pxa168_eth_probe,
|
|
.remove = pxa168_eth_remove,
|
|
.shutdown = pxa168_eth_shutdown,
|
|
.resume = pxa168_eth_resume,
|
|
.suspend = pxa168_eth_suspend,
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.of_match_table = of_match_ptr(pxa168_eth_of_match),
|
|
},
|
|
};
|
|
|
|
module_platform_driver(pxa168_eth_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
|
|
MODULE_ALIAS("platform:pxa168_eth");
|