From: Kuo-Jung Su <dant...@faraday-tech.com> Faraday FTMAC110 10/100Mbps supports half-word data transfer for Linux. However it has a weird DMA alignment issue:
(1) Tx DMA Buffer Address: 1 bytes aligned: Invalid 2 bytes aligned: O.K 4 bytes aligned: O.K (2) Rx DMA Buffer Address: 1 bytes aligned: Invalid 2 bytes aligned: O.K 4 bytes aligned: Invalid!!! Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> --- drivers/net/Makefile | 1 + drivers/net/ftmac110.c | 484 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/ftmac110.h | 131 +++++++++++++ 3 files changed, 616 insertions(+) create mode 100644 drivers/net/ftmac110.c create mode 100644 drivers/net/ftmac110.h diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 786a656..0e23817 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -46,6 +46,7 @@ COBJS-$(CONFIG_ETHOC) += ethoc.o COBJS-$(CONFIG_FEC_MXC) += fec_mxc.o COBJS-$(CONFIG_FSLDMAFEC) += fsl_mcdmafec.o mcfmii.o COBJS-$(CONFIG_FTGMAC100) += ftgmac100.o +COBJS-$(CONFIG_FTMAC110) += ftmac110.o COBJS-$(CONFIG_FTMAC100) += ftmac100.o COBJS-$(CONFIG_GRETH) += greth.o COBJS-$(CONFIG_INCA_IP_SWITCH) += inca-ip_sw.o diff --git a/drivers/net/ftmac110.c b/drivers/net/ftmac110.c new file mode 100644 index 0000000..67373f5 --- /dev/null +++ b/drivers/net/ftmac110.c @@ -0,0 +1,484 @@ +/* + * Faraday 10/100Mbps Ethernet Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dant...@faraday-tech.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <net.h> +#include <asm/dma-mapping.h> + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +#include <miiphy.h> +#endif + +#include "ftmac110.h" + +#ifndef EMAC_REG32 +#define EMAC_REG32(dev, off) *(volatile uint32_t *)((dev)->iobase + (off)) +#endif + +#define CFG_DEBUG_DESC 0 +#define CFG_DEBUG_TX 0 +#define CFG_DEBUG_RX 0 + +#define CFG_RXDES_NUM 8 +#define CFG_TXDES_NUM 2 +#define CFG_XBUF_SIZE 1536 + +/*******************************************************************/ +/* FTMAC110 DMA design issue */ +/* Dante Su 2010.02.03 */ +/* */ +/* The DMA engine has a weird restriction that its Rx DMA engine */ +/* accepts only 16-bits aligned address, 32-bits aligned is not */ +/* acceptable. However this restriction does not apply to Tx DMA. */ +/* Conclusion: */ +/* (1) Tx DMA Buffer Address: */ +/* 1 bytes aligned: Invalid */ +/* 2 bytes aligned: O.K */ +/* 4 bytes aligned: O.K (-> u-boot ZeroCopy is possible) */ +/* (2) Rx DMA Buffer Address: */ +/* 1 bytes aligned: Invalid */ +/* 2 bytes aligned: O.K */ +/* 4 bytes aligned: Invalid */ +/*******************************************************************/ + +struct ftmac110_priv { + uint32_t iobase; + uint32_t irqmask; + uint32_t maccr; + uint32_t lnkup; + + struct ftmac110_rxd *rx_descs; + ulong rx_descs_dma; + uint32_t rx_idx; + + struct ftmac110_txd *tx_descs; + ulong tx_descs_dma; + uint32_t tx_idx; + + uint32_t phy_addr; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static char ftmac110_mac_addr[] = {0x00, 0x41, 0x71, 0x00, 0x00, 0x52}; + +static int ftmac110_reset(struct eth_device *dev); + +static uint16_t mdio_read(struct eth_device *dev, uint8_t phyaddr, uint8_t phyreg) +{ + uint32_t tmp; + + tmp = MIIREG_READ + | (phyaddr << MIIREG_PHYADDR_SHIFT) + | (phyreg << MIIREG_PHYREG_SHIFT) + | 0x30000000; + + EMAC_REG32(dev, REG_PHYCR) = tmp; + + do { + tmp = EMAC_REG32(dev, REG_PHYCR); + } while (tmp & MIIREG_READ); + + return (uint16_t)(tmp & 0xFFFF); +} + +static void mdio_write(struct eth_device *dev, uint8_t phyaddr, uint8_t phyreg, uint16_t phydata) +{ + unsigned int tmp; + + tmp = MIIREG_WRITE + | (phyaddr << MIIREG_PHYADDR_SHIFT) + | (phyreg << MIIREG_PHYREG_SHIFT) + | 0x30000000; + + EMAC_REG32(dev, REG_PHYDR) = phydata; + EMAC_REG32(dev, REG_PHYCR) = tmp; + + do { + tmp = EMAC_REG32(dev, REG_PHYCR); + } while (tmp & MIIREG_WRITE); +} + +static uint32_t ftmac110_phyqry(struct eth_device *dev) +{ + ulong ts; + uint32_t maccr; + uint16_t pa, tmp; + struct ftmac110_priv *priv = dev->priv; + + maccr = MACCR_100M | MACCR_FD; + + /* 0. find the phy device */ + for (pa = 0; pa < 32; ++pa) { + tmp = mdio_read(dev, pa, MII_PHYSID1); + if (tmp == 0xFFFF || tmp == 0x0000) + continue; + break; + } + if (pa >= 32) { + puts("ftmac110: phy device not found!\n"); + return maccr; + } else { + priv->phy_addr = pa; + } + + /* 1. check link status */ + priv->lnkup = 0; + for (ts = get_timer(0); get_timer(ts) < 1000; ) { + if (mdio_read(dev, priv->phy_addr, MII_BMSR) & BMSR_LSTATUS) { + priv->lnkup = 1; + break; + } + } + if (!priv->lnkup) { + puts("ftmac110: link down\n"); + goto exit; + } + + /* 2. check A/N status */ + for (ts = get_timer(0); get_timer(ts) < 1000; ) { + if (mdio_read(dev, priv->phy_addr, MII_BMSR) & BMSR_ANEGCOMPLETE) + break; + } + if (get_timer(ts) >= 1000) { + puts("ftmac110: A/N failed\n"); + goto exit; + } + + /* 3. build MACCR with the PHY status */ + tmp = mdio_read(dev, priv->phy_addr, MII_ADVERTISE); + tmp &= mdio_read(dev, priv->phy_addr, MII_LPA); + + /* 3-1. 10/100Mbps Detection */ + if (tmp & LPA_100FULL) /* 100Mbps full-duplex */ + maccr = MACCR_100M | MACCR_FD; + else if (tmp & LPA_100HALF) /* 100Mbps half-duplex */ + maccr = MACCR_100M; + else if (tmp & LPA_10FULL) /* 10Mbps full-duplex */ + maccr = MACCR_FD; + else if (tmp & LPA_10HALF) /* 10Mbps half-duplex */ + maccr = 0; + else + maccr = MACCR_100M | MACCR_FD; + + printf("ftmac110: %d Mbps, %s\n", + (maccr & MACCR_100M) ? 100 : 10, + (maccr & MACCR_FD) ? "Full" : "half"); + +exit: + return maccr; +} + +static int ftmac110_reset(struct eth_device *dev) +{ + uint32_t i, tmp, maccr; + struct ftmac110_priv *priv = dev->priv; + + /* + * 1. MAC reset + */ + EMAC_REG32(priv, REG_MACCR) = MACCR_RESET; + do { + tmp = EMAC_REG32(priv, REG_MACCR); + } while (tmp & MACCR_RESET); + + /* 1-1. tx ring */ + for (i = 0; i < CFG_TXDES_NUM; ++i) + priv->tx_descs[i].owner = 0; /* owned by SW */ + priv->tx_idx = 0; + + /* 1-2. rx ring */ + for (i = 0; i < CFG_RXDES_NUM; ++i) { + priv->rx_descs[i].owner = 1; /* owned by HW */ + priv->rx_descs[i].bufsz = CFG_XBUF_SIZE; + } + priv->rx_idx = 0; + + /* + * 2. PHY status query + */ + maccr = ftmac110_phyqry(dev); + + /* + * 3. Fix up the MACCR value + */ + priv->maccr = maccr | MACCR_CRCAPD | MACCR_RXALL | MACCR_RXRUNT + | MACCR_RXEN | MACCR_TXEN | MACCR_RXDMAEN | MACCR_TXDMAEN; + priv->irqmask = 0; + + /* + * 4. MAC address setup + */ + do { + uint8_t *ma = dev->enetaddr; + EMAC_REG32(priv, REG_HMAC) = ma[1] | (ma[0] << 8); + EMAC_REG32(priv, REG_LMAC) = ma[5] | (ma[4] << 8) | (ma[3] << 16) | (ma[2] << 24); + } while (0); + + /* + * 5. MAC registers setup + */ + EMAC_REG32(priv, REG_RXBAR) = priv->rx_descs_dma; + EMAC_REG32(priv, REG_TXBAR) = priv->tx_descs_dma; + EMAC_REG32(priv, REG_ITC) = 0x00001010; + EMAC_REG32(priv, REG_APTC) = 0x00000001; + EMAC_REG32(priv, REG_DBLAC) = 0x00000390; + EMAC_REG32(priv, REG_ISR) = 0x000003FF; + EMAC_REG32(priv, REG_IMR) = priv->irqmask; + EMAC_REG32(priv, REG_MACCR) = priv->maccr; + + return 0; +} + +static int ftmac110_probe(struct eth_device *dev, bd_t *bis) +{ + debug("ftmac110: probe\n"); + + if (ftmac110_reset(dev)) + return -1; + + return 0; +} + +static void ftmac110_halt(struct eth_device *dev) +{ + struct ftmac110_priv *priv = dev->priv; + + EMAC_REG32(priv, REG_IMR) = 0; + EMAC_REG32(priv, REG_MACCR) = 0; + + debug("ftmac110: halt\n"); +} + +static int ftmac110_send(struct eth_device *dev, void *packet, int length) +{ + struct ftmac110_priv *priv = dev->priv; + struct ftmac110_txd *cur_desc; + + if (!priv->lnkup) + return 0; + + if (length <= 0 || length > CFG_XBUF_SIZE) { + printf("ftmac110: bad tx packet length(%d)\n", length); + return 0; + } + + if (length < 60) + length = 60; + + cur_desc = &priv->tx_descs[priv->tx_idx]; + if (cur_desc->owner) { + EMAC_REG32(priv, REG_TXPD) = 0xffffffff; /* kick-off Tx DMA */ + printf("ftmac110: out of txd\n"); + return 0; + } + + memcpy(cur_desc->vbuf, (void *)packet, length); + dma_map_single(cur_desc->vbuf, length, DMA_TO_DEVICE); + + cur_desc->len = length; + cur_desc->lts = 1; + cur_desc->fts = 1; + cur_desc->owner = 1; + + EMAC_REG32(priv, REG_TXPD) = 0xffffffff; /* kick-off Tx DMA */ + +#if CFG_DEBUG_TX + printf("ftmac110: txd[%d]@0x%08X, buf=0x%p(pa=0x%08X)\n", + priv->tx_idx, (uint32_t)cur_desc, + cur_desc->vbuf, cur_desc->buf); +#endif + + priv->tx_idx = (priv->tx_idx + 1) % CFG_TXDES_NUM; + + return length; +} + +static int ftmac110_recv(struct eth_device *dev) +{ + struct ftmac110_priv *priv = dev->priv; + struct ftmac110_rxd *cur_desc; + uint32_t rlen = 0; + uint8_t *buf; + uint16_t len; + + if (!priv->lnkup) + return 0; + + do { + cur_desc = &priv->rx_descs[priv->rx_idx]; + if (cur_desc->owner == 1) + break; + + len = cur_desc->len; + buf = cur_desc->vbuf; + + if (cur_desc->error) { + printf("ftmac110: rx error\n"); + } else { +#if CFG_DEBUG_RX + printf("ftmac110: rxd[%d]@0x%p, buf=0x%08X, len=%d\n", + priv->rx_idx, cur_desc, + (uint32_t)buf, len); +#endif + dma_map_single(buf, len, DMA_FROM_DEVICE); + NetReceive(buf, len); + rlen += len; + } + + cur_desc->len = 0; + cur_desc->owner = 1; /* owned by hardware */ + + priv->rx_idx = (priv->rx_idx + 1) % CFG_RXDES_NUM; + } while (0); + + return rlen; +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + +static int ftmac110_mdio_read (const char *devname, uint8_t addr, uint8_t reg, uint16_t *value) +{ + int ret = 0; + struct eth_device *dev; + + dev = eth_get_dev_by_name(devname); + if (dev == NULL) { + printf("%s: no such device\n", devname); + ret = -1; + } else { + *value = mdio_read(dev, addr, reg); + } + + return ret; +} + +static int ftmac110_mdio_write (const char *devname, uint8_t addr, uint8_t reg, uint16_t value) +{ + int ret = 0; + struct eth_device *dev; + + dev = eth_get_dev_by_name(devname); + if (dev == NULL) { + printf("%s: no such device\n", devname); + ret = -1; + } else { + mdio_write(dev, addr, reg, value); + } + + return ret; +} + +#endif /* #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) */ + +int ftmac110_initialize(bd_t *bis) +{ + int i, card_number = 0; + char enetvar[32]; + char *tmp, *end; + struct eth_device *dev; + struct ftmac110_priv *priv; + + dev = malloc(sizeof(struct eth_device) + sizeof(struct ftmac110_priv)); + if (dev == NULL) { + panic("ftmac110: out of memory 1\n"); + return -1; + } + priv = (struct ftmac110_priv *)(dev + 1); + memset(dev, 0, sizeof(struct eth_device) + sizeof(struct ftmac110_priv)); + + sprintf(dev->name, "FTMAC110#%d", card_number); + + dev->iobase = priv->iobase = CONFIG_FTMAC110_BASE; + dev->priv = priv; + dev->init = ftmac110_probe; + dev->halt = ftmac110_halt; + dev->send = ftmac110_send; + dev->recv = ftmac110_recv; + + sprintf(enetvar, card_number ? "eth%daddr" : "ethaddr", card_number); + + tmp = getenv(enetvar); + if (tmp != NULL) { + for (i = 0; i < 6; i++) { + dev->enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end + 1 : end; + } + } else { + memcpy(dev->enetaddr, ftmac110_mac_addr, 6); + } + + /* allocate tx descriptors (it must be 16 bytes aligned) */ + priv->tx_descs = dma_alloc_coherent(sizeof(struct ftmac110_txd) * CFG_TXDES_NUM, &priv->tx_descs_dma); + if (priv->tx_descs == NULL) + panic("ftmac110: out of memory 3\n"); + memset((void *)priv->tx_descs, 0, sizeof(struct ftmac110_txd) * CFG_TXDES_NUM); + for (i = 0; i < CFG_TXDES_NUM; ++i) { + void *va = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE); + if (!va) + panic("ftmac110: out of memory 4\n"); + priv->tx_descs[i].vbuf = va; + priv->tx_descs[i].buf = virt_to_phys(va); + priv->tx_descs[i].len = 0; + priv->tx_descs[i].end = 0; + priv->tx_descs[i].owner = 0; /* owned by SW */ + } + priv->tx_descs[CFG_TXDES_NUM - 1].end = 1; + priv->tx_idx = 0; + + /* allocate rx descriptors (it must be 16 bytes aligned) */ + priv->rx_descs = dma_alloc_coherent(sizeof(struct ftmac110_rxd) * CFG_RXDES_NUM, &priv->rx_descs_dma); + if (priv->rx_descs == NULL) + panic("ftmac110: out of memory 4\n"); + memset((void *)priv->rx_descs, 0, sizeof(struct ftmac110_rxd) * CFG_RXDES_NUM); + for (i = 0; i < CFG_RXDES_NUM; ++i) { + void *va = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE + 2); + if (!va) + panic("ftmac110: out of memory 5\n"); + /* it needs to be exactly 2 bytes aligned */ + va = ((uint8_t *)va + 2); + priv->rx_descs[i].vbuf = va; + priv->rx_descs[i].buf = (uint32_t)virt_to_phys(va); + priv->rx_descs[i].len = 0; + priv->rx_descs[i].bufsz = CFG_XBUF_SIZE; + priv->rx_descs[i].end = 0; + priv->rx_descs[i].owner = 1; /* owned by HW */ + } + priv->rx_descs[CFG_RXDES_NUM - 1].end = 1; + priv->rx_idx = 0; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, ftmac110_mdio_read, ftmac110_mdio_write); +#endif + +#if CFG_DEBUG_DESC + for (i = 0; i < CFG_TXDES_NUM; ++i) { + printf("TXD[%d]: 0x%08X, 0x%08X<=0x%08x\n", + i, (uint32_t)&priv->tx_descs[i], + (uint32_t)priv->tx_descs[i].vbuf, + (uint32_t)priv->tx_descs[i].buf); + } + for (i = 0; i < CFG_RXDES_NUM; ++i) { + printf("RXD[%d]: 0x%08X, 0x%08X<=0x%08X\n", + i, (uint32_t)&priv->rx_descs[i], + (uint32_t)priv->rx_descs[i].vbuf, + (uint32_t)priv->rx_descs[i].buf); + } +#endif + + card_number++; + + return card_number; +} diff --git a/drivers/net/ftmac110.h b/drivers/net/ftmac110.h new file mode 100644 index 0000000..b98bd7b --- /dev/null +++ b/drivers/net/ftmac110.h @@ -0,0 +1,131 @@ +/* + * Faraday 10/100Mbps Ethernet Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dant...@faraday-tech.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#ifndef __FTMAC110_H +#define __FTMAC110_H + +/* + * FTMAC110 MAC Registers + */ +#define REG_ISR 0x00 /* Interrups status register */ +#define REG_IMR 0x04 /* Interrupt maks register */ +#define REG_HMAC 0x08 /* MAC address (Most significant) */ +#define REG_LMAC 0x0c /* MAC address (Least significant) */ +#define REG_MHT0 0x10 /* Multicast Hash Table 0 register */ +#define REG_MHT1 0x14 /* Multicast Hash Table 1 register */ +#define REG_TXPD 0x18 /* Transmit Poll Demand register */ +#define REG_RXPD 0x1c /* Receive Poll Demand register */ +#define REG_TXBAR 0x20 /* Transmit Ring Base Address register */ +#define REG_RXBAR 0x24 /* Receive Ring Base Address register */ +#define REG_ITC 0x28 /* Interrupt timer control register */ +#define REG_APTC 0x2C /* Automatic Polling Timer control register */ +#define REG_DBLAC 0x30 /* DMA Burst Length and Arbitration control register */ + +#define REG_MACCR 0x88 /* MAC control register */ +#define REG_MACSR 0x8C /* MAC status register */ +#define REG_PHYCR 0x90 /* PHY control register */ +#define REG_PHYDR 0x94 /* PHY data register */ +#define REG_FCR 0x98 /* Flow Control register */ +#define REG_BPR 0x9c /* Back pressure register */ + +/* Interrupt status register(ISR), Interrupt mask register(IMR) bit setting */ +#define ISR_PHYSTCHG (1 << 9) /* phy status change */ +#define ISR_AHBERR (1 << 8) /* bus error */ +#define ISR_RXLOST (1 << 7) /* rx lost due to fifo overflow */ +#define ISR_RXFIFO (1 << 6) /* rx to fifo */ +#define ISR_TXLOST (1 << 5) /* tx lost due to collision */ +#define ISR_TXOK (1 << 4) /* tx to ethernet */ +#define ISR_NOTXBUF (1 << 3) /* out of tx buffer */ +#define ISR_TXFIFO (1 << 2) /* packets transmitted to fifo */ +#define ISR_NORXBUF (1 << 1) /* out of rx buffer */ +#define ISR_RXOK (1 << 0) /* packets received to buffer (ram) */ + +/* MACC control bits */ +#define MACCR_100M (1 << 18) /* 100Mbps mode */ +#define MACCR_RXBCST (1 << 17) /* receive all broadcast packet */ +#define MACCR_RXMCST (1 << 16) /* receive all multicast packet */ +#define MACCR_FD (1 << 15) /* full duplex */ +#define MACCR_CRCAPD (1 << 14) /* append crc to transmit packet */ +#define MACCR_RXALL (1 << 12) /* not check incoming packet's destination address */ +#define MACCR_RXFTL (1 << 11) /* store incoming packet even its length > 1518 byte */ +#define MACCR_RXRUNT (1 << 10) /* store incoming packet even its length < 64 byte */ +#define MACCR_RXMCSTHT (1 << 9) /* receive multicast packet by matching hash table */ +#define MACCR_RXEN (1 << 8) /* rx enable */ +#define MACCR_RXINHDTX (1 << 6) /* rx in half duplex tx */ +#define MACCR_TXEN (1 << 5) /* tx enable */ +#define MACCR_CRCDIS (1 << 4) /* store incoming packet even if crc error */ +#define MACCR_LOOPBACK (1 << 3) /* Internal loop-back */ +#define MACCR_RESET (1 << 2) /* reset */ +#define MACCR_RXDMAEN (1 << 1) /* enable rx dma */ +#define MACCR_TXDMAEN (1 << 0) /* enable tx dma */ + +/* + * MII PHY Registers + */ + +/* + * Bits related to the MII interface + */ +#define MIIREG_READ (1 << 26) +#define MIIREG_WRITE (1 << 27) +#define MIIREG_PHYREG_SHIFT 21 +#define MIIREG_PHYADDR_SHIFT 16 + +/* + * Receive Ring descriptor structure + */ +struct ftmac110_rxd { + /* RXDES0 */ + uint32_t len:11; + uint32_t rsvd1:5; + uint32_t mcast:1; + uint32_t bcast:1; + uint32_t error:5; + uint32_t rsvd2:5; + uint32_t lrs:1; + uint32_t frs:1; + uint32_t rsvd3:1; + uint32_t owner:1; /* BIT: 31 - 1:Hardware, 0: Software */ + + /* RXDES1 */ + uint32_t bufsz:11; + uint32_t rsvd4:20; + uint32_t end:1; + + /* RXDES2 */ + uint32_t buf; + + /* RXDES3 */ + void *vbuf; +}; + +struct ftmac110_txd { + /* TXDES0 */ + uint32_t collision:2; + uint32_t rsvd1:29; + uint32_t owner:1; /* BIT: 31 - 1:Hardware, 0: Software */ + + /* TXDES1 */ + uint32_t len:11; + uint32_t rsvd2:16; + uint32_t lts:1; + uint32_t fts:1; + uint32_t tx2fic:1; + uint32_t txic:1; + uint32_t end:1; + + /* TXDES2 */ + uint32_t buf; + + /* TXDES3 */ + void *vbuf; +}; + +#endif /* FTMAC110_H */ -- 1.7.9.5 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot