mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 14:10:43 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
737 lines
18 KiB
C
737 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2011
|
|
* eInfochips Ltd. <www.einfochips.com>
|
|
* Written-by: Ajay Bhargav <contact@8051projects.net>
|
|
*
|
|
* (C) Copyright 2010
|
|
* Marvell Semiconductor <www.marvell.com>
|
|
* Contributor: Mahavir Jain <mjain@marvell.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <net.h>
|
|
#include <malloc.h>
|
|
#include <miiphy.h>
|
|
#include <netdev.h>
|
|
#include <asm/types.h>
|
|
#include <asm/byteorder.h>
|
|
#include <linux/err.h>
|
|
#include <linux/mii.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/armada100.h>
|
|
#include "armada100_fec.h"
|
|
|
|
#define PHY_ADR_REQ 0xFF /* Magic number to read/write PHY address */
|
|
|
|
#ifdef DEBUG
|
|
static int eth_dump_regs(struct eth_device *dev)
|
|
{
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
unsigned int i = 0;
|
|
|
|
printf("\noffset: phy_adr, value: 0x%x\n", readl(®s->phyadr));
|
|
printf("offset: smi, value: 0x%x\n", readl(®s->smi));
|
|
for (i = 0x400; i <= 0x4e4; i += 4)
|
|
printf("offset: 0x%x, value: 0x%x\n",
|
|
i, readl(ARMD1_FEC_BASE + i));
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int armdfec_phy_timeout(u32 *reg, u32 flag, int cond)
|
|
{
|
|
u32 timeout = PHY_WAIT_ITERATIONS;
|
|
u32 reg_val;
|
|
|
|
while (--timeout) {
|
|
reg_val = readl(reg);
|
|
if (cond && (reg_val & flag))
|
|
break;
|
|
else if (!cond && !(reg_val & flag))
|
|
break;
|
|
udelay(PHY_WAIT_MICRO_SECONDS);
|
|
}
|
|
return !timeout;
|
|
}
|
|
|
|
static int smi_reg_read(struct mii_dev *bus, int phy_addr, int devad,
|
|
int phy_reg)
|
|
{
|
|
u16 value = 0;
|
|
struct eth_device *dev = eth_get_dev_by_name(bus->name);
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
u32 val;
|
|
|
|
if (phy_addr == PHY_ADR_REQ && phy_reg == PHY_ADR_REQ) {
|
|
val = readl(®s->phyadr);
|
|
value = val & 0x1f;
|
|
return value;
|
|
}
|
|
|
|
/* check parameters */
|
|
if (phy_addr > PHY_MASK) {
|
|
printf("ARMD100 FEC: (%s) Invalid phy address: 0x%X\n",
|
|
__func__, phy_addr);
|
|
return -EINVAL;
|
|
}
|
|
if (phy_reg > PHY_MASK) {
|
|
printf("ARMD100 FEC: (%s) Invalid register offset: 0x%X\n",
|
|
__func__, phy_reg);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* wait for the SMI register to become available */
|
|
if (armdfec_phy_timeout(®s->smi, SMI_BUSY, false)) {
|
|
printf("ARMD100 FEC: (%s) PHY busy timeout\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
writel((phy_addr << 16) | (phy_reg << 21) | SMI_OP_R, ®s->smi);
|
|
|
|
/* now wait for the data to be valid */
|
|
if (armdfec_phy_timeout(®s->smi, SMI_R_VALID, true)) {
|
|
val = readl(®s->smi);
|
|
printf("ARMD100 FEC: (%s) PHY Read timeout, val=0x%x\n",
|
|
__func__, val);
|
|
return -1;
|
|
}
|
|
val = readl(®s->smi);
|
|
value = val & 0xffff;
|
|
|
|
return value;
|
|
}
|
|
|
|
static int smi_reg_write(struct mii_dev *bus, int phy_addr, int devad,
|
|
int phy_reg, u16 value)
|
|
{
|
|
struct eth_device *dev = eth_get_dev_by_name(bus->name);
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
|
|
if (phy_addr == PHY_ADR_REQ && phy_reg == PHY_ADR_REQ) {
|
|
clrsetbits_le32(®s->phyadr, 0x1f, value & 0x1f);
|
|
return 0;
|
|
}
|
|
|
|
/* check parameters */
|
|
if (phy_addr > PHY_MASK) {
|
|
printf("ARMD100 FEC: (%s) Invalid phy address\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
if (phy_reg > PHY_MASK) {
|
|
printf("ARMD100 FEC: (%s) Invalid register offset\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* wait for the SMI register to become available */
|
|
if (armdfec_phy_timeout(®s->smi, SMI_BUSY, false)) {
|
|
printf("ARMD100 FEC: (%s) PHY busy timeout\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
writel((phy_addr << 16) | (phy_reg << 21) | SMI_OP_W | (value & 0xffff),
|
|
®s->smi);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Abort any transmit and receive operations and put DMA
|
|
* in idle state. AT and AR bits are cleared upon entering
|
|
* in IDLE state. So poll those bits to verify operation.
|
|
*/
|
|
static void abortdma(struct eth_device *dev)
|
|
{
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
int delay;
|
|
int maxretries = 40;
|
|
u32 tmp;
|
|
|
|
while (--maxretries) {
|
|
writel(SDMA_CMD_AR | SDMA_CMD_AT, ®s->sdma_cmd);
|
|
udelay(100);
|
|
|
|
delay = 10;
|
|
while (--delay) {
|
|
tmp = readl(®s->sdma_cmd);
|
|
if (!(tmp & (SDMA_CMD_AR | SDMA_CMD_AT)))
|
|
break;
|
|
udelay(10);
|
|
}
|
|
if (delay)
|
|
break;
|
|
}
|
|
|
|
if (!maxretries)
|
|
printf("ARMD100 FEC: (%s) DMA Stuck\n", __func__);
|
|
}
|
|
|
|
static inline u32 nibble_swapping_32_bit(u32 x)
|
|
{
|
|
return ((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4);
|
|
}
|
|
|
|
static inline u32 nibble_swapping_16_bit(u32 x)
|
|
{
|
|
return ((x & 0x0000f0f0) >> 4) | ((x & 0x00000f0f) << 4);
|
|
}
|
|
|
|
static inline u32 flip_4_bits(u32 x)
|
|
{
|
|
return ((x & 0x01) << 3) | ((x & 0x002) << 1)
|
|
| ((x & 0x04) >> 1) | ((x & 0x008) >> 3);
|
|
}
|
|
|
|
/*
|
|
* This function will calculate the hash function of the address.
|
|
* depends on the hash mode and hash size.
|
|
* Inputs
|
|
* mach - the 2 most significant bytes of the MAC address.
|
|
* macl - the 4 least significant bytes of the MAC address.
|
|
* Outputs
|
|
* return the calculated entry.
|
|
*/
|
|
static u32 hash_function(u32 mach, u32 macl)
|
|
{
|
|
u32 hashresult;
|
|
u32 addrh;
|
|
u32 addrl;
|
|
u32 addr0;
|
|
u32 addr1;
|
|
u32 addr2;
|
|
u32 addr3;
|
|
u32 addrhswapped;
|
|
u32 addrlswapped;
|
|
|
|
addrh = nibble_swapping_16_bit(mach);
|
|
addrl = nibble_swapping_32_bit(macl);
|
|
|
|
addrhswapped = flip_4_bits(addrh & 0xf)
|
|
+ ((flip_4_bits((addrh >> 4) & 0xf)) << 4)
|
|
+ ((flip_4_bits((addrh >> 8) & 0xf)) << 8)
|
|
+ ((flip_4_bits((addrh >> 12) & 0xf)) << 12);
|
|
|
|
addrlswapped = flip_4_bits(addrl & 0xf)
|
|
+ ((flip_4_bits((addrl >> 4) & 0xf)) << 4)
|
|
+ ((flip_4_bits((addrl >> 8) & 0xf)) << 8)
|
|
+ ((flip_4_bits((addrl >> 12) & 0xf)) << 12)
|
|
+ ((flip_4_bits((addrl >> 16) & 0xf)) << 16)
|
|
+ ((flip_4_bits((addrl >> 20) & 0xf)) << 20)
|
|
+ ((flip_4_bits((addrl >> 24) & 0xf)) << 24)
|
|
+ ((flip_4_bits((addrl >> 28) & 0xf)) << 28);
|
|
|
|
addrh = addrhswapped;
|
|
addrl = addrlswapped;
|
|
|
|
addr0 = (addrl >> 2) & 0x03f;
|
|
addr1 = (addrl & 0x003) | (((addrl >> 8) & 0x7f) << 2);
|
|
addr2 = (addrl >> 15) & 0x1ff;
|
|
addr3 = ((addrl >> 24) & 0x0ff) | ((addrh & 1) << 8);
|
|
|
|
hashresult = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
|
|
hashresult = hashresult & 0x07ff;
|
|
return hashresult;
|
|
}
|
|
|
|
/*
|
|
* This function will add an entry to the address table.
|
|
* depends on the hash mode and hash size that was initialized.
|
|
* Inputs
|
|
* mach - the 2 most significant bytes of the MAC address.
|
|
* macl - the 4 least significant bytes of the MAC address.
|
|
* skip - if 1, skip this address.
|
|
* rd - the RD field in the address table.
|
|
* Outputs
|
|
* address table entry is added.
|
|
* 0 if success.
|
|
* -ENOSPC if table full
|
|
*/
|
|
static int add_del_hash_entry(struct armdfec_device *darmdfec, u32 mach,
|
|
u32 macl, u32 rd, u32 skip, int del)
|
|
{
|
|
struct addr_table_entry_t *entry, *start;
|
|
u32 newhi;
|
|
u32 newlo;
|
|
u32 i;
|
|
|
|
newlo = (((mach >> 4) & 0xf) << 15)
|
|
| (((mach >> 0) & 0xf) << 11)
|
|
| (((mach >> 12) & 0xf) << 7)
|
|
| (((mach >> 8) & 0xf) << 3)
|
|
| (((macl >> 20) & 0x1) << 31)
|
|
| (((macl >> 16) & 0xf) << 27)
|
|
| (((macl >> 28) & 0xf) << 23)
|
|
| (((macl >> 24) & 0xf) << 19)
|
|
| (skip << HTESKIP) | (rd << HTERDBIT)
|
|
| HTEVALID;
|
|
|
|
newhi = (((macl >> 4) & 0xf) << 15)
|
|
| (((macl >> 0) & 0xf) << 11)
|
|
| (((macl >> 12) & 0xf) << 7)
|
|
| (((macl >> 8) & 0xf) << 3)
|
|
| (((macl >> 21) & 0x7) << 0);
|
|
|
|
/*
|
|
* Pick the appropriate table, start scanning for free/reusable
|
|
* entries at the index obtained by hashing the specified MAC address
|
|
*/
|
|
start = (struct addr_table_entry_t *)(darmdfec->htpr);
|
|
entry = start + hash_function(mach, macl);
|
|
for (i = 0; i < HOP_NUMBER; i++) {
|
|
if (!(entry->lo & HTEVALID)) {
|
|
break;
|
|
} else {
|
|
/* if same address put in same position */
|
|
if (((entry->lo & 0xfffffff8) == (newlo & 0xfffffff8))
|
|
&& (entry->hi == newhi))
|
|
break;
|
|
}
|
|
if (entry == start + 0x7ff)
|
|
entry = start;
|
|
else
|
|
entry++;
|
|
}
|
|
|
|
if (((entry->lo & 0xfffffff8) != (newlo & 0xfffffff8)) &&
|
|
(entry->hi != newhi) && del)
|
|
return 0;
|
|
|
|
if (i == HOP_NUMBER) {
|
|
if (!del) {
|
|
printf("ARMD100 FEC: (%s) table section is full\n",
|
|
__func__);
|
|
return -ENOSPC;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the selected entry
|
|
*/
|
|
if (del) {
|
|
entry->hi = 0;
|
|
entry->lo = 0;
|
|
} else {
|
|
entry->hi = newhi;
|
|
entry->lo = newlo;
|
|
}
|
|
|
|
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 armdfec_device *darmdfec,
|
|
u8 *oaddr, u8 *addr)
|
|
{
|
|
u32 mach;
|
|
u32 macl;
|
|
|
|
/* Delete old entry */
|
|
if (oaddr) {
|
|
mach = (oaddr[0] << 8) | oaddr[1];
|
|
macl = (oaddr[2] << 24) | (oaddr[3] << 16) |
|
|
(oaddr[4] << 8) | oaddr[5];
|
|
add_del_hash_entry(darmdfec, mach, macl, 1, 0, HASH_DELETE);
|
|
}
|
|
|
|
/* Add new entry */
|
|
mach = (addr[0] << 8) | addr[1];
|
|
macl = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
|
|
add_del_hash_entry(darmdfec, mach, macl, 1, 0, HASH_ADD);
|
|
}
|
|
|
|
/* Address Table Initialization */
|
|
static void init_hashtable(struct eth_device *dev)
|
|
{
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
memset(darmdfec->htpr, 0, HASH_ADDR_TABLE_SIZE);
|
|
writel((u32)darmdfec->htpr, ®s->htpr);
|
|
}
|
|
|
|
/*
|
|
* This detects PHY chip from address 0-31 by reading PHY status
|
|
* registers. PHY chip can be connected at any of this address.
|
|
*/
|
|
static int ethernet_phy_detect(struct eth_device *dev)
|
|
{
|
|
u32 val;
|
|
u16 tmp, mii_status;
|
|
u8 addr;
|
|
|
|
for (addr = 0; addr < 32; addr++) {
|
|
if (miiphy_read(dev->name, addr, MII_BMSR, &mii_status) != 0)
|
|
/* try next phy */
|
|
continue;
|
|
|
|
/* invalid MII status. More validation required here... */
|
|
if (mii_status == 0 || mii_status == 0xffff)
|
|
/* try next phy */
|
|
continue;
|
|
|
|
if (miiphy_read(dev->name, addr, MII_PHYSID1, &tmp) != 0)
|
|
/* try next phy */
|
|
continue;
|
|
|
|
val = tmp << 16;
|
|
if (miiphy_read(dev->name, addr, MII_PHYSID2, &tmp) != 0)
|
|
/* try next phy */
|
|
continue;
|
|
|
|
val |= tmp;
|
|
|
|
if ((val & 0xfffffff0) != 0)
|
|
return addr;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void armdfec_init_rx_desc_ring(struct armdfec_device *darmdfec)
|
|
{
|
|
struct rx_desc *p_rx_desc;
|
|
int i;
|
|
|
|
/* initialize the Rx descriptors ring */
|
|
p_rx_desc = darmdfec->p_rxdesc;
|
|
for (i = 0; i < RINGSZ; i++) {
|
|
p_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
|
|
p_rx_desc->buf_size = PKTSIZE_ALIGN;
|
|
p_rx_desc->byte_cnt = 0;
|
|
p_rx_desc->buf_ptr = darmdfec->p_rxbuf + i * PKTSIZE_ALIGN;
|
|
if (i == (RINGSZ - 1)) {
|
|
p_rx_desc->nxtdesc_p = darmdfec->p_rxdesc;
|
|
} else {
|
|
p_rx_desc->nxtdesc_p = (struct rx_desc *)
|
|
((u32)p_rx_desc + ARMDFEC_RXQ_DESC_ALIGNED_SIZE);
|
|
p_rx_desc = p_rx_desc->nxtdesc_p;
|
|
}
|
|
}
|
|
darmdfec->p_rxdesc_curr = darmdfec->p_rxdesc;
|
|
}
|
|
|
|
static int armdfec_init(struct eth_device *dev, bd_t *bd)
|
|
{
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
int phy_adr;
|
|
u32 temp;
|
|
|
|
armdfec_init_rx_desc_ring(darmdfec);
|
|
|
|
/* Disable interrupts */
|
|
writel(0, ®s->im);
|
|
writel(0, ®s->ic);
|
|
/* Write to ICR to clear interrupts. */
|
|
writel(0, ®s->iwc);
|
|
|
|
/*
|
|
* Abort any transmit and receive operations and put DMA
|
|
* in idle state.
|
|
*/
|
|
abortdma(dev);
|
|
|
|
/* Initialize address hash table */
|
|
init_hashtable(dev);
|
|
|
|
/* SDMA configuration */
|
|
writel(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 */
|
|
®s->sdma_conf);
|
|
/* Port Configuration */
|
|
writel(PCR_HS, ®s->pconf); /* Hash size is 1/2kb */
|
|
|
|
/* Set extended port configuration */
|
|
writel(PCXR_2BSM | /* Two byte suffix aligns IP hdr */
|
|
PCXR_DSCP_EN | /* Enable DSCP in IP */
|
|
PCXR_MFL_1536 | /* Set MTU = 1536 */
|
|
PCXR_FLP | /* do not force link pass */
|
|
PCXR_TX_HIGH_PRI, /* Transmit - high priority queue */
|
|
®s->pconf_ext);
|
|
|
|
update_hash_table_mac_address(darmdfec, NULL, dev->enetaddr);
|
|
|
|
/* Update TX and RX queue descriptor register */
|
|
temp = (u32)®s->txcdp[TXQ];
|
|
writel((u32)darmdfec->p_txdesc, temp);
|
|
temp = (u32)®s->rxfdp[RXQ];
|
|
writel((u32)darmdfec->p_rxdesc, temp);
|
|
temp = (u32)®s->rxcdp[RXQ];
|
|
writel((u32)darmdfec->p_rxdesc_curr, temp);
|
|
|
|
/* Enable Interrupts */
|
|
writel(ALL_INTS, ®s->im);
|
|
|
|
/* Enable Ethernet Port */
|
|
setbits_le32(®s->pconf, PCR_EN);
|
|
|
|
/* Enable RX DMA engine */
|
|
setbits_le32(®s->sdma_cmd, SDMA_CMD_ERD);
|
|
|
|
#ifdef DEBUG
|
|
eth_dump_regs(dev);
|
|
#endif
|
|
|
|
#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII))
|
|
|
|
#if defined(CONFIG_PHY_BASE_ADR)
|
|
miiphy_write(dev->name, PHY_ADR_REQ, PHY_ADR_REQ, CONFIG_PHY_BASE_ADR);
|
|
#else
|
|
/* Search phy address from range 0-31 */
|
|
phy_adr = ethernet_phy_detect(dev);
|
|
if (phy_adr < 0) {
|
|
printf("ARMD100 FEC: PHY not detected at address range 0-31\n");
|
|
return -1;
|
|
} else {
|
|
debug("ARMD100 FEC: PHY detected at addr %d\n", phy_adr);
|
|
miiphy_write(dev->name, PHY_ADR_REQ, PHY_ADR_REQ, phy_adr);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN)
|
|
/* Wait up to 5s for the link status */
|
|
for (i = 0; i < 5; i++) {
|
|
u16 phy_adr;
|
|
|
|
miiphy_read(dev->name, 0xFF, 0xFF, &phy_adr);
|
|
/* Return if we get link up */
|
|
if (miiphy_link(dev->name, phy_adr))
|
|
return 0;
|
|
udelay(1000000);
|
|
}
|
|
|
|
printf("ARMD100 FEC: No link on %s\n", dev->name);
|
|
return -1;
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void armdfec_halt(struct eth_device *dev)
|
|
{
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
|
|
/* Stop RX DMA */
|
|
clrbits_le32(®s->sdma_cmd, SDMA_CMD_ERD);
|
|
|
|
/*
|
|
* Abort any transmit and receive operations and put DMA
|
|
* in idle state.
|
|
*/
|
|
abortdma(dev);
|
|
|
|
/* Disable interrupts */
|
|
writel(0, ®s->im);
|
|
writel(0, ®s->ic);
|
|
writel(0, ®s->iwc);
|
|
|
|
/* Disable Port */
|
|
clrbits_le32(®s->pconf, PCR_EN);
|
|
}
|
|
|
|
static int armdfec_send(struct eth_device *dev, void *dataptr, int datasize)
|
|
{
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct armdfec_reg *regs = darmdfec->regs;
|
|
struct tx_desc *p_txdesc = darmdfec->p_txdesc;
|
|
void *p = (void *)dataptr;
|
|
int retry = PHY_WAIT_ITERATIONS * PHY_WAIT_MICRO_SECONDS;
|
|
u32 cmd_sts, temp;
|
|
|
|
/* Copy buffer if it's misaligned */
|
|
if ((u32)dataptr & 0x07) {
|
|
if (datasize > PKTSIZE_ALIGN) {
|
|
printf("ARMD100 FEC: Non-aligned data too large (%d)\n",
|
|
datasize);
|
|
return -1;
|
|
}
|
|
memcpy(darmdfec->p_aligned_txbuf, p, datasize);
|
|
p = darmdfec->p_aligned_txbuf;
|
|
}
|
|
|
|
p_txdesc->cmd_sts = TX_ZERO_PADDING | TX_GEN_CRC;
|
|
p_txdesc->cmd_sts |= TX_FIRST_DESC | TX_LAST_DESC;
|
|
p_txdesc->cmd_sts |= BUF_OWNED_BY_DMA;
|
|
p_txdesc->cmd_sts |= TX_EN_INT;
|
|
p_txdesc->buf_ptr = p;
|
|
p_txdesc->byte_cnt = datasize;
|
|
|
|
/* Apply send command using high priority TX queue */
|
|
temp = (u32)®s->txcdp[TXQ];
|
|
writel((u32)p_txdesc, temp);
|
|
writel(SDMA_CMD_TXDL | SDMA_CMD_TXDH | SDMA_CMD_ERD, ®s->sdma_cmd);
|
|
|
|
/*
|
|
* wait for packet xmit completion
|
|
*/
|
|
cmd_sts = readl(&p_txdesc->cmd_sts);
|
|
while (cmd_sts & BUF_OWNED_BY_DMA) {
|
|
/* return fail if error is detected */
|
|
if ((cmd_sts & (TX_ERROR | TX_LAST_DESC)) ==
|
|
(TX_ERROR | TX_LAST_DESC)) {
|
|
printf("ARMD100 FEC: (%s) in xmit packet\n", __func__);
|
|
return -1;
|
|
}
|
|
cmd_sts = readl(&p_txdesc->cmd_sts);
|
|
if (!(retry--)) {
|
|
printf("ARMD100 FEC: (%s) xmit packet timeout!\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int armdfec_recv(struct eth_device *dev)
|
|
{
|
|
struct armdfec_device *darmdfec = to_darmdfec(dev);
|
|
struct rx_desc *p_rxdesc_curr = darmdfec->p_rxdesc_curr;
|
|
u32 cmd_sts;
|
|
u32 timeout = 0;
|
|
u32 temp;
|
|
|
|
/* wait untill rx packet available or timeout */
|
|
do {
|
|
if (timeout < PHY_WAIT_ITERATIONS * PHY_WAIT_MICRO_SECONDS) {
|
|
timeout++;
|
|
} else {
|
|
debug("ARMD100 FEC: %s time out...\n", __func__);
|
|
return -1;
|
|
}
|
|
} while (readl(&p_rxdesc_curr->cmd_sts) & BUF_OWNED_BY_DMA);
|
|
|
|
if (p_rxdesc_curr->byte_cnt != 0) {
|
|
debug("ARMD100 FEC: %s: Received %d byte Packet @ 0x%x"
|
|
"(cmd_sts= %08x)\n", __func__,
|
|
(u32)p_rxdesc_curr->byte_cnt,
|
|
(u32)p_rxdesc_curr->buf_ptr,
|
|
(u32)p_rxdesc_curr->cmd_sts);
|
|
}
|
|
|
|
/*
|
|
* In case received a packet without first/last bits on
|
|
* OR the error summary bit is on,
|
|
* the packets needs to be dropeed.
|
|
*/
|
|
cmd_sts = readl(&p_rxdesc_curr->cmd_sts);
|
|
|
|
if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
|
|
(RX_FIRST_DESC | RX_LAST_DESC)) {
|
|
printf("ARMD100 FEC: (%s) Dropping packet spread on"
|
|
" multiple descriptors\n", __func__);
|
|
} else if (cmd_sts & RX_ERROR) {
|
|
printf("ARMD100 FEC: (%s) Dropping packet with errors\n",
|
|
__func__);
|
|
} else {
|
|
/* !!! call higher layer processing */
|
|
debug("ARMD100 FEC: (%s) Sending Received packet to"
|
|
" upper layer (net_process_received_packet)\n", __func__);
|
|
|
|
/*
|
|
* let the upper layer handle the packet, subtract offset
|
|
* as two dummy bytes are added in received buffer see
|
|
* PORT_CONFIG_EXT register bit TWO_Byte_Stuff_Mode bit.
|
|
*/
|
|
net_process_received_packet(
|
|
p_rxdesc_curr->buf_ptr + RX_BUF_OFFSET,
|
|
(int)(p_rxdesc_curr->byte_cnt - RX_BUF_OFFSET));
|
|
}
|
|
/*
|
|
* free these descriptors and point next in the ring
|
|
*/
|
|
p_rxdesc_curr->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
|
|
p_rxdesc_curr->buf_size = PKTSIZE_ALIGN;
|
|
p_rxdesc_curr->byte_cnt = 0;
|
|
|
|
temp = (u32)&darmdfec->p_rxdesc_curr;
|
|
writel((u32)p_rxdesc_curr->nxtdesc_p, temp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int armada100_fec_register(unsigned long base_addr)
|
|
{
|
|
struct armdfec_device *darmdfec;
|
|
struct eth_device *dev;
|
|
|
|
darmdfec = malloc(sizeof(struct armdfec_device));
|
|
if (!darmdfec)
|
|
goto error;
|
|
|
|
memset(darmdfec, 0, sizeof(struct armdfec_device));
|
|
|
|
darmdfec->htpr = memalign(8, HASH_ADDR_TABLE_SIZE);
|
|
if (!darmdfec->htpr)
|
|
goto error1;
|
|
|
|
darmdfec->p_rxdesc = memalign(PKTALIGN,
|
|
ARMDFEC_RXQ_DESC_ALIGNED_SIZE * RINGSZ + 1);
|
|
|
|
if (!darmdfec->p_rxdesc)
|
|
goto error1;
|
|
|
|
darmdfec->p_rxbuf = memalign(PKTALIGN, RINGSZ * PKTSIZE_ALIGN + 1);
|
|
if (!darmdfec->p_rxbuf)
|
|
goto error1;
|
|
|
|
darmdfec->p_aligned_txbuf = memalign(8, PKTSIZE_ALIGN);
|
|
if (!darmdfec->p_aligned_txbuf)
|
|
goto error1;
|
|
|
|
darmdfec->p_txdesc = memalign(PKTALIGN, sizeof(struct tx_desc) + 1);
|
|
if (!darmdfec->p_txdesc)
|
|
goto error1;
|
|
|
|
dev = &darmdfec->dev;
|
|
/* Assign ARMADA100 Fast Ethernet Controller Base Address */
|
|
darmdfec->regs = (void *)base_addr;
|
|
|
|
/* must be less than sizeof(dev->name) */
|
|
strcpy(dev->name, "armd-fec0");
|
|
|
|
dev->init = armdfec_init;
|
|
dev->halt = armdfec_halt;
|
|
dev->send = armdfec_send;
|
|
dev->recv = armdfec_recv;
|
|
|
|
eth_register(dev);
|
|
|
|
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
|
|
int retval;
|
|
struct mii_dev *mdiodev = mdio_alloc();
|
|
if (!mdiodev)
|
|
return -ENOMEM;
|
|
strncpy(mdiodev->name, dev->name, MDIO_NAME_LEN);
|
|
mdiodev->read = smi_reg_read;
|
|
mdiodev->write = smi_reg_write;
|
|
|
|
retval = mdio_register(mdiodev);
|
|
if (retval < 0)
|
|
return retval;
|
|
#endif
|
|
return 0;
|
|
|
|
error1:
|
|
free(darmdfec->p_aligned_txbuf);
|
|
free(darmdfec->p_rxbuf);
|
|
free(darmdfec->p_rxdesc);
|
|
free(darmdfec->htpr);
|
|
error:
|
|
free(darmdfec);
|
|
printf("AMD100 FEC: (%s) Failed to allocate memory\n", __func__);
|
|
return -1;
|
|
}
|