summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@ti.com>2013-06-24 22:27:44 -0400
committerTom Rini <trini@ti.com>2013-06-24 22:27:44 -0400
commitca85eb8c4271509aaac1ccb26ae3eb1a7827b4e6 (patch)
tree1532b13194c3d7daa8f1f10f3263fb806911cd67 /drivers
parent4e78e0317d3aab42c381b01033a0cd869ea28979 (diff)
parent433a2c5325b982b49b099e526d373d07d0cc5e97 (diff)
downloadu-boot-imx-ca85eb8c4271509aaac1ccb26ae3eb1a7827b4e6.zip
u-boot-imx-ca85eb8c4271509aaac1ccb26ae3eb1a7827b4e6.tar.gz
u-boot-imx-ca85eb8c4271509aaac1ccb26ae3eb1a7827b4e6.tar.bz2
Merge branch 'master' of git://git.denx.de/u-boot-net
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/Makefile3
-rw-r--r--drivers/net/designware.c4
-rw-r--r--drivers/net/ftgmac100.c70
-rw-r--r--drivers/net/ftmac110.c473
-rw-r--r--drivers/net/ftmac110.h177
-rw-r--r--drivers/net/ks8851_mll.c645
-rw-r--r--drivers/net/ks8851_mll.h357
-rw-r--r--drivers/net/macb.c185
-rw-r--r--drivers/net/macb.h55
-rw-r--r--drivers/net/mvgbe.c71
-rw-r--r--drivers/net/mvgbe.h7
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/atheros.c45
-rw-r--r--drivers/net/phy/icplus.c94
-rw-r--r--drivers/net/phy/marvell.c48
-rw-r--r--drivers/net/phy/micrel.c113
-rw-r--r--drivers/net/phy/natsemi.c37
-rw-r--r--drivers/net/phy/phy.c26
-rw-r--r--drivers/net/sunxi_wemac.c533
19 files changed, 2841 insertions, 103 deletions
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 786a656..9cf2983 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -46,10 +46,12 @@ 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
COBJS-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o
+COBJS-$(CONFIG_KS8851_MLL) += ks8851_mll.o
COBJS-$(CONFIG_LAN91C96) += lan91c96.o
COBJS-$(CONFIG_MACB) += macb.o
COBJS-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
@@ -68,6 +70,7 @@ COBJS-$(CONFIG_RTL8169) += rtl8169.o
COBJS-$(CONFIG_SH_ETHER) += sh_eth.o
COBJS-$(CONFIG_SMC91111) += smc91111.o
COBJS-$(CONFIG_SMC911X) += smc911x.o
+COBJS-$(CONFIG_SUNXI_WEMAC) += sunxi_wemac.o
COBJS-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
COBJS-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o
COBJS-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index bf21a08..46f6601 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -113,7 +113,9 @@ static int mac_reset(struct eth_device *dev)
int timeout = CONFIG_MACRESET_TIMEOUT;
writel(DMAMAC_SRST, &dma_p->busmode);
- writel(MII_PORTSELECT, &mac_p->conf);
+
+ if (priv->interface != PHY_INTERFACE_MODE_RGMII)
+ writel(MII_PORTSELECT, &mac_p->conf);
start = get_timer(0);
while (get_timer(start) < timeout) {
diff --git a/drivers/net/ftgmac100.c b/drivers/net/ftgmac100.c
index 69ba57d..2dbb328 100644
--- a/drivers/net/ftgmac100.c
+++ b/drivers/net/ftgmac100.c
@@ -27,11 +27,13 @@
#include <malloc.h>
#include <net.h>
#include <asm/io.h>
+#include <asm/dma-mapping.h>
#include <linux/mii.h>
#include "ftgmac100.h"
#define ETH_ZLEN 60
+#define CFG_XBUF_SIZE 1536
/* RBSR - hw default init value is also 0x640 */
#define RBSR_DEFAULT_VALUE 0x640
@@ -40,8 +42,10 @@
#define PKTBUFSTX 4 /* must be power of 2 */
struct ftgmac100_data {
- struct ftgmac100_txdes txdes[PKTBUFSTX];
- struct ftgmac100_rxdes rxdes[PKTBUFSRX];
+ ulong txdes_dma;
+ struct ftgmac100_txdes *txdes;
+ ulong rxdes_dma;
+ struct ftgmac100_rxdes *rxdes;
int tx_index;
int rx_index;
int phy_addr;
@@ -375,13 +379,34 @@ static int ftgmac100_init(struct eth_device *dev, bd_t *bd)
{
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
struct ftgmac100_data *priv = dev->priv;
- struct ftgmac100_txdes *txdes = priv->txdes;
- struct ftgmac100_rxdes *rxdes = priv->rxdes;
+ struct ftgmac100_txdes *txdes;
+ struct ftgmac100_rxdes *rxdes;
unsigned int maccr;
+ void *buf;
int i;
debug("%s()\n", __func__);
+ if (!priv->txdes) {
+ txdes = dma_alloc_coherent(
+ sizeof(*txdes) * PKTBUFSTX, &priv->txdes_dma);
+ if (!txdes)
+ panic("ftgmac100: out of memory\n");
+ memset(txdes, 0, sizeof(*txdes) * PKTBUFSTX);
+ priv->txdes = txdes;
+ }
+ txdes = priv->txdes;
+
+ if (!priv->rxdes) {
+ rxdes = dma_alloc_coherent(
+ sizeof(*rxdes) * PKTBUFSRX, &priv->rxdes_dma);
+ if (!rxdes)
+ panic("ftgmac100: out of memory\n");
+ memset(rxdes, 0, sizeof(*rxdes) * PKTBUFSRX);
+ priv->rxdes = rxdes;
+ }
+ rxdes = priv->rxdes;
+
/* set the ethernet address */
ftgmac100_set_mac_from_env(dev);
@@ -397,21 +422,31 @@ static int ftgmac100_init(struct eth_device *dev, bd_t *bd)
for (i = 0; i < PKTBUFSTX; i++) {
/* TXBUF_BADR */
- txdes[i].txdes3 = 0;
+ if (!txdes[i].txdes2) {
+ buf = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE);
+ if (!buf)
+ panic("ftgmac100: out of memory\n");
+ txdes[i].txdes3 = virt_to_phys(buf);
+ txdes[i].txdes2 = (uint)buf;
+ }
txdes[i].txdes1 = 0;
}
for (i = 0; i < PKTBUFSRX; i++) {
/* RXBUF_BADR */
- rxdes[i].rxdes3 = (unsigned int)NetRxPackets[i];
+ if (!rxdes[i].rxdes2) {
+ buf = NetRxPackets[i];
+ rxdes[i].rxdes3 = virt_to_phys(buf);
+ rxdes[i].rxdes2 = (uint)buf;
+ }
rxdes[i].rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY;
}
/* transmit ring */
- writel((unsigned int)txdes, &ftgmac100->txr_badr);
+ writel(priv->txdes_dma, &ftgmac100->txr_badr);
/* receive ring */
- writel((unsigned int)rxdes, &ftgmac100->rxr_badr);
+ writel(priv->rxdes_dma, &ftgmac100->rxr_badr);
/* poll receive descriptor automatically */
writel(FTGMAC100_APTC_RXPOLL_CNT(1), &ftgmac100->aptc);
@@ -466,8 +501,11 @@ static int ftgmac100_recv(struct eth_device *dev)
debug("%s(): RX buffer %d, %x received\n",
__func__, priv->rx_index, rxlen);
+ /* invalidate d-cache */
+ dma_map_single((void *)curr_des->rxdes2, rxlen, DMA_FROM_DEVICE);
+
/* pass the packet up to the protocol layers. */
- NetReceive((void *)curr_des->rxdes3, rxlen);
+ NetReceive((void *)curr_des->rxdes2, rxlen);
/* release buffer to DMA */
curr_des->rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY;
@@ -485,7 +523,6 @@ static int ftgmac100_send(struct eth_device *dev, void *packet, int length)
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
struct ftgmac100_data *priv = dev->priv;
struct ftgmac100_txdes *curr_des = &priv->txdes[priv->tx_index];
- int start;
if (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) {
debug("%s(): no TX descriptor available\n", __func__);
@@ -496,8 +533,8 @@ static int ftgmac100_send(struct eth_device *dev, void *packet, int length)
length = (length < ETH_ZLEN) ? ETH_ZLEN : length;
- /* initiate a transmit sequence */
- curr_des->txdes3 = (unsigned int)packet; /* TXBUF_BADR */
+ memcpy((void *)curr_des->txdes2, (void *)packet, length);
+ dma_map_single((void *)curr_des->txdes2, length, DMA_TO_DEVICE);
/* only one descriptor on TXBUF */
curr_des->txdes0 &= FTGMAC100_TXDES0_EDOTR;
@@ -509,15 +546,6 @@ static int ftgmac100_send(struct eth_device *dev, void *packet, int length)
/* start transmit */
writel(1, &ftgmac100->txpd);
- /* wait for transfer to succeed */
- start = get_timer(0);
- while (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) {
- if (get_timer(0) >= 5) {
- debug("%s(): timed out\n", __func__);
- return -1;
- }
- }
-
debug("%s(): packet sent\n", __func__);
priv->tx_index = (priv->tx_index + 1) % PKTBUFSTX;
diff --git a/drivers/net/ftmac110.c b/drivers/net/ftmac110.c
new file mode 100644
index 0000000..579dcc7
--- /dev/null
+++ b/drivers/net/ftmac110.c
@@ -0,0 +1,473 @@
+/*
+ * Faraday 10/100Mbps Ethernet Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@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/errno.h>
+#include <asm/io.h>
+#include <asm/dma-mapping.h>
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+#include <miiphy.h>
+#endif
+
+#include "ftmac110.h"
+
+#define CFG_RXDES_NUM 8
+#define CFG_TXDES_NUM 2
+#define CFG_XBUF_SIZE 1536
+
+#define CFG_MDIORD_TIMEOUT (CONFIG_SYS_HZ >> 1) /* 500 ms */
+#define CFG_MDIOWR_TIMEOUT (CONFIG_SYS_HZ >> 1) /* 500 ms */
+#define CFG_LINKUP_TIMEOUT (CONFIG_SYS_HZ << 2) /* 4 sec */
+
+/*
+ * FTMAC110 DMA design issue
+ *
+ * Its 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_chip {
+ void __iomem *regs;
+ uint32_t imr;
+ uint32_t maccr;
+ uint32_t lnkup;
+ uint32_t phy_addr;
+
+ struct ftmac110_rxd *rxd;
+ ulong rxd_dma;
+ uint32_t rxd_idx;
+
+ struct ftmac110_txd *txd;
+ ulong txd_dma;
+ uint32_t txd_idx;
+};
+
+static int ftmac110_reset(struct eth_device *dev);
+
+static uint16_t mdio_read(struct eth_device *dev,
+ uint8_t phyaddr, uint8_t phyreg)
+{
+ struct ftmac110_chip *chip = dev->priv;
+ struct ftmac110_regs __iomem *regs = chip->regs;
+ uint32_t tmp, ts;
+ uint16_t ret = 0xffff;
+
+ tmp = PHYCR_READ
+ | (phyaddr << PHYCR_ADDR_SHIFT)
+ | (phyreg << PHYCR_REG_SHIFT);
+
+ writel(tmp, &regs->phycr);
+
+ for (ts = get_timer(0); get_timer(ts) < CFG_MDIORD_TIMEOUT; ) {
+ tmp = readl(&regs->phycr);
+ if (tmp & PHYCR_READ)
+ continue;
+ break;
+ }
+
+ if (tmp & PHYCR_READ)
+ printf("ftmac110: mdio read timeout\n");
+ else
+ ret = (uint16_t)(tmp & 0xffff);
+
+ return ret;
+}
+
+static void mdio_write(struct eth_device *dev,
+ uint8_t phyaddr, uint8_t phyreg, uint16_t phydata)
+{
+ struct ftmac110_chip *chip = dev->priv;
+ struct ftmac110_regs __iomem *regs = chip->regs;
+ uint32_t tmp, ts;
+
+ tmp = PHYCR_WRITE
+ | (phyaddr << PHYCR_ADDR_SHIFT)
+ | (phyreg << PHYCR_REG_SHIFT);
+
+ writel(phydata, &regs->phydr);
+ writel(tmp, &regs->phycr);
+
+ for (ts = get_timer(0); get_timer(ts) < CFG_MDIOWR_TIMEOUT; ) {
+ if (readl(&regs->phycr) & PHYCR_WRITE)
+ continue;
+ break;
+ }
+
+ if (readl(&regs->phycr) & PHYCR_WRITE)
+ printf("ftmac110: mdio write timeout\n");
+}
+
+static uint32_t ftmac110_phyqry(struct eth_device *dev)
+{
+ ulong ts;
+ uint32_t maccr;
+ uint16_t pa, tmp, bmsr, bmcr;
+ struct ftmac110_chip *chip = dev->priv;
+
+ /* Default = 100Mbps Full */
+ maccr = MACCR_100M | MACCR_FD;
+
+ /* 1. find the phy device */
+ for (pa = 0; pa < 32; ++pa) {
+ tmp = mdio_read(dev, pa, MII_PHYSID1);
+ if (tmp == 0xFFFF || tmp == 0x0000)
+ continue;
+ chip->phy_addr = pa;
+ break;
+ }
+ if (pa >= 32) {
+ puts("ftmac110: phy device not found!\n");
+ goto exit;
+ }
+
+ /* 2. wait until link-up & auto-negotiation complete */
+ chip->lnkup = 0;
+ bmcr = mdio_read(dev, chip->phy_addr, MII_BMCR);
+ ts = get_timer(0);
+ do {
+ bmsr = mdio_read(dev, chip->phy_addr, MII_BMSR);
+ chip->lnkup = (bmsr & BMSR_LSTATUS) ? 1 : 0;
+ if (!chip->lnkup)
+ continue;
+ if (!(bmcr & BMCR_ANENABLE) || (bmsr & BMSR_ANEGCOMPLETE))
+ break;
+ } while (get_timer(ts) < CFG_LINKUP_TIMEOUT);
+ if (!chip->lnkup) {
+ puts("ftmac110: link down\n");
+ goto exit;
+ }
+ if (!(bmcr & BMCR_ANENABLE))
+ puts("ftmac110: auto negotiation disabled\n");
+ else if (!(bmsr & BMSR_ANEGCOMPLETE))
+ puts("ftmac110: auto negotiation timeout\n");
+
+ /* 3. derive MACCR */
+ if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_ANEGCOMPLETE)) {
+ tmp = mdio_read(dev, chip->phy_addr, MII_ADVERTISE);
+ tmp &= mdio_read(dev, chip->phy_addr, MII_LPA);
+ 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 {
+ if (bmcr & BMCR_SPEED100)
+ maccr = MACCR_100M;
+ else
+ maccr = 0;
+ if (bmcr & BMCR_FULLDPLX)
+ maccr |= MACCR_FD;
+ }
+
+exit:
+ printf("ftmac110: %d Mbps, %s\n",
+ (maccr & MACCR_100M) ? 100 : 10,
+ (maccr & MACCR_FD) ? "Full" : "half");
+ return maccr;
+}
+
+static int ftmac110_reset(struct eth_device *dev)
+{
+ uint8_t *a;
+ uint32_t i, maccr;
+ struct ftmac110_chip *chip = dev->priv;
+ struct ftmac110_regs __iomem *regs = chip->regs;
+
+ /* 1. MAC reset */
+ writel(MACCR_RESET, &regs->maccr);
+ for (i = get_timer(0); get_timer(i) < 1000; ) {
+ if (readl(&regs->maccr) & MACCR_RESET)
+ continue;
+ break;
+ }
+ if (readl(&regs->maccr) & MACCR_RESET) {
+ printf("ftmac110: reset failed\n");
+ return -ENXIO;
+ }
+
+ /* 1-1. Init tx ring */
+ for (i = 0; i < CFG_TXDES_NUM; ++i) {
+ /* owned by SW */
+ chip->txd[i].ct[0] = 0;
+ }
+ chip->txd_idx = 0;
+
+ /* 1-2. Init rx ring */
+ for (i = 0; i < CFG_RXDES_NUM; ++i) {
+ /* owned by HW */
+ chip->rxd[i].ct[0] = cpu_to_le32(FTMAC110_RXCT0_OWNER);
+ }
+ chip->rxd_idx = 0;
+
+ /* 2. PHY status query */
+ maccr = ftmac110_phyqry(dev);
+
+ /* 3. Fix up the MACCR value */
+ chip->maccr = maccr | MACCR_CRCAPD | MACCR_RXALL | MACCR_RXRUNT
+ | MACCR_RXEN | MACCR_TXEN | MACCR_RXDMAEN | MACCR_TXDMAEN;
+
+ /* 4. MAC address setup */
+ a = dev->enetaddr;
+ writel(a[1] | (a[0] << 8), &regs->mac[0]);
+ writel(a[5] | (a[4] << 8) | (a[3] << 16)
+ | (a[2] << 24), &regs->mac[1]);
+
+ /* 5. MAC registers setup */
+ writel(chip->rxd_dma, &regs->rxba);
+ writel(chip->txd_dma, &regs->txba);
+ /* interrupt at each tx/rx */
+ writel(ITC_DEFAULT, &regs->itc);
+ /* no tx pool, rx poll = 1 normal cycle */
+ writel(APTC_DEFAULT, &regs->aptc);
+ /* rx threshold = [6/8 fifo, 2/8 fifo] */
+ writel(DBLAC_DEFAULT, &regs->dblac);
+ /* disable & clear all interrupt status */
+ chip->imr = 0;
+ writel(ISR_ALL, &regs->isr);
+ writel(chip->imr, &regs->imr);
+ /* enable mac */
+ writel(chip->maccr, &regs->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_chip *chip = dev->priv;
+ struct ftmac110_regs __iomem *regs = chip->regs;
+
+ writel(0, &regs->imr);
+ writel(0, &regs->maccr);
+
+ debug("ftmac110: halt\n");
+}
+
+static int ftmac110_send(struct eth_device *dev, void *pkt, int len)
+{
+ struct ftmac110_chip *chip = dev->priv;
+ struct ftmac110_regs __iomem *regs = chip->regs;
+ struct ftmac110_txd *des;
+
+ if (!chip->lnkup)
+ return 0;
+
+ if (len <= 0 || len > CFG_XBUF_SIZE) {
+ printf("ftmac110: bad tx pkt len(%d)\n", len);
+ return 0;
+ }
+
+ len = max(60, len);
+
+ des = &chip->txd[chip->txd_idx];
+ if (le32_to_cpu(des->ct[0]) & FTMAC110_TXCT0_OWNER) {
+ /* kick-off Tx DMA */
+ writel(0xffffffff, &regs->txpd);
+ printf("ftmac110: out of txd\n");
+ return 0;
+ }
+
+ memcpy(des->vbuf, (void *)pkt, len);
+ dma_map_single(des->vbuf, len, DMA_TO_DEVICE);
+
+ /* update len, fts and lts */
+ des->ct[1] &= cpu_to_le32(FTMAC110_TXCT1_END);
+ des->ct[1] |= cpu_to_le32(FTMAC110_TXCT1_LEN(len)
+ | FTMAC110_TXCT1_FTS | FTMAC110_TXCT1_LTS);
+
+ /* set owner bit and clear others */
+ des->ct[0] = cpu_to_le32(FTMAC110_TXCT0_OWNER);
+
+ /* kick-off Tx DMA */
+ writel(0xffffffff, &regs->txpd);
+
+ chip->txd_idx = (chip->txd_idx + 1) % CFG_TXDES_NUM;
+
+ return len;
+}
+
+static int ftmac110_recv(struct eth_device *dev)
+{
+ struct ftmac110_chip *chip = dev->priv;
+ struct ftmac110_rxd *des;
+ uint32_t ct0, len, rlen = 0;
+ uint8_t *buf;
+
+ if (!chip->lnkup)
+ return 0;
+
+ do {
+ des = &chip->rxd[chip->rxd_idx];
+ ct0 = le32_to_cpu(des->ct[0]);
+ if (ct0 & FTMAC110_RXCT0_OWNER)
+ break;
+
+ len = FTMAC110_RXCT0_LEN(ct0);
+ buf = des->vbuf;
+
+ if (ct0 & FTMAC110_RXCT0_ERRMASK) {
+ printf("ftmac110: rx error\n");
+ } else {
+ dma_map_single(buf, len, DMA_FROM_DEVICE);
+ NetReceive(buf, len);
+ rlen += len;
+ }
+
+ /* owned by hardware */
+ des->ct[0] = cpu_to_le32(FTMAC110_RXCT0_OWNER);
+
+ chip->rxd_idx = (chip->rxd_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_nr = 0;
+ struct eth_device *dev;
+ struct ftmac110_chip *chip;
+
+ dev = malloc(sizeof(*dev) + sizeof(*chip));
+ if (dev == NULL) {
+ panic("ftmac110: out of memory 1\n");
+ return -1;
+ }
+ chip = (struct ftmac110_chip *)(dev + 1);
+ memset(dev, 0, sizeof(*dev) + sizeof(*chip));
+
+ sprintf(dev->name, "FTMAC110#%d", card_nr);
+
+ dev->iobase = CONFIG_FTMAC110_BASE;
+ chip->regs = (void __iomem *)dev->iobase;
+ dev->priv = chip;
+ dev->init = ftmac110_probe;
+ dev->halt = ftmac110_halt;
+ dev->send = ftmac110_send;
+ dev->recv = ftmac110_recv;
+
+ if (!eth_getenv_enetaddr_by_index("eth", card_nr, dev->enetaddr))
+ eth_random_enetaddr(dev->enetaddr);
+
+ /* allocate tx descriptors (it must be 16 bytes aligned) */
+ chip->txd = dma_alloc_coherent(
+ sizeof(struct ftmac110_txd) * CFG_TXDES_NUM, &chip->txd_dma);
+ if (!chip->txd)
+ panic("ftmac110: out of memory 3\n");
+ memset(chip->txd, 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");
+ chip->txd[i].vbuf = va;
+ chip->txd[i].buf = cpu_to_le32(virt_to_phys(va));
+ chip->txd[i].ct[1] = 0;
+ chip->txd[i].ct[0] = 0; /* owned by SW */
+ }
+ chip->txd[i - 1].ct[1] |= cpu_to_le32(FTMAC110_TXCT1_END);
+ chip->txd_idx = 0;
+
+ /* allocate rx descriptors (it must be 16 bytes aligned) */
+ chip->rxd = dma_alloc_coherent(
+ sizeof(struct ftmac110_rxd) * CFG_RXDES_NUM, &chip->rxd_dma);
+ if (!chip->rxd)
+ panic("ftmac110: out of memory 4\n");
+ memset((void *)chip->rxd, 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);
+ chip->rxd[i].vbuf = va;
+ chip->rxd[i].buf = cpu_to_le32(virt_to_phys(va));
+ chip->rxd[i].ct[1] = cpu_to_le32(CFG_XBUF_SIZE);
+ chip->rxd[i].ct[0] = cpu_to_le32(FTMAC110_RXCT0_OWNER);
+ }
+ chip->rxd[i - 1].ct[1] |= cpu_to_le32(FTMAC110_RXCT1_END);
+ chip->rxd_idx = 0;
+
+ eth_register(dev);
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+ miiphy_register(dev->name, ftmac110_mdio_read, ftmac110_mdio_write);
+#endif
+
+ card_nr++;
+
+ return card_nr;
+}
diff --git a/drivers/net/ftmac110.h b/drivers/net/ftmac110.h
new file mode 100644
index 0000000..5b2d23b
--- /dev/null
+++ b/drivers/net/ftmac110.h
@@ -0,0 +1,177 @@
+/*
+ * Faraday 10/100Mbps Ethernet Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@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
+
+struct ftmac110_regs {
+ uint32_t isr; /* 0x00: Interrups Status Register */
+ uint32_t imr; /* 0x04: Interrupt Mask Register */
+ uint32_t mac[2]; /* 0x08: MAC Address */
+ uint32_t mht[2]; /* 0x10: Multicast Hash Table Register */
+ uint32_t txpd; /* 0x18: Tx Poll Demand Register */
+ uint32_t rxpd; /* 0x1c: Rx Poll Demand Register */
+ uint32_t txba; /* 0x20: Tx Ring Base Address Register */
+ uint32_t rxba; /* 0x24: Rx Ring Base Address Register */
+ uint32_t itc; /* 0x28: Interrupt Timer Control Register */
+ uint32_t aptc; /* 0x2C: Automatic Polling Timer Control Register */
+ uint32_t dblac; /* 0x30: DMA Burst Length&Arbitration Control */
+ uint32_t revr; /* 0x34: Revision Register */
+ uint32_t fear; /* 0x38: Feature Register */
+ uint32_t rsvd[19];
+ uint32_t maccr; /* 0x88: MAC Control Register */
+ uint32_t macsr; /* 0x8C: MAC Status Register */
+ uint32_t phycr; /* 0x90: PHY Control Register */
+ uint32_t phydr; /* 0x94: PHY Data Register */
+ uint32_t fcr; /* 0x98: Flow Control Register */
+ uint32_t bpr; /* 0x9C: Back Pressure Register */
+};
+
+/*
+ * Interrupt status/mask register(ISR/IMR) bits
+ */
+#define ISR_ALL 0x3ff
+#define ISR_PHYSTCHG (1 << 9) /* phy status change */
+#define ISR_AHBERR (1 << 8) /* bus error */
+#define ISR_RXLOST (1 << 7) /* rx lost */
+#define ISR_RXFIFO (1 << 6) /* rx to fifo */
+#define ISR_TXLOST (1 << 5) /* tx lost */
+#define ISR_TXOK (1 << 4) /* tx to ethernet */
+#define ISR_NOTXBUF (1 << 3) /* out of tx buffer */
+#define ISR_TXFIFO (1 << 2) /* tx to fifo */
+#define ISR_NORXBUF (1 << 1) /* out of rx buffer */
+#define ISR_RXOK (1 << 0) /* rx to buffer */
+
+/*
+ * MACCR control bits
+ */
+#define MACCR_100M (1 << 18) /* 100Mbps mode */
+#define MACCR_RXBCST (1 << 17) /* rx broadcast packet */
+#define MACCR_RXMCST (1 << 16) /* rx multicast packet */
+#define MACCR_FD (1 << 15) /* full duplex */
+#define MACCR_CRCAPD (1 << 14) /* tx crc append */
+#define MACCR_RXALL (1 << 12) /* rx all packets */
+#define MACCR_RXFTL (1 << 11) /* rx packet even it's > 1518 byte */
+#define MACCR_RXRUNT (1 << 10) /* rx packet even it's < 64 byte */
+#define MACCR_RXMCSTHT (1 << 9) /* rx multicast 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) /* tx packet even it's crc error */
+#define MACCR_LOOPBACK (1 << 3) /* loop-back */
+#define MACCR_RESET (1 << 2) /* reset */
+#define MACCR_RXDMAEN (1 << 1) /* rx dma enable */
+#define MACCR_TXDMAEN (1 << 0) /* tx dma enable */
+
+/*
+ * PHYCR control bits
+ */
+#define PHYCR_READ (1 << 26)
+#define PHYCR_WRITE (1 << 27)
+#define PHYCR_REG_SHIFT 21
+#define PHYCR_ADDR_SHIFT 16
+
+/*
+ * ITC control bits
+ */
+
+/* Tx Cycle Length */
+#define ITC_TX_CYCLONG (1 << 15) /* 100Mbps=81.92us; 10Mbps=819.2us */
+#define ITC_TX_CYCNORM (0 << 15) /* 100Mbps=5.12us; 10Mbps=51.2us */
+/* Tx Threshold: Aggregate n interrupts as 1 interrupt */
+#define ITC_TX_THR(n) (((n) & 0x7) << 12)
+/* Tx Interrupt Timeout = n * Tx Cycle */
+#define ITC_TX_ITMO(n) (((n) & 0xf) << 8)
+/* Rx Cycle Length */
+#define ITC_RX_CYCLONG (1 << 7) /* 100Mbps=81.92us; 10Mbps=819.2us */
+#define ITC_RX_CYCNORM (0 << 7) /* 100Mbps=5.12us; 10Mbps=51.2us */
+/* Rx Threshold: Aggregate n interrupts as 1 interrupt */
+#define ITC_RX_THR(n) (((n) & 0x7) << 4)
+/* Rx Interrupt Timeout = n * Rx Cycle */
+#define ITC_RX_ITMO(n) (((n) & 0xf) << 0)
+
+#define ITC_DEFAULT \
+ (ITC_TX_THR(1) | ITC_TX_ITMO(0) | ITC_RX_THR(1) | ITC_RX_ITMO(0))
+
+/*
+ * APTC contrl bits
+ */
+
+/* Tx Cycle Length */
+#define APTC_TX_CYCLONG (1 << 12) /* 100Mbps=81.92us; 10Mbps=819.2us */
+#define APTC_TX_CYCNORM (0 << 12) /* 100Mbps=5.12us; 10Mbps=51.2us */
+/* Tx Poll Timeout = n * Tx Cycle, 0=No auto polling */
+#define APTC_TX_PTMO(n) (((n) & 0xf) << 8)
+/* Rx Cycle Length */
+#define APTC_RX_CYCLONG (1 << 4) /* 100Mbps=81.92us; 10Mbps=819.2us */
+#define APTC_RX_CYCNORM (0 << 4) /* 100Mbps=5.12us; 10Mbps=51.2us */
+/* Rx Poll Timeout = n * Rx Cycle, 0=No auto polling */
+#define APTC_RX_PTMO(n) (((n) & 0xf) << 0)
+
+#define APTC_DEFAULT (APTC_TX_PTMO(0) | APTC_RX_PTMO(1))
+
+/*
+ * DBLAC contrl bits
+ */
+#define DBLAC_BURST_MAX_ANY (0 << 14) /* un-limited */
+#define DBLAC_BURST_MAX_32X4 (2 << 14) /* max = 32 x 4 bytes */
+#define DBLAC_BURST_MAX_64X4 (3 << 14) /* max = 64 x 4 bytes */
+#define DBLAC_RXTHR_EN (1 << 9) /* enable rx threshold arbitration */
+#define DBLAC_RXTHR_HIGH(n) (((n) & 0x7) << 6) /* upper bound = n/8 fifo */
+#define DBLAC_RXTHR_LOW(n) (((n) & 0x7) << 3) /* lower bound = n/8 fifo */
+#define DBLAC_BURST_CAP16 (1 << 2) /* support burst 16 */
+#define DBLAC_BURST_CAP8 (1 << 1) /* support burst 8 */
+#define DBLAC_BURST_CAP4 (1 << 0) /* support burst 4 */
+
+#define DBLAC_DEFAULT \
+ (DBLAC_RXTHR_EN | DBLAC_RXTHR_HIGH(6) | DBLAC_RXTHR_LOW(2))
+
+/*
+ * descriptor structure
+ */
+struct ftmac110_rxd {
+ uint32_t ct[2];
+ uint32_t buf;
+ void *vbuf; /* reserved */
+};
+
+#define FTMAC110_RXCT0_OWNER BIT_MASK(31) /* owner: 1=HW, 0=SW */
+#define FTMAC110_RXCT0_FRS BIT_MASK(29) /* first pkt desc */
+#define FTMAC110_RXCT0_LRS BIT_MASK(28) /* last pkt desc */
+#define FTMAC110_RXCT0_ODDNB BIT_MASK(22) /* odd nibble */
+#define FTMAC110_RXCT0_RUNT BIT_MASK(21) /* runt pkt */
+#define FTMAC110_RXCT0_FTL BIT_MASK(20) /* frame too long */
+#define FTMAC110_RXCT0_CRC BIT_MASK(19) /* pkt crc error */
+#define FTMAC110_RXCT0_ERR BIT_MASK(18) /* bus error */
+#define FTMAC110_RXCT0_ERRMASK (0x1f << 18) /* all errors */
+#define FTMAC110_RXCT0_BCST BIT_MASK(17) /* Bcst pkt */
+#define FTMAC110_RXCT0_MCST BIT_MASK(16) /* Mcst pkt */
+#define FTMAC110_RXCT0_LEN(x) ((x) & 0x7ff)
+
+#define FTMAC110_RXCT1_END BIT_MASK(31)
+#define FTMAC110_RXCT1_BUFSZ(x) ((x) & 0x7ff)
+
+struct ftmac110_txd {
+ uint32_t ct[2];
+ uint32_t buf;
+ void *vbuf; /* reserved */
+};
+
+#define FTMAC110_TXCT0_OWNER BIT_MASK(31) /* owner: 1=HW, 0=SW */
+#define FTMAC110_TXCT0_COL 0x00000003 /* collision */
+
+#define FTMAC110_TXCT1_END BIT_MASK(31) /* end of ring */
+#define FTMAC110_TXCT1_TXIC BIT_MASK(30) /* tx done interrupt */
+#define FTMAC110_TXCT1_TX2FIC BIT_MASK(29) /* tx fifo interrupt */
+#define FTMAC110_TXCT1_FTS BIT_MASK(28) /* first pkt desc */
+#define FTMAC110_TXCT1_LTS BIT_MASK(27) /* last pkt desc */
+#define FTMAC110_TXCT1_LEN(x) ((x) & 0x7ff)
+
+#endif /* FTMAC110_H */
diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c
new file mode 100644
index 0000000..b02d59a
--- /dev/null
+++ b/drivers/net/ks8851_mll.c
@@ -0,0 +1,645 @@
+/*
+ * Micrel KS8851_MLL 16bit Network driver
+ * Copyright (c) 2011 Roberto Cerati <roberto.cerati@bticino.it>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <net.h>
+#include <miiphy.h>
+
+#include "ks8851_mll.h"
+
+#define DRIVERNAME "ks8851_mll"
+
+#define MAX_RECV_FRAMES 32
+#define MAX_BUF_SIZE 2048
+#define TX_BUF_SIZE 2000
+#define RX_BUF_SIZE 2000
+
+static const struct chip_id chip_ids[] = {
+ {CIDER_ID, "KSZ8851"},
+ {0, NULL},
+};
+
+/*
+ * union ks_tx_hdr - tx header data
+ * @txb: The header as bytes
+ * @txw: The header as 16bit, little-endian words
+ *
+ * A dual representation of the tx header data to allow
+ * access to individual bytes, and to allow 16bit accesses
+ * with 16bit alignment.
+ */
+union ks_tx_hdr {
+ u8 txb[4];
+ __le16 txw[2];
+};
+
+/*
+ * struct ks_net - KS8851 driver private data
+ * @net_device : The network device we're bound to
+ * @txh : temporaly buffer to save status/length.
+ * @frame_head_info : frame header information for multi-pkt rx.
+ * @statelock : Lock on this structure for tx list.
+ * @msg_enable : The message flags controlling driver output (see ethtool).
+ * @frame_cnt : number of frames received.
+ * @bus_width : i/o bus width.
+ * @irq : irq number assigned to this device.
+ * @rc_rxqcr : Cached copy of KS_RXQCR.
+ * @rc_txcr : Cached copy of KS_TXCR.
+ * @rc_ier : Cached copy of KS_IER.
+ * @sharedbus : Multipex(addr and data bus) mode indicator.
+ * @cmd_reg_cache : command register cached.
+ * @cmd_reg_cache_int : command register cached. Used in the irq handler.
+ * @promiscuous : promiscuous mode indicator.
+ * @all_mcast : mutlicast indicator.
+ * @mcast_lst_size : size of multicast list.
+ * @mcast_lst : multicast list.
+ * @mcast_bits : multicast enabed.
+ * @mac_addr : MAC address assigned to this device.
+ * @fid : frame id.
+ * @extra_byte : number of extra byte prepended rx pkt.
+ * @enabled : indicator this device works.
+ */
+
+/* Receive multiplex framer header info */
+struct type_frame_head {
+ u16 sts; /* Frame status */
+ u16 len; /* Byte count */
+} fr_h_i[MAX_RECV_FRAMES];
+
+struct ks_net {
+ struct net_device *netdev;
+ union ks_tx_hdr txh;
+ struct type_frame_head *frame_head_info;
+ u32 msg_enable;
+ u32 frame_cnt;
+ int bus_width;
+ int irq;
+ u16 rc_rxqcr;
+ u16 rc_txcr;
+ u16 rc_ier;
+ u16 sharedbus;
+ u16 cmd_reg_cache;
+ u16 cmd_reg_cache_int;
+ u16 promiscuous;
+ u16 all_mcast;
+ u16 mcast_lst_size;
+ u8 mcast_lst[MAX_MCAST_LST][MAC_ADDR_LEN];
+ u8 mcast_bits[HW_MCAST_SIZE];
+ u8 mac_addr[6];
+ u8 fid;
+ u8 extra_byte;
+ u8 enabled;
+} ks_str, *ks;
+
+#define BE3 0x8000 /* Byte Enable 3 */
+#define BE2 0x4000 /* Byte Enable 2 */
+#define BE1 0x2000 /* Byte Enable 1 */
+#define BE0 0x1000 /* Byte Enable 0 */
+
+static u8 ks_rdreg8(struct eth_device *dev, u16 offset)
+{
+ u8 shift_bit = offset & 0x03;
+ u8 shift_data = (offset & 1) << 3;
+
+ writew(offset | (BE0 << shift_bit), dev->iobase + 2);
+
+ return (u8)(readw(dev->iobase) >> shift_data);
+}
+
+static u16 ks_rdreg16(struct eth_device *dev, u16 offset)
+{
+ writew(offset | ((BE1 | BE0) << (offset & 0x02)), dev->iobase + 2);
+
+ return readw(dev->iobase);
+}
+
+static void ks_wrreg8(struct eth_device *dev, u16 offset, u8 val)
+{
+ u8 shift_bit = (offset & 0x03);
+ u16 value_write = (u16)(val << ((offset & 1) << 3));
+
+ writew(offset | (BE0 << shift_bit), dev->iobase + 2);
+ writew(value_write, dev->iobase);
+}
+
+static void ks_wrreg16(struct eth_device *dev, u16 offset, u16 val)
+{
+ writew(offset | ((BE1 | BE0) << (offset & 0x02)), dev->iobase + 2);
+ writew(val, dev->iobase);
+}
+
+/*
+ * ks_inblk - read a block of data from QMU. This is called after sudo DMA mode
+ * enabled.
+ * @ks: The chip state
+ * @wptr: buffer address to save data
+ * @len: length in byte to read
+ */
+static inline void ks_inblk(struct eth_device *dev, u16 *wptr, u32 len)
+{
+ len >>= 1;
+
+ while (len--)
+ *wptr++ = readw(dev->iobase);
+}
+
+/*
+ * ks_outblk - write data to QMU. This is called after sudo DMA mode enabled.
+ * @ks: The chip information
+ * @wptr: buffer address
+ * @len: length in byte to write
+ */
+static inline void ks_outblk(struct eth_device *dev, u16 *wptr, u32 len)
+{
+ len >>= 1;
+
+ while (len--)
+ writew(*wptr++, dev->iobase);
+}
+
+static void ks_enable_int(struct eth_device *dev)
+{
+ ks_wrreg16(dev, KS_IER, ks->rc_ier);
+}
+
+static void ks_set_powermode(struct eth_device *dev, unsigned pwrmode)
+{
+ unsigned pmecr;
+
+ ks_rdreg16(dev, KS_GRR);
+ pmecr = ks_rdreg16(dev, KS_PMECR);
+ pmecr &= ~PMECR_PM_MASK;
+ pmecr |= pwrmode;
+
+ ks_wrreg16(dev, KS_PMECR, pmecr);
+}
+
+/*
+ * ks_read_config - read chip configuration of bus width.
+ * @ks: The chip information
+ */
+static void ks_read_config(struct eth_device *dev)
+{
+ u16 reg_data = 0;
+
+ /* Regardless of bus width, 8 bit read should always work. */
+ reg_data = ks_rdreg8(dev, KS_CCR) & 0x00FF;
+ reg_data |= ks_rdreg8(dev, KS_CCR + 1) << 8;
+
+ /* addr/data bus are multiplexed */
+ ks->sharedbus = (reg_data & CCR_SHARED) == CCR_SHARED;
+
+ /*
+ * There are garbage data when reading data from QMU,
+ * depending on bus-width.
+ */
+ if (reg_data & CCR_8BIT) {
+ ks->bus_width = ENUM_BUS_8BIT;
+ ks->extra_byte = 1;
+ } else if (reg_data & CCR_16BIT) {
+ ks->bus_width = ENUM_BUS_16BIT;
+ ks->extra_byte = 2;
+ } else {
+ ks->bus_width = ENUM_BUS_32BIT;
+ ks->extra_byte = 4;
+ }
+}
+
+/*
+ * ks_soft_reset - issue one of the soft reset to the device
+ * @ks: The device state.
+ * @op: The bit(s) to set in the GRR
+ *
+ * Issue the relevant soft-reset command to the device's GRR register
+ * specified by @op.
+ *
+ * Note, the delays are in there as a caution to ensure that the reset
+ * has time to take effect and then complete. Since the datasheet does
+ * not currently specify the exact sequence, we have chosen something
+ * that seems to work with our device.
+ */
+static void ks_soft_reset(struct eth_device *dev, unsigned op)
+{
+ /* Disable interrupt first */
+ ks_wrreg16(dev, KS_IER, 0x0000);
+ ks_wrreg16(dev, KS_GRR, op);
+ mdelay(10); /* wait a short time to effect reset */
+ ks_wrreg16(dev, KS_GRR, 0);
+ mdelay(1); /* wait for condition to clear */
+}
+
+void ks_enable_qmu(struct eth_device *dev)
+{
+ u16 w;
+
+ w = ks_rdreg16(dev, KS_TXCR);
+
+ /* Enables QMU Transmit (TXCR). */
+ ks_wrreg16(dev, KS_TXCR, w | TXCR_TXE);
+
+ /* Enable RX Frame Count Threshold and Auto-Dequeue RXQ Frame */
+ w = ks_rdreg16(dev, KS_RXQCR);
+ ks_wrreg16(dev, KS_RXQCR, w | RXQCR_RXFCTE);
+
+ /* Enables QMU Receive (RXCR1). */
+ w = ks_rdreg16(dev, KS_RXCR1);
+ ks_wrreg16(dev, KS_RXCR1, w | RXCR1_RXE);
+}
+
+static void ks_disable_qmu(struct eth_device *dev)
+{
+ u16 w;
+
+ w = ks_rdreg16(dev, KS_TXCR);
+
+ /* Disables QMU Transmit (TXCR). */
+ w &= ~TXCR_TXE;
+ ks_wrreg16(dev, KS_TXCR, w);
+
+ /* Disables QMU Receive (RXCR1). */
+ w = ks_rdreg16(dev, KS_RXCR1);
+ w &= ~RXCR1_RXE;
+ ks_wrreg16(dev, KS_RXCR1, w);
+}
+
+static inline void ks_read_qmu(struct eth_device *dev, u16 *buf, u32 len)
+{
+ u32 r = ks->extra_byte & 0x1;
+ u32 w = ks->extra_byte - r;
+
+ /* 1. set sudo DMA mode */
+ ks_wrreg16(dev, KS_RXFDPR, RXFDPR_RXFPAI);
+ ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff);
+
+ /*
+ * 2. read prepend data
+ *
+ * read 4 + extra bytes and discard them.
+ * extra bytes for dummy, 2 for status, 2 for len
+ */
+
+ if (r)
+ ks_rdreg8(dev, 0);
+
+ ks_inblk(dev, buf, w + 2 + 2);
+
+ /* 3. read pkt data */
+ ks_inblk(dev, buf, ALIGN(len, 4));
+
+ /* 4. reset sudo DMA Mode */
+ ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr & ~RXQCR_SDA) & 0xff);
+}
+
+static void ks_rcv(struct eth_device *dev, uchar **pv_data)
+{
+ struct type_frame_head *frame_hdr = ks->frame_head_info;
+ int i;
+
+ ks->frame_cnt = ks_rdreg16(dev, KS_RXFCTR) >> 8;
+
+ /* read all header information */
+ for (i = 0; i < ks->frame_cnt; i++) {
+ /* Checking Received packet status */
+ frame_hdr->sts = ks_rdreg16(dev, KS_RXFHSR);
+ /* Get packet len from hardware */
+ frame_hdr->len = ks_rdreg16(dev, KS_RXFHBCR);
+ frame_hdr++;
+ }
+
+ frame_hdr = ks->frame_head_info;
+ while (ks->frame_cnt--) {
+ if ((frame_hdr->sts & RXFSHR_RXFV) &&
+ (frame_hdr->len < RX_BUF_SIZE) &&
+ frame_hdr->len) {
+ /* read data block including CRC 4 bytes */
+ ks_read_qmu(dev, (u16 *)(*pv_data), frame_hdr->len);
+
+ /* NetRxPackets buffer size is ok (*pv_data pointer) */
+ NetReceive(*pv_data, frame_hdr->len);
+ pv_data++;
+ } else {
+ ks_wrreg16(dev, KS_RXQCR, (ks->rc_rxqcr | RXQCR_RRXEF));
+ printf(DRIVERNAME ": bad packet\n");
+ }
+ frame_hdr++;
+ }
+}
+
+/*
+ * ks_read_selftest - read the selftest memory info.
+ * @ks: The device state
+ *
+ * Read and check the TX/RX memory selftest information.
+ */
+static int ks_read_selftest(struct eth_device *dev)
+{
+ u16 both_done = MBIR_TXMBF | MBIR_RXMBF;
+ u16 mbir;
+ int ret = 0;
+
+ mbir = ks_rdreg16(dev, KS_MBIR);
+
+ if ((mbir & both_done) != both_done) {
+ printf(DRIVERNAME ": Memory selftest not finished\n");
+ return 0;
+ }
+
+ if (mbir & MBIR_TXMBFA) {
+ printf(DRIVERNAME ": TX memory selftest fails\n");
+ ret |= 1;
+ }
+
+ if (mbir & MBIR_RXMBFA) {
+ printf(DRIVERNAME ": RX memory selftest fails\n");
+ ret |= 2;
+ }
+
+ debug(DRIVERNAME ": the selftest passes\n");
+
+ return ret;
+}
+
+static void ks_setup(struct eth_device *dev)
+{
+ u16 w;
+
+ /* Setup Transmit Frame Data Pointer Auto-Increment (TXFDPR) */
+ ks_wrreg16(dev, KS_TXFDPR, TXFDPR_TXFPAI);
+
+ /* Setup Receive Frame Data Pointer Auto-Increment */
+ ks_wrreg16(dev, KS_RXFDPR, RXFDPR_RXFPAI);
+
+ /* Setup Receive Frame Threshold - 1 frame (RXFCTFC) */
+ ks_wrreg16(dev, KS_RXFCTR, 1 & RXFCTR_THRESHOLD_MASK);
+
+ /* Setup RxQ Command Control (RXQCR) */
+ ks->rc_rxqcr = RXQCR_CMD_CNTL;
+ ks_wrreg16(dev, KS_RXQCR, ks->rc_rxqcr);
+
+ /*
+ * set the force mode to half duplex, default is full duplex
+ * because if the auto-negotiation fails, most switch uses
+ * half-duplex.
+ */
+ w = ks_rdreg16(dev, KS_P1MBCR);
+ w &= ~P1MBCR_FORCE_FDX;
+ ks_wrreg16(dev, KS_P1MBCR, w);
+
+ w = TXCR_TXFCE | TXCR_TXPE | TXCR_TXCRC | TXCR_TCGIP;
+ ks_wrreg16(dev, KS_TXCR, w);
+
+ w = RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXIPFCC;
+
+ /* Normal mode */
+ w |= RXCR1_RXPAFMA;
+
+ ks_wrreg16(dev, KS_RXCR1, w);
+}
+
+static void ks_setup_int(struct eth_device *dev)
+{
+ ks->rc_ier = 0x00;
+
+ /* Clear the interrupts status of the hardware. */
+ ks_wrreg16(dev, KS_ISR, 0xffff);
+
+ /* Enables the interrupts of the hardware. */
+ ks->rc_ier = (IRQ_LCI | IRQ_TXI | IRQ_RXI);
+}
+
+static int ks8851_mll_detect_chip(struct eth_device *dev)
+{
+ unsigned short val, i;
+
+ ks_read_config(dev);
+
+ val = ks_rdreg16(dev, KS_CIDER);
+
+ if (val == 0xffff) {
+ /* Special case -- no chip present */
+ printf(DRIVERNAME ": is chip mounted ?\n");
+ return -1;
+ } else if ((val & 0xfff0) != CIDER_ID) {
+ printf(DRIVERNAME ": Invalid chip id 0x%04x\n", val);
+ return -1;
+ }
+
+ debug("Read back KS8851 id 0x%x\n", val);
+
+ /* only one entry in the table */
+ val &= 0xfff0;
+ for (i = 0; chip_ids[i].id != 0; i++) {
+ if (chip_ids[i].id == val)
+ break;
+ }
+ if (!chip_ids[i].id) {
+ printf(DRIVERNAME ": Unknown chip ID %04x\n", val);
+ return -1;
+ }
+
+ dev->priv = (void *)&chip_ids[i];
+
+ return 0;
+}
+
+static void ks8851_mll_reset(struct eth_device *dev)
+{
+ /* wake up powermode to normal mode */
+ ks_set_powermode(dev, PMECR_PM_NORMAL);
+ mdelay(1); /* wait for normal mode to take effect */
+
+ /* Disable interrupt and reset */
+ ks_soft_reset(dev, GRR_GSR);
+
+ /* turn off the IRQs and ack any outstanding */
+ ks_wrreg16(dev, KS_IER, 0x0000);
+ ks_wrreg16(dev, KS_ISR, 0xffff);
+
+ /* shutdown RX/TX QMU */
+ ks_disable_qmu(dev);
+}
+
+static void ks8851_mll_phy_configure(struct eth_device *dev)
+{
+ u16 data;
+
+ ks_setup(dev);
+ ks_setup_int(dev);
+
+ /* Probing the phy */
+ data = ks_rdreg16(dev, KS_OBCR);
+ ks_wrreg16(dev, KS_OBCR, data | OBCR_ODS_16MA);
+
+ debug(DRIVERNAME ": phy initialized\n");
+}
+
+static void ks8851_mll_enable(struct eth_device *dev)
+{
+ ks_wrreg16(dev, KS_ISR, 0xffff);
+ ks_enable_int(dev);
+ ks_enable_qmu(dev);
+}
+
+static int ks8851_mll_init(struct eth_device *dev, bd_t *bd)
+{
+ struct chip_id *id = dev->priv;
+
+ debug(DRIVERNAME ": detected %s controller\n", id->name);
+
+ if (ks_read_selftest(dev)) {
+ printf(DRIVERNAME ": Selftest failed\n");
+ return -1;
+ }
+
+ ks8851_mll_reset(dev);
+
+ /* Configure the PHY, initialize the link state */
+ ks8851_mll_phy_configure(dev);
+
+ /* static allocation of private informations */
+ ks->frame_head_info = fr_h_i;
+
+ /* Turn on Tx + Rx */
+ ks8851_mll_enable(dev);
+
+ return 0;
+}
+
+static void ks_write_qmu(struct eth_device *dev, u8 *pdata, u16 len)
+{
+ /* start header at txb[0] to align txw entries */
+ ks->txh.txw[0] = 0;
+ ks->txh.txw[1] = cpu_to_le16(len);
+
+ /* 1. set sudo-DMA mode */
+ ks_wrreg16(dev, KS_TXFDPR, TXFDPR_TXFPAI);
+ ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff);
+ /* 2. write status/lenth info */
+ ks_outblk(dev, ks->txh.txw, 4);
+ /* 3. write pkt data */
+ ks_outblk(dev, (u16 *)pdata, ALIGN(len, 4));
+ /* 4. reset sudo-DMA mode */
+ ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr & ~RXQCR_SDA) & 0xff);
+ /* 5. Enqueue Tx(move the pkt from TX buffer into TXQ) */
+ ks_wrreg16(dev, KS_TXQCR, TXQCR_METFE);
+ /* 6. wait until TXQCR_METFE is auto-cleared */
+ do { } while (ks_rdreg16(dev, KS_TXQCR) & TXQCR_METFE);
+}
+
+static int ks8851_mll_send(struct eth_device *dev, void *packet, int length)
+{
+ u8 *data = (u8 *)packet;
+ u16 tmplen = (u16)length;
+ u16 retv;
+
+ /*
+ * Extra space are required:
+ * 4 byte for alignment, 4 for status/length, 4 for CRC
+ */
+ retv = ks_rdreg16(dev, KS_TXMIR) & 0x1fff;
+ if (retv >= tmplen + 12) {
+ ks_write_qmu(dev, data, tmplen);
+ return 0;
+ } else {
+ printf(DRIVERNAME ": failed to send packet: No buffer\n");
+ return -1;
+ }
+}
+
+static void ks8851_mll_halt(struct eth_device *dev)
+{
+ ks8851_mll_reset(dev);
+}
+
+/*
+ * Maximum receive ring size; that is, the number of packets
+ * we can buffer before overflow happens. Basically, this just
+ * needs to be enough to prevent a packet being discarded while
+ * we are processing the previous one.
+ */
+static int ks8851_mll_recv(struct eth_device *dev)
+{
+ u16 status;
+
+ status = ks_rdreg16(dev, KS_ISR);
+
+ ks_wrreg16(dev, KS_ISR, status);
+
+ if ((status & IRQ_RXI))
+ ks_rcv(dev, (uchar **)NetRxPackets);
+
+ if ((status & IRQ_LDI)) {
+ u16 pmecr = ks_rdreg16(dev, KS_PMECR);
+ pmecr &= ~PMECR_WKEVT_MASK;
+ ks_wrreg16(dev, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
+ }
+
+ return 0;
+}
+
+static int ks8851_mll_write_hwaddr(struct eth_device *dev)
+{
+ u16 addrl, addrm, addrh;
+
+ addrh = (dev->enetaddr[0] << 8) | dev->enetaddr[1];
+ addrm = (dev->enetaddr[2] << 8) | dev->enetaddr[3];
+ addrl = (dev->enetaddr[4] << 8) | dev->enetaddr[5];
+
+ ks_wrreg16(dev, KS_MARH, addrh);
+ ks_wrreg16(dev, KS_MARM, addrm);
+ ks_wrreg16(dev, KS_MARL, addrl);
+
+ return 0;
+}
+
+int ks8851_mll_initialize(u8 dev_num, int base_addr)
+{
+ struct eth_device *dev;
+
+ dev = malloc(sizeof(*dev));
+ if (!dev) {
+ printf("Error: Failed to allocate memory\n");
+ return -1;
+ }
+ memset(dev, 0, sizeof(*dev));
+
+ dev->iobase = base_addr;
+
+ ks = &ks_str;
+
+ /* Try to detect chip. Will fail if not present. */
+ if (ks8851_mll_detect_chip(dev)) {
+ free(dev);
+ return -1;
+ }
+
+ dev->init = ks8851_mll_init;
+ dev->halt = ks8851_mll_halt;
+ dev->send = ks8851_mll_send;
+ dev->recv = ks8851_mll_recv;
+ dev->write_hwaddr = ks8851_mll_write_hwaddr;
+ sprintf(dev->name, "%s-%hu", DRIVERNAME, dev_num);
+
+ eth_register(dev);
+
+ return 0;
+}
diff --git a/drivers/net/ks8851_mll.h b/drivers/net/ks8851_mll.h
new file mode 100644
index 0000000..7f90ae4
--- /dev/null
+++ b/drivers/net/ks8851_mll.h
@@ -0,0 +1,357 @@
+/*
+ * drivers/net/ks8851_mll.c
+ *
+ * Supports:
+ * KS8851 16bit MLL chip from Micrel Inc.
+ *
+ * Copyright (c) 2009 Micrel Inc.
+ *
+ * modified by
+ * (c) 2011 Bticino s.p.a, Roberto Cerati <roberto.cerati@bticino.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _KS8851_MLL_H_
+#define _KS8851_MLL_H_
+
+#include <linux/types.h>
+
+#define KS_CCR 0x08
+#define CCR_EEPROM (1 << 9)
+#define CCR_SPI (1 << 8)
+#define CCR_8BIT (1 << 7)
+#define CCR_16BIT (1 << 6)
+#define CCR_32BIT (1 << 5)
+#define CCR_SHARED (1 << 4)
+#define CCR_32PIN (1 << 0)
+
+/* MAC address registers */
+#define KS_MARL 0x10
+#define KS_MARM 0x12
+#define KS_MARH 0x14
+
+#define KS_OBCR 0x20
+#define OBCR_ODS_16MA (1 << 6)
+
+#define KS_EEPCR 0x22
+#define EEPCR_EESA (1 << 4)
+#define EEPCR_EESB (1 << 3)
+#define EEPCR_EEDO (1 << 2)
+#define EEPCR_EESCK (1 << 1)
+#define EEPCR_EECS (1 << 0)
+
+#define KS_MBIR 0x24
+#define MBIR_TXMBF (1 << 12)
+#define MBIR_TXMBFA (1 << 11)
+#define MBIR_RXMBF (1 << 4)
+#define MBIR_RXMBFA (1 << 3)
+
+#define KS_GRR 0x26
+#define GRR_QMU (1 << 1)
+#define GRR_GSR (1 << 0)
+
+#define KS_WFCR 0x2A
+#define WFCR_MPRXE (1 << 7)
+#define WFCR_WF3E (1 << 3)
+#define WFCR_WF2E (1 << 2)
+#define WFCR_WF1E (1 << 1)
+#define WFCR_WF0E (1 << 0)
+
+#define KS_WF0CRC0 0x30
+#define KS_WF0CRC1 0x32
+#define KS_WF0BM0 0x34
+#define KS_WF0BM1 0x36
+#define KS_WF0BM2 0x38
+#define KS_WF0BM3 0x3A
+
+#define KS_WF1CRC0 0x40
+#define KS_WF1CRC1 0x42
+#define KS_WF1BM0 0x44
+#define KS_WF1BM1 0x46
+#define KS_WF1BM2 0x48
+#define KS_WF1BM3 0x4A
+
+#define KS_WF2CRC0 0x50
+#define KS_WF2CRC1 0x52
+#define KS_WF2BM0 0x54
+#define KS_WF2BM1 0x56
+#define KS_WF2BM2 0x58
+#define KS_WF2BM3 0x5A
+
+#define KS_WF3CRC0 0x60
+#define KS_WF3CRC1 0x62
+#define KS_WF3BM0 0x64
+#define KS_WF3BM1 0x66
+#define KS_WF3BM2 0x68
+#define KS_WF3BM3 0x6A
+
+#define KS_TXCR 0x70
+#define TXCR_TCGICMP (1 << 8)
+#define TXCR_TCGUDP (1 << 7)
+#define TXCR_TCGTCP (1 << 6)
+#define TXCR_TCGIP (1 << 5)
+#define TXCR_FTXQ (1 << 4)
+#define TXCR_TXFCE (1 << 3)
+#define TXCR_TXPE (1 << 2)
+#define TXCR_TXCRC (1 << 1)
+#define TXCR_TXE (1 << 0)
+
+#define KS_TXSR 0x72
+#define TXSR_TXLC (1 << 13)
+#define TXSR_TXMC (1 << 12)
+#define TXSR_TXFID_MASK (0x3f << 0)
+#define TXSR_TXFID_SHIFT (0)
+#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f)
+
+
+#define KS_RXCR1 0x74
+#define RXCR1_FRXQ (1 << 15)
+#define RXCR1_RXUDPFCC (1 << 14)
+#define RXCR1_RXTCPFCC (1 << 13)
+#define RXCR1_RXIPFCC (1 << 12)
+#define RXCR1_RXPAFMA (1 << 11)
+#define RXCR1_RXFCE (1 << 10)
+#define RXCR1_RXEFE (1 << 9)
+#define RXCR1_RXMAFMA (1 << 8)
+#define RXCR1_RXBE (1 << 7)
+#define RXCR1_RXME (1 << 6)
+#define RXCR1_RXUE (1 << 5)
+#define RXCR1_RXAE (1 << 4)
+#define RXCR1_RXINVF (1 << 1)
+#define RXCR1_RXE (1 << 0)
+#define RXCR1_FILTER_MASK (RXCR1_RXINVF | RXCR1_RXAE | \
+ RXCR1_RXMAFMA | RXCR1_RXPAFMA)
+
+#define KS_RXCR2 0x76
+#define RXCR2_SRDBL_MASK (0x7 << 5)
+#define RXCR2_SRDBL_SHIFT (5)
+#define RXCR2_SRDBL_4B (0x0 << 5)
+#define RXCR2_SRDBL_8B (0x1 << 5)
+#define RXCR2_SRDBL_16B (0x2 << 5)
+#define RXCR2_SRDBL_32B (0x3 << 5)
+/* #define RXCR2_SRDBL_FRAME (0x4 << 5) */
+#define RXCR2_IUFFP (1 << 4)
+#define RXCR2_RXIUFCEZ (1 << 3)
+#define RXCR2_UDPLFE (1 << 2)
+#define RXCR2_RXICMPFCC (1 << 1)
+#define RXCR2_RXSAF (1 << 0)
+
+#define KS_TXMIR 0x78
+
+#define KS_RXFHSR 0x7C
+#define RXFSHR_RXFV (1 << 15)
+#define RXFSHR_RXICMPFCS (1 << 13)
+#define RXFSHR_RXIPFCS (1 << 12)
+#define RXFSHR_RXTCPFCS (1 << 11)
+#define RXFSHR_RXUDPFCS (1 << 10)
+#define RXFSHR_RXBF (1 << 7)
+#define RXFSHR_RXMF (1 << 6)
+#define RXFSHR_RXUF (1 << 5)
+#define RXFSHR_RXMR (1 << 4)
+#define RXFSHR_RXFT (1 << 3)
+#define RXFSHR_RXFTL (1 << 2)
+#define RXFSHR_RXRF (1 << 1)
+#define RXFSHR_RXCE (1 << 0)
+#define RXFSHR_ERR (RXFSHR_RXCE | RXFSHR_RXRF |\
+ RXFSHR_RXFTL | RXFSHR_RXMR |\
+ RXFSHR_RXICMPFCS | RXFSHR_RXIPFCS |\
+ RXFSHR_RXTCPFCS)
+#define KS_RXFHBCR 0x7E
+#define RXFHBCR_CNT_MASK 0x0FFF
+
+#define KS_TXQCR 0x80
+#define TXQCR_AETFE (1 << 2)
+#define TXQCR_TXQMAM (1 << 1)
+#define TXQCR_METFE (1 << 0)
+
+#define KS_RXQCR 0x82
+#define RXQCR_RXDTTS (1 << 12)
+#define RXQCR_RXDBCTS (1 << 11)
+#define RXQCR_RXFCTS (1 << 10)
+#define RXQCR_RXIPHTOE (1 << 9)
+#define RXQCR_RXDTTE (1 << 7)
+#define RXQCR_RXDBCTE (1 << 6)
+#define RXQCR_RXFCTE (1 << 5)
+#define RXQCR_ADRFE (1 << 4)
+#define RXQCR_SDA (1 << 3)
+#define RXQCR_RRXEF (1 << 0)
+#define RXQCR_CMD_CNTL (RXQCR_RXFCTE|RXQCR_ADRFE)
+
+#define KS_TXFDPR 0x84
+#define TXFDPR_TXFPAI (1 << 14)
+#define TXFDPR_TXFP_MASK (0x7ff << 0)
+#define TXFDPR_TXFP_SHIFT (0)
+
+#define KS_RXFDPR 0x86
+#define RXFDPR_RXFPAI (1 << 14)
+
+#define KS_RXDTTR 0x8C
+#define KS_RXDBCTR 0x8E
+
+#define KS_IER 0x90
+#define KS_ISR 0x92
+#define IRQ_LCI (1 << 15)
+#define IRQ_TXI (1 << 14)
+#define IRQ_RXI (1 << 13)
+#define IRQ_RXOI (1 << 11)
+#define IRQ_TXPSI (1 << 9)
+#define IRQ_RXPSI (1 << 8)
+#define IRQ_TXSAI (1 << 6)
+#define IRQ_RXWFDI (1 << 5)
+#define IRQ_RXMPDI (1 << 4)
+#define IRQ_LDI (1 << 3)
+#define IRQ_EDI (1 << 2)
+#define IRQ_SPIBEI (1 << 1)
+#define IRQ_DEDI (1 << 0)
+
+#define KS_RXFCTR 0x9C
+#define RXFCTR_THRESHOLD_MASK 0x00FF
+
+#define KS_RXFC 0x9D
+#define RXFCTR_RXFC_MASK (0xff << 8)
+#define RXFCTR_RXFC_SHIFT (8)
+#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff)
+#define RXFCTR_RXFCT_MASK (0xff << 0)
+#define RXFCTR_RXFCT_SHIFT (0)
+
+#define KS_TXNTFSR 0x9E
+
+#define KS_MAHTR0 0xA0
+#define KS_MAHTR1 0xA2
+#define KS_MAHTR2 0xA4
+#define KS_MAHTR3 0xA6
+
+#define KS_FCLWR 0xB0
+#define KS_FCHWR 0xB2
+#define KS_FCOWR 0xB4
+
+#define KS_CIDER 0xC0
+#define CIDER_ID 0x8870
+#define CIDER_REV_MASK (0x7 << 1)
+#define CIDER_REV_SHIFT (1)
+#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7)
+
+#define KS_CGCR 0xC6
+#define KS_IACR 0xC8
+#define IACR_RDEN (1 << 12)
+#define IACR_TSEL_MASK (0x3 << 10)
+#define IACR_TSEL_SHIFT (10)
+#define IACR_TSEL_MIB (0x3 << 10)
+#define IACR_ADDR_MASK (0x1f << 0)
+#define IACR_ADDR_SHIFT (0)
+
+#define KS_IADLR 0xD0
+#define KS_IAHDR 0xD2
+
+#define KS_PMECR 0xD4
+#define PMECR_PME_DELAY (1 << 14)
+#define PMECR_PME_POL (1 << 12)
+#define PMECR_WOL_WAKEUP (1 << 11)
+#define PMECR_WOL_MAGICPKT (1 << 10)
+#define PMECR_WOL_LINKUP (1 << 9)
+#define PMECR_WOL_ENERGY (1 << 8)
+#define PMECR_AUTO_WAKE_EN (1 << 7)
+#define PMECR_WAKEUP_NORMAL (1 << 6)
+#define PMECR_WKEVT_MASK (0xf << 2)
+#define PMECR_WKEVT_SHIFT (2)
+#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf)
+#define PMECR_WKEVT_ENERGY (0x1 << 2)
+#define PMECR_WKEVT_LINK (0x2 << 2)
+#define PMECR_WKEVT_MAGICPKT (0x4 << 2)
+#define PMECR_WKEVT_FRAME (0x8 << 2)
+#define PMECR_PM_MASK (0x3 << 0)
+#define PMECR_PM_SHIFT (0)
+#define PMECR_PM_NORMAL (0x0 << 0)
+#define PMECR_PM_ENERGY (0x1 << 0)
+#define PMECR_PM_SOFTDOWN (0x2 << 0)
+#define PMECR_PM_POWERSAVE (0x3 << 0)
+
+/* Standard MII PHY data */
+#define KS_P1MBCR 0xE4
+#define P1MBCR_FORCE_FDX (1 << 8)
+
+#define KS_P1MBSR 0xE6
+#define P1MBSR_AN_COMPLETE (1 << 5)
+#define P1MBSR_AN_CAPABLE (1 << 3)
+#define P1MBSR_LINK_UP (1 << 2)
+
+#define KS_PHY1ILR 0xE8
+#define KS_PHY1IHR 0xEA
+#define KS_P1ANAR 0xEC
+#define KS_P1ANLPR 0xEE
+
+#define KS_P1SCLMD 0xF4
+#define P1SCLMD_LEDOFF (1 << 15)
+#define P1SCLMD_TXIDS (1 << 14)
+#define P1SCLMD_RESTARTAN (1 << 13)
+#define P1SCLMD_DISAUTOMDIX (1 << 10)
+#define P1SCLMD_FORCEMDIX (1 << 9)
+#define P1SCLMD_AUTONEGEN (1 << 7)
+#define P1SCLMD_FORCE100 (1 << 6)
+#define P1SCLMD_FORCEFDX (1 << 5)
+#define P1SCLMD_ADV_FLOW (1 << 4)
+#define P1SCLMD_ADV_100BT_FDX (1 << 3)
+#define P1SCLMD_ADV_100BT_HDX (1 << 2)
+#define P1SCLMD_ADV_10BT_FDX (1 << 1)
+#define P1SCLMD_ADV_10BT_HDX (1 << 0)
+
+#define KS_P1CR 0xF6
+#define P1CR_HP_MDIX (1 << 15)
+#define P1CR_REV_POL (1 << 13)
+#define P1CR_OP_100M (1 << 10)
+#define P1CR_OP_FDX (1 << 9)
+#define P1CR_OP_MDI (1 << 7)
+#define P1CR_AN_DONE (1 << 6)
+#define P1CR_LINK_GOOD (1 << 5)
+#define P1CR_PNTR_FLOW (1 << 4)
+#define P1CR_PNTR_100BT_FDX (1 << 3)
+#define P1CR_PNTR_100BT_HDX (1 << 2)
+#define P1CR_PNTR_10BT_FDX (1 << 1)
+#define P1CR_PNTR_10BT_HDX (1 << 0)
+
+/* TX Frame control */
+#define TXFR_TXIC (1 << 15)
+#define TXFR_TXFID_MASK (0x3f << 0)
+#define TXFR_TXFID_SHIFT (0)
+
+#define KS_P1SR 0xF8
+#define P1SR_HP_MDIX (1 << 15)
+#define P1SR_REV_POL (1 << 13)
+#define P1SR_OP_100M (1 << 10)
+#define P1SR_OP_FDX (1 << 9)
+#define P1SR_OP_MDI (1 << 7)
+#define P1SR_AN_DONE (1 << 6)
+#define P1SR_LINK_GOOD (1 << 5)
+#define P1SR_PNTR_FLOW (1 << 4)
+#define P1SR_PNTR_100BT_FDX (1 << 3)
+#define P1SR_PNTR_100BT_HDX (1 << 2)
+#define P1SR_PNTR_10BT_FDX (1 << 1)
+#define P1SR_PNTR_10BT_HDX (1 << 0)
+
+#define ENUM_BUS_NONE 0
+#define ENUM_BUS_8BIT 1
+#define ENUM_BUS_16BIT 2
+#define ENUM_BUS_32BIT 3
+
+#define MAX_MCAST_LST 32
+#define HW_MCAST_SIZE 8
+#define MAC_ADDR_LEN 6
+
+/* Chip ID values */
+struct chip_id {
+ u16 id;
+ char *name;
+};
+
+#endif
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 8bacbda..b7802a2 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -103,9 +103,15 @@ struct macb_device {
const struct device *dev;
struct eth_device netdev;
unsigned short phy_addr;
+ struct mii_dev *bus;
};
#define to_macb(_nd) container_of(_nd, struct macb_device, netdev)
+static int macb_is_gem(struct macb_device *macb)
+{
+ return MACB_BFEXT(IDNUM, macb_readl(macb, MID)) == 0x2;
+}
+
static void macb_mdio_write(struct macb_device *macb, u8 reg, u16 value)
{
unsigned long netctl;
@@ -163,7 +169,12 @@ static u16 macb_mdio_read(struct macb_device *macb, u8 reg)
return MACB_BFEXT(DATA, frame);
}
-#if defined(CONFIG_CMD_MII)
+void __weak arch_get_mdio_control(const char *name)
+{
+ return;
+}
+
+#if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
int macb_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value)
{
@@ -173,6 +184,7 @@ int macb_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value)
if ( macb->phy_addr != phy_adr )
return -1;
+ arch_get_mdio_control(devname);
*value = macb_mdio_read(macb, reg);
return 0;
@@ -186,6 +198,7 @@ int macb_miiphy_write(const char *devname, u8 phy_adr, u8 reg, u16 value)
if ( macb->phy_addr != phy_adr )
return -1;
+ arch_get_mdio_control(devname);
macb_mdio_write(macb, reg, value);
return 0;
@@ -372,11 +385,15 @@ static int macb_phy_find(struct macb_device *macb)
static int macb_phy_init(struct macb_device *macb)
{
struct eth_device *netdev = &macb->netdev;
+#ifdef CONFIG_PHYLIB
+ struct phy_device *phydev;
+#endif
u32 ncfgr;
u16 phy_id, status, adv, lpa;
int media, speed, duplex;
int i;
+ arch_get_mdio_control(netdev->name);
#ifdef CONFIG_MACB_SEARCH_PHY
/* Auto-detect phy_addr */
if (!macb_phy_find(macb)) {
@@ -391,6 +408,13 @@ static int macb_phy_init(struct macb_device *macb)
return 0;
}
+#ifdef CONFIG_PHYLIB
+ phydev->bus = macb->bus;
+ phydev->dev = netdev;
+ phydev->addr = macb->phy_addr;
+ phy_config(phydev);
+#endif
+
status = macb_mdio_read(macb, MII_BMSR);
if (!(status & BMSR_LSTATUS)) {
/* Try to re-negotiate if we don't have link already. */
@@ -408,28 +432,64 @@ static int macb_phy_init(struct macb_device *macb)
printf("%s: link down (status: 0x%04x)\n",
netdev->name, status);
return 0;
- } else {
- adv = macb_mdio_read(macb, MII_ADVERTISE);
- lpa = macb_mdio_read(macb, MII_LPA);
- media = mii_nway_result(lpa & adv);
- speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
- ? 1 : 0);
- duplex = (media & ADVERTISE_FULL) ? 1 : 0;
- printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
- netdev->name,
- speed ? "100" : "10",
- duplex ? "full" : "half",
- lpa);
-
- ncfgr = macb_readl(macb, NCFGR);
- ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
- if (speed)
- ncfgr |= MACB_BIT(SPD);
- if (duplex)
- ncfgr |= MACB_BIT(FD);
- macb_writel(macb, NCFGR, ncfgr);
- return 1;
}
+
+ /* First check for GMAC */
+ if (macb_is_gem(macb)) {
+ lpa = macb_mdio_read(macb, MII_STAT1000);
+ if (lpa & (1 << 11)) {
+ speed = 1000;
+ duplex = 1;
+ } else {
+ if (lpa & (1 << 10)) {
+ speed = 1000;
+ duplex = 1;
+ } else {
+ speed = 0;
+ }
+ }
+
+ if (speed == 1000) {
+ printf("%s: link up, %dMbps %s-duplex (lpa: 0x%04x)\n",
+ netdev->name,
+ speed,
+ duplex ? "full" : "half",
+ lpa);
+
+ ncfgr = macb_readl(macb, NCFGR);
+ ncfgr &= ~(GEM_BIT(GBE) | MACB_BIT(SPD) | MACB_BIT(FD));
+ if (speed)
+ ncfgr |= GEM_BIT(GBE);
+ if (duplex)
+ ncfgr |= MACB_BIT(FD);
+ macb_writel(macb, NCFGR, ncfgr);
+
+ return 1;
+ }
+ }
+
+ /* fall back for EMAC checking */
+ adv = macb_mdio_read(macb, MII_ADVERTISE);
+ lpa = macb_mdio_read(macb, MII_LPA);
+ media = mii_nway_result(lpa & adv);
+ speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
+ ? 1 : 0);
+ duplex = (media & ADVERTISE_FULL) ? 1 : 0;
+ printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
+ netdev->name,
+ speed ? "100" : "10",
+ duplex ? "full" : "half",
+ lpa);
+
+ ncfgr = macb_readl(macb, NCFGR);
+ ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
+ if (speed)
+ ncfgr |= MACB_BIT(SPD);
+ if (duplex)
+ ncfgr |= MACB_BIT(FD);
+ macb_writel(macb, NCFGR, ncfgr);
+
+ return 1;
}
static int macb_init(struct eth_device *netdev, bd_t *bd)
@@ -464,26 +524,28 @@ static int macb_init(struct eth_device *netdev, bd_t *bd)
macb_writel(macb, RBQP, macb->rx_ring_dma);
macb_writel(macb, TBQP, macb->tx_ring_dma);
+ if (macb_is_gem(macb)) {
+#ifdef CONFIG_RGMII
+ gem_writel(macb, UR, GEM_BIT(RGMII));
+#else
+ gem_writel(macb, UR, 0);
+#endif
+ } else {
/* choose RMII or MII mode. This depends on the board */
#ifdef CONFIG_RMII
-#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
- defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \
- defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \
- defined(CONFIG_AT91SAM9XE) || defined(CONFIG_AT91SAM9X5)
+#ifdef CONFIG_AT91FAMILY
macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, 0);
#endif
#else
-#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
- defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \
- defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \
- defined(CONFIG_AT91SAM9XE) || defined(CONFIG_AT91SAM9X5)
+#ifdef CONFIG_AT91FAMILY
macb_writel(macb, USRIO, MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, MACB_BIT(MII));
#endif
#endif /* CONFIG_RMII */
+ }
if (!macb_phy_init(macb))
return -1;
@@ -527,11 +589,48 @@ static int macb_write_hwaddr(struct eth_device *dev)
return 0;
}
+static u32 macb_mdc_clk_div(int id, struct macb_device *macb)
+{
+ u32 config;
+ unsigned long macb_hz = get_macb_pclk_rate(id);
+
+ if (macb_hz < 20000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV8);
+ else if (macb_hz < 40000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV16);
+ else if (macb_hz < 80000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV32);
+ else
+ config = MACB_BF(CLK, MACB_CLK_DIV64);
+
+ return config;
+}
+
+static u32 gem_mdc_clk_div(int id, struct macb_device *macb)
+{
+ u32 config;
+ unsigned long macb_hz = get_macb_pclk_rate(id);
+
+ if (macb_hz < 20000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV8);
+ else if (macb_hz < 40000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV16);
+ else if (macb_hz < 80000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV32);
+ else if (macb_hz < 120000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV48);
+ else if (macb_hz < 160000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV64);
+ else
+ config = GEM_BF(CLK, GEM_CLK_DIV96);
+
+ return config;
+}
+
int macb_eth_initialize(int id, void *regs, unsigned int phy_addr)
{
struct macb_device *macb;
struct eth_device *netdev;
- unsigned long macb_hz;
u32 ncfgr;
macb = malloc(sizeof(struct macb_device));
@@ -555,7 +654,11 @@ int macb_eth_initialize(int id, void *regs, unsigned int phy_addr)
macb->regs = regs;
macb->phy_addr = phy_addr;
- sprintf(netdev->name, "macb%d", id);
+ if (macb_is_gem(macb))
+ sprintf(netdev->name, "gmac%d", id);
+ else
+ sprintf(netdev->name, "macb%d", id);
+
netdev->init = macb_init;
netdev->halt = macb_halt;
netdev->send = macb_send;
@@ -566,22 +669,20 @@ int macb_eth_initialize(int id, void *regs, unsigned int phy_addr)
* Do some basic initialization so that we at least can talk
* to the PHY
*/
- macb_hz = get_macb_pclk_rate(id);
- if (macb_hz < 20000000)
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV8);
- else if (macb_hz < 40000000)
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV16);
- else if (macb_hz < 80000000)
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV32);
- else
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV64);
+ if (macb_is_gem(macb)) {
+ ncfgr = gem_mdc_clk_div(id, macb);
+ ncfgr |= GEM_BF(DBW, 1);
+ } else {
+ ncfgr = macb_mdc_clk_div(id, macb);
+ }
macb_writel(macb, NCFGR, ncfgr);
eth_register(netdev);
-#if defined(CONFIG_CMD_MII)
+#if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
miiphy_register(netdev->name, macb_miiphy_read, macb_miiphy_write);
+ macb->bus = miiphy_get_dev_by_name(netdev->name);
#endif
return 0;
}
diff --git a/drivers/net/macb.h b/drivers/net/macb.h
index f92a20c..68eef00 100644
--- a/drivers/net/macb.h
+++ b/drivers/net/macb.h
@@ -26,6 +26,7 @@
#define MACB_NCR 0x0000
#define MACB_NCFGR 0x0004
#define MACB_NSR 0x0008
+#define GEM_UR 0x000c
#define MACB_TSR 0x0014
#define MACB_RBQP 0x0018
#define MACB_TBQP 0x001c
@@ -71,6 +72,7 @@
#define MACB_TPQ 0x00bc
#define MACB_USRIO 0x00c0
#define MACB_WOL 0x00c4
+#define MACB_MID 0x00fc
/* Bitfields in NCR */
#define MACB_LB_OFFSET 0
@@ -138,6 +140,13 @@
#define MACB_IRXFCS_OFFSET 19
#define MACB_IRXFCS_SIZE 1
+#define GEM_GBE_OFFSET 10
+#define GEM_GBE_SIZE 1
+#define GEM_CLK_OFFSET 18
+#define GEM_CLK_SIZE 3
+#define GEM_DBW_OFFSET 21
+#define GEM_DBW_SIZE 2
+
/* Bitfields in NSR */
#define MACB_NSR_LINK_OFFSET 0
#define MACB_NSR_LINK_SIZE 1
@@ -146,6 +155,10 @@
#define MACB_IDLE_OFFSET 2
#define MACB_IDLE_SIZE 1
+/* Bitfields in UR */
+#define GEM_RGMII_OFFSET 0
+#define GEM_RGMII_SIZE 1
+
/* Bitfields in TSR */
#define MACB_UBR_OFFSET 0
#define MACB_UBR_SIZE 1
@@ -240,12 +253,25 @@
#define MACB_WOL_MTI_OFFSET 19
#define MACB_WOL_MTI_SIZE 1
+/* Bitfields in MID */
+#define MACB_IDNUM_OFFSET 16
+#define MACB_IDNUM_SIZE 16
+
+/* Bitfields in DCFG1 */
/* Constants for CLK */
#define MACB_CLK_DIV8 0
#define MACB_CLK_DIV16 1
#define MACB_CLK_DIV32 2
#define MACB_CLK_DIV64 3
+/* GEM specific constants for CLK */
+#define GEM_CLK_DIV8 0
+#define GEM_CLK_DIV16 1
+#define GEM_CLK_DIV32 2
+#define GEM_CLK_DIV48 3
+#define GEM_CLK_DIV64 4
+#define GEM_CLK_DIV96 5
+
/* Constants for MAN register */
#define MACB_MAN_SOF 1
#define MACB_MAN_WRITE 1
@@ -255,21 +281,38 @@
/* Bit manipulation macros */
#define MACB_BIT(name) \
(1 << MACB_##name##_OFFSET)
-#define MACB_BF(name,value) \
+#define MACB_BF(name, value) \
(((value) & ((1 << MACB_##name##_SIZE) - 1)) \
<< MACB_##name##_OFFSET)
-#define MACB_BFEXT(name,value)\
+#define MACB_BFEXT(name, value)\
(((value) >> MACB_##name##_OFFSET) \
& ((1 << MACB_##name##_SIZE) - 1))
-#define MACB_BFINS(name,value,old) \
+#define MACB_BFINS(name, value, old) \
(((old) & ~(((1 << MACB_##name##_SIZE) - 1) \
<< MACB_##name##_OFFSET)) \
- | MACB_BF(name,value))
+ | MACB_BF(name, value))
+
+#define GEM_BIT(name) \
+ (1 << GEM_##name##_OFFSET)
+#define GEM_BF(name, value) \
+ (((value) & ((1 << GEM_##name##_SIZE) - 1)) \
+ << GEM_##name##_OFFSET)
+#define GEM_BFEXT(name, value)\
+ (((value) >> GEM_##name##_OFFSET) \
+ & ((1 << GEM_##name##_SIZE) - 1))
+#define GEM_BFINS(name, value, old) \
+ (((old) & ~(((1 << GEM_##name##_SIZE) - 1) \
+ << GEM_##name##_OFFSET)) \
+ | GEM_BF(name, value))
/* Register access macros */
-#define macb_readl(port,reg) \
+#define macb_readl(port, reg) \
readl((port)->regs + MACB_##reg)
-#define macb_writel(port,reg,value) \
+#define macb_writel(port, reg, value) \
writel((value), (port)->regs + MACB_##reg)
+#define gem_readl(port, reg) \
+ readl((port)->regs + GEM_##reg)
+#define gem_writel(port, reg, value) \
+ writel((value), (port)->regs + GEM_##reg)
#endif /* __DRIVERS_MACB_H__ */
diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c
index 47bf27c..319fe8a 100644
--- a/drivers/net/mvgbe.c
+++ b/drivers/net/mvgbe.c
@@ -43,6 +43,8 @@
#include <asm/arch/kirkwood.h>
#elif defined(CONFIG_ORION5X)
#include <asm/arch/orion5x.h>
+#elif defined(CONFIG_DOVE)
+#include <asm/arch/dove.h>
#endif
#include "mvgbe.h"
@@ -52,7 +54,7 @@ DECLARE_GLOBAL_DATA_PTR;
#define MV_PHY_ADR_REQUEST 0xee
#define MVGBE_SMI_REG (((struct mvgbe_registers *)MVGBE0_BASE)->smi)
-#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+#if defined(CONFIG_PHYLIB) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
/*
* smi_reg_read - miiphy_read callback function.
*
@@ -184,6 +186,25 @@ static int smi_reg_write(const char *devname, u8 phy_adr, u8 reg_ofs, u16 data)
}
#endif
+#if defined(CONFIG_PHYLIB)
+int mvgbe_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr,
+ int reg_addr)
+{
+ u16 data;
+ int ret;
+ ret = smi_reg_read(bus->name, phy_addr, reg_addr, &data);
+ if (ret)
+ return ret;
+ return data;
+}
+
+int mvgbe_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr,
+ int reg_addr, u16 data)
+{
+ return smi_reg_write(bus->name, phy_addr, reg_addr, data);
+}
+#endif
+
/* Stop and checks all queues */
static void stop_queue(u32 * qreg)
{
@@ -467,8 +488,9 @@ static int mvgbe_init(struct eth_device *dev)
/* Enable port Rx. */
MVGBE_REG_WR(regs->rqc, (1 << RXUQ));
-#if (defined (CONFIG_MII) || defined (CONFIG_CMD_MII)) \
- && defined (CONFIG_SYS_FAULT_ECHO_LINK_DOWN)
+#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) && \
+ !defined(CONFIG_PHYLIB) && \
+ defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN)
/* Wait up to 5s for the link status */
for (i = 0; i < 5; i++) {
u16 phyadr;
@@ -647,6 +669,45 @@ static int mvgbe_recv(struct eth_device *dev)
return 0;
}
+#if defined(CONFIG_PHYLIB)
+int mvgbe_phylib_init(struct eth_device *dev, int phyid)
+{
+ struct mii_dev *bus;
+ struct phy_device *phydev;
+ int ret;
+
+ bus = mdio_alloc();
+ if (!bus) {
+ printf("mdio_alloc failed\n");
+ return -ENOMEM;
+ }
+ bus->read = mvgbe_phy_read;
+ bus->write = mvgbe_phy_write;
+ sprintf(bus->name, dev->name);
+
+ ret = mdio_register(bus);
+ if (ret) {
+ printf("mdio_register failed\n");
+ free(bus);
+ return -ENOMEM;
+ }
+
+ /* Set phy address of the port */
+ mvgbe_phy_write(bus, MV_PHY_ADR_REQUEST, 0, MV_PHY_ADR_REQUEST, phyid);
+
+ phydev = phy_connect(bus, phyid, dev, PHY_INTERFACE_MODE_RGMII);
+ if (!phydev) {
+ printf("phy_connect failed\n");
+ return -ENODEV;
+ }
+
+ phy_config(phydev);
+ phy_startup(phydev);
+
+ return 0;
+}
+#endif
+
int mvgbe_initialize(bd_t *bis)
{
struct mvgbe_device *dmvgbe;
@@ -729,7 +790,9 @@ error1:
eth_register(dev);
-#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+#if defined(CONFIG_PHYLIB)
+ mvgbe_phylib_init(dev, PHY_BASE_ADR + devnum);
+#elif defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_register(dev->name, smi_reg_read, smi_reg_write);
/* Set phy address of the port */
miiphy_write(dev->name, MV_PHY_ADR_REQUEST,
diff --git a/drivers/net/mvgbe.h b/drivers/net/mvgbe.h
index d8a5429..7f5d98f 100644
--- a/drivers/net/mvgbe.h
+++ b/drivers/net/mvgbe.h
@@ -308,10 +308,17 @@
#define EBAR_TARGET_GUNIT 0x00000007
/* Window attrib */
+#if defined(CONFIG_DOVE)
+#define EBAR_DRAM_CS0 0x00000000
+#define EBAR_DRAM_CS1 0x00000000
+#define EBAR_DRAM_CS2 0x00000000
+#define EBAR_DRAM_CS3 0x00000000
+#else
#define EBAR_DRAM_CS0 0x00000E00
#define EBAR_DRAM_CS1 0x00000D00
#define EBAR_DRAM_CS2 0x00000B00
#define EBAR_DRAM_CS3 0x00000700
+#endif
/* DRAM Target interface */
#define EBAR_DRAM_NO_CACHE_COHERENCY 0x00000000
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index af5f4b8..695873e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -35,6 +35,7 @@ COBJS-$(CONFIG_PHY_ATHEROS) += atheros.o
COBJS-$(CONFIG_PHY_BROADCOM) += broadcom.o
COBJS-$(CONFIG_PHY_DAVICOM) += davicom.o
COBJS-$(CONFIG_PHY_ET1011C) += et1011c.o
+COBJS-$(CONFIG_PHY_ICPLUS) += icplus.o
COBJS-$(CONFIG_PHY_LXT) += lxt.o
COBJS-$(CONFIG_PHY_MARVELL) += marvell.o
COBJS-$(CONFIG_PHY_MICREL) += micrel.o
diff --git a/drivers/net/phy/atheros.c b/drivers/net/phy/atheros.c
index 9b3808b..09d4879 100644
--- a/drivers/net/phy/atheros.c
+++ b/drivers/net/phy/atheros.c
@@ -16,7 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
- * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011, 2013 Freescale Semiconductor, Inc.
* author Andy Fleming
*
*/
@@ -30,6 +30,27 @@ static int ar8021_config(struct phy_device *phydev)
return 0;
}
+static int ar8035_config(struct phy_device *phydev)
+{
+ int regval;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, 0xd, 0x0007);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0xe, 0x8016);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0xd, 0x4007);
+ regval = phy_read(phydev, MDIO_DEVAD_NONE, 0xe);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0xe, (regval|0x0018));
+
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05);
+ regval = phy_read(phydev, MDIO_DEVAD_NONE, 0x1e);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, (regval|0x0100));
+
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
static struct phy_driver AR8021_driver = {
.name = "AR8021",
.uid = 0x4dd040,
@@ -40,9 +61,31 @@ static struct phy_driver AR8021_driver = {
.shutdown = genphy_shutdown,
};
+static struct phy_driver AR8031_driver = {
+ .name = "AR8031",
+ .uid = 0x4dd074,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = genphy_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+static struct phy_driver AR8035_driver = {
+ .name = "AR8035",
+ .uid = 0x4dd072,
+ .mask = 0x4fffff,
+ .features = PHY_GBIT_FEATURES,
+ .config = ar8035_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
int phy_atheros_init(void)
{
phy_register(&AR8021_driver);
+ phy_register(&AR8031_driver);
+ phy_register(&AR8035_driver);
return 0;
}
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
new file mode 100644
index 0000000..dd5c592
--- /dev/null
+++ b/drivers/net/phy/icplus.c
@@ -0,0 +1,94 @@
+/*
+ * ICPlus PHY drivers
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ */
+#include <phy.h>
+
+/* IP101A/G - IP1001 */
+#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */
+#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */
+#define IP1001_PHASE_SEL_MASK 3 /* IP1001 RX/TXPHASE_SEL */
+#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */
+#define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */
+#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */
+#define IP101A_G_IRQ_PIN_USED (1<<15) /* INTR pin used */
+#define IP101A_G_IRQ_DEFAULT IP101A_G_IRQ_PIN_USED
+
+static int ip1001_config(struct phy_device *phydev)
+{
+ int c;
+
+ /* Enable Auto Power Saving mode */
+ c = phy_read(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2);
+ if (c < 0)
+ return c;
+ c |= IP1001_APS_ON;
+ c = phy_write(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2, c);
+ if (c < 0)
+ return c;
+
+ /* INTR pin used: speed/link/duplex will cause an interrupt */
+ c = phy_write(phydev, MDIO_DEVAD_NONE, IP101A_G_IRQ_CONF_STATUS,
+ IP101A_G_IRQ_DEFAULT);
+ if (c < 0)
+ return c;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
+ /*
+ * Additional delay (2ns) used to adjust RX clock phase
+ * at RGMII interface
+ */
+ c = phy_read(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS);
+ if (c < 0)
+ return c;
+
+ c |= IP1001_PHASE_SEL_MASK;
+ c = phy_write(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS,
+ c);
+ if (c < 0)
+ return c;
+ }
+
+ return 0;
+}
+
+static int ip1001_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ genphy_parse_link(phydev);
+
+ return 0;
+}
+static struct phy_driver IP1001_driver = {
+ .name = "ICPlus IP1001",
+ .uid = 0x02430d90,
+ .mask = 0x0ffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &ip1001_config,
+ .startup = &ip1001_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_icplus_init(void)
+{
+ phy_register(&IP1001_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 46801c7..8397e32 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -89,6 +89,12 @@
#define MIIM_88E1149_PHY_PAGE 29
+/* 88E1310 PHY defines */
+#define MIIM_88E1310_PHY_LED_CTRL 16
+#define MIIM_88E1310_PHY_IRQ_EN 18
+#define MIIM_88E1310_PHY_RGMII_CTRL 21
+#define MIIM_88E1310_PHY_PAGE 22
+
/* Marvell 88E1011S */
static int m88e1011s_config(struct phy_device *phydev)
{
@@ -394,6 +400,37 @@ static int m88e1149_config(struct phy_device *phydev)
return 0;
}
+/* Marvell 88E1310 */
+static int m88e1310_config(struct phy_device *phydev)
+{
+ u16 reg;
+
+ /* LED link and activity */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0003);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_LED_CTRL);
+ reg = (reg & ~0xf) | 0x1;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_LED_CTRL, reg);
+
+ /* Set LED2/INT to INT mode, low active */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0003);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_IRQ_EN);
+ reg = (reg & 0x77ff) | 0x0880;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_IRQ_EN, reg);
+
+ /* Set RGMII delay */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0002);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_RGMII_CTRL);
+ reg |= 0x0030;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_RGMII_CTRL, reg);
+
+ /* Ensure to return to page 0 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0000);
+
+ genphy_config_aneg(phydev);
+ phy_reset(phydev);
+
+ return 0;
+}
static struct phy_driver M88E1011S_driver = {
.name = "Marvell 88E1011S",
@@ -475,8 +512,19 @@ static struct phy_driver M88E1518_driver = {
.shutdown = &genphy_shutdown,
};
+static struct phy_driver M88E1310_driver = {
+ .name = "Marvell 88E1310",
+ .uid = 0x01410e90,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1310_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
int phy_marvell_init(void)
{
+ phy_register(&M88E1310_driver);
phy_register(&M88E1149S_driver);
phy_register(&M88E1145_driver);
phy_register(&M88E1121R_driver);
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 30f3264..aa9cbcf 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -18,6 +18,7 @@
*
* Copyright 2010-2011 Freescale Semiconductor, Inc.
* author Andy Fleming
+ * (C) 2012 NetModule AG, David Andrey, added KSZ9031
*
*/
#include <config.h>
@@ -52,16 +53,46 @@ static struct phy_driver KS8721_driver = {
};
#endif
+
+/**
+ * KSZ9021 - KSZ9031 common
+ */
+
+#define MII_KSZ90xx_PHY_CTL 0x1f
+#define MIIM_KSZ90xx_PHYCTL_1000 (1 << 6)
+#define MIIM_KSZ90xx_PHYCTL_100 (1 << 5)
+#define MIIM_KSZ90xx_PHYCTL_10 (1 << 4)
+#define MIIM_KSZ90xx_PHYCTL_DUPLEX (1 << 3)
+
+static int ksz90xx_startup(struct phy_device *phydev)
+{
+ unsigned phy_ctl;
+ genphy_update_link(phydev);
+ phy_ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ90xx_PHY_CTL);
+
+ if (phy_ctl & MIIM_KSZ90xx_PHYCTL_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (phy_ctl & MIIM_KSZ90xx_PHYCTL_1000)
+ phydev->speed = SPEED_1000;
+ else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_100)
+ phydev->speed = SPEED_100;
+ else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_10)
+ phydev->speed = SPEED_10;
+ return 0;
+}
#ifdef CONFIG_PHY_MICREL_KSZ9021
-/* ksz9021 PHY Registers */
+
+/*
+ * KSZ9021
+ */
+
+/* PHY Registers */
#define MII_KSZ9021_EXTENDED_CTRL 0x0b
#define MII_KSZ9021_EXTENDED_DATAW 0x0c
#define MII_KSZ9021_EXTENDED_DATAR 0x0d
-#define MII_KSZ9021_PHY_CTL 0x1f
-#define MIIM_KSZ9021_PHYCTL_1000 (1 << 6)
-#define MIIM_KSZ9021_PHYCTL_100 (1 << 5)
-#define MIIM_KSZ9021_PHYCTL_10 (1 << 4)
-#define MIIM_KSZ9021_PHYCTL_DUPLEX (1 << 3)
#define CTRL1000_PREFER_MASTER (1 << 10)
#define CTRL1000_CONFIG_MASTER (1 << 11)
@@ -106,37 +137,64 @@ static int ksz9021_config(struct phy_device *phydev)
return 0;
}
-static int ksz9021_startup(struct phy_device *phydev)
-{
- unsigned phy_ctl;
- genphy_update_link(phydev);
- phy_ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_PHY_CTL);
-
- if (phy_ctl & MIIM_KSZ9021_PHYCTL_DUPLEX)
- phydev->duplex = DUPLEX_FULL;
- else
- phydev->duplex = DUPLEX_HALF;
-
- if (phy_ctl & MIIM_KSZ9021_PHYCTL_1000)
- phydev->speed = SPEED_1000;
- else if (phy_ctl & MIIM_KSZ9021_PHYCTL_100)
- phydev->speed = SPEED_100;
- else if (phy_ctl & MIIM_KSZ9021_PHYCTL_10)
- phydev->speed = SPEED_10;
- return 0;
-}
-
static struct phy_driver ksz9021_driver = {
.name = "Micrel ksz9021",
.uid = 0x221610,
.mask = 0xfffff0,
.features = PHY_GBIT_FEATURES,
.config = &ksz9021_config,
- .startup = &ksz9021_startup,
+ .startup = &ksz90xx_startup,
.shutdown = &genphy_shutdown,
};
#endif
+/**
+ * KSZ9031
+ */
+/* PHY Registers */
+#define MII_KSZ9031_MMD_ACCES_CTRL 0x0d
+#define MII_KSZ9031_MMD_REG_DATA 0x0e
+
+/* Accessors to extended registers*/
+int ksz9031_phy_extended_write(struct phy_device *phydev,
+ int devaddr, int regnum, u16 mode, u16 val)
+{
+ /*select register addr for mmd*/
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
+ /*select register for mmd*/
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_REG_DATA, regnum);
+ /*setup mode*/
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, (mode | devaddr));
+ /*write the value*/
+ return phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_REG_DATA, val);
+}
+
+int ksz9031_phy_extended_read(struct phy_device *phydev, int devaddr,
+ int regnum, u16 mode)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_REG_DATA, regnum);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, (devaddr | mode));
+ return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9031_MMD_REG_DATA);
+}
+
+static struct phy_driver ksz9031_driver = {
+ .name = "Micrel ksz9031",
+ .uid = 0x221620,
+ .mask = 0xfffffe,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config,
+ .startup = &ksz90xx_startup,
+ .shutdown = &genphy_shutdown,
+};
+
int phy_micrel_init(void)
{
phy_register(&KSZ804_driver);
@@ -145,5 +203,6 @@ int phy_micrel_init(void)
#else
phy_register(&KS8721_driver);
#endif
+ phy_register(&ksz9031_driver);
return 0;
}
diff --git a/drivers/net/phy/natsemi.c b/drivers/net/phy/natsemi.c
index ea60ac1..6dc7ed5 100644
--- a/drivers/net/phy/natsemi.c
+++ b/drivers/net/phy/natsemi.c
@@ -22,6 +22,42 @@
*/
#include <phy.h>
+/* NatSemi DP83630 */
+
+#define DP83630_PHY_PAGESEL_REG 0x13
+#define DP83630_PHY_PTP_COC_REG 0x14
+#define DP83630_PHY_PTP_CLKOUT_EN (1<<15)
+#define DP83630_PHY_RBR_REG 0x17
+
+static int dp83630_config(struct phy_device *phydev)
+{
+ int ptp_coc_reg;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+ phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PAGESEL_REG, 0x6);
+ ptp_coc_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ DP83630_PHY_PTP_COC_REG);
+ ptp_coc_reg &= ~DP83630_PHY_PTP_CLKOUT_EN;
+ phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PTP_COC_REG,
+ ptp_coc_reg);
+ phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PAGESEL_REG, 0);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static struct phy_driver DP83630_driver = {
+ .name = "NatSemi DP83630",
+ .uid = 0x20005ce1,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &dp83630_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+
/* DP83865 Link and Auto-Neg Status Register */
#define MIIM_DP83865_LANR 0x11
#define MIIM_DP83865_SPD_MASK 0x0018
@@ -90,6 +126,7 @@ static struct phy_driver DP83865_driver = {
int phy_natsemi_init(void)
{
+ phy_register(&DP83630_driver);
phy_register(&DP83865_driver);
return 0;
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index f8c5481..7c0eaec 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -75,6 +75,10 @@ static int genphy_config_advert(struct phy_device *phydev)
adv |= ADVERTISE_PAUSE_CAP;
if (advertise & ADVERTISED_Asym_Pause)
adv |= ADVERTISE_PAUSE_ASYM;
+ if (advertise & ADVERTISED_1000baseX_Half)
+ adv |= ADVERTISE_1000XHALF;
+ if (advertise & ADVERTISED_1000baseX_Full)
+ adv |= ADVERTISE_1000XFULL;
if (adv != oldadv) {
err = phy_write(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE, adv);
@@ -280,7 +284,7 @@ int genphy_update_link(struct phy_device *phydev)
*
* Stolen from Linux's mii.c and phy_device.c
*/
-static int genphy_parse_link(struct phy_device *phydev)
+int genphy_parse_link(struct phy_device *phydev)
{
int mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
@@ -288,6 +292,7 @@ static int genphy_parse_link(struct phy_device *phydev)
if (mii_reg & BMSR_ANEGCAPABLE) {
u32 lpa = 0;
u32 gblpa = 0;
+ u32 estatus = 0;
/* Check for gigabit capability */
if (mii_reg & BMSR_ERCAP) {
@@ -327,6 +332,18 @@ static int genphy_parse_link(struct phy_device *phydev)
} else if (lpa & LPA_10FULL)
phydev->duplex = DUPLEX_FULL;
+
+ if (mii_reg & BMSR_ESTATEN)
+ estatus = phy_read(phydev, MDIO_DEVAD_NONE,
+ MII_ESTATUS);
+
+ if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_XHALF |
+ ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) {
+ phydev->speed = SPEED_1000;
+ if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_TFULL))
+ phydev->duplex = DUPLEX_FULL;
+ }
+
} else {
u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
@@ -384,6 +401,10 @@ int genphy_config(struct phy_device *phydev)
features |= SUPPORTED_1000baseT_Full;
if (val & ESTATUS_1000_THALF)
features |= SUPPORTED_1000baseT_Half;
+ if (val & ESTATUS_1000_XFULL)
+ features |= SUPPORTED_1000baseX_Full;
+ if (val & ESTATUS_1000_XHALF)
+ features |= SUPPORTED_1000baseX_Full;
}
phydev->supported = features;
@@ -433,6 +454,9 @@ int phy_init(void)
#ifdef CONFIG_PHY_ET1011C
phy_et1011c_init();
#endif
+#ifdef CONFIG_PHY_ICPLUS
+ phy_icplus_init();
+#endif
#ifdef CONFIG_PHY_LXT
phy_lxt_init();
#endif
diff --git a/drivers/net/sunxi_wemac.c b/drivers/net/sunxi_wemac.c
new file mode 100644
index 0000000..1db3529
--- /dev/null
+++ b/drivers/net/sunxi_wemac.c
@@ -0,0 +1,533 @@
+/*
+ * sunxi_wemac.c -- Allwinner A10 ethernet driver
+ *
+ * (C) Copyright 2012, Stefan Roese <sr@denx.de>
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <net.h>
+#include <miiphy.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/gpio.h>
+
+/* EMAC register */
+struct wemac_regs {
+ u32 ctl; /* 0x00 */
+ u32 tx_mode; /* 0x04 */
+ u32 tx_flow; /* 0x08 */
+ u32 tx_ctl0; /* 0x0c */
+ u32 tx_ctl1; /* 0x10 */
+ u32 tx_ins; /* 0x14 */
+ u32 tx_pl0; /* 0x18 */
+ u32 tx_pl1; /* 0x1c */
+ u32 tx_sta; /* 0x20 */
+ u32 tx_io_data; /* 0x24 */
+ u32 tx_io_data1; /* 0x28 */
+ u32 tx_tsvl0; /* 0x2c */
+ u32 tx_tsvh0; /* 0x30 */
+ u32 tx_tsvl1; /* 0x34 */
+ u32 tx_tsvh1; /* 0x38 */
+ u32 rx_ctl; /* 0x3c */
+ u32 rx_hash0; /* 0x40 */
+ u32 rx_hash1; /* 0x44 */
+ u32 rx_sta; /* 0x48 */
+ u32 rx_io_data; /* 0x4c */
+ u32 rx_fbc; /* 0x50 */
+ u32 int_ctl; /* 0x54 */
+ u32 int_sta; /* 0x58 */
+ u32 mac_ctl0; /* 0x5c */
+ u32 mac_ctl1; /* 0x60 */
+ u32 mac_ipgt; /* 0x64 */
+ u32 mac_ipgr; /* 0x68 */
+ u32 mac_clrt; /* 0x6c */
+ u32 mac_maxf; /* 0x70 */
+ u32 mac_supp; /* 0x74 */
+ u32 mac_test; /* 0x78 */
+ u32 mac_mcfg; /* 0x7c */
+ u32 mac_mcmd; /* 0x80 */
+ u32 mac_madr; /* 0x84 */
+ u32 mac_mwtd; /* 0x88 */
+ u32 mac_mrdd; /* 0x8c */
+ u32 mac_mind; /* 0x90 */
+ u32 mac_ssrr; /* 0x94 */
+ u32 mac_a0; /* 0x98 */
+ u32 mac_a1; /* 0x9c */
+};
+
+/* SRAMC register */
+struct sunxi_sramc_regs {
+ u32 ctrl0;
+ u32 ctrl1;
+};
+
+/* 0: Disable 1: Aborted frame enable(default) */
+#define EMAC_TX_AB_M (0x1 << 0)
+/* 0: CPU 1: DMA(default) */
+#define EMAC_TX_TM (0x1 << 1)
+
+#define EMAC_TX_SETUP (0)
+
+/* 0: DRQ asserted 1: DRQ automatically(default) */
+#define EMAC_RX_DRQ_MODE (0x1 << 1)
+/* 0: CPU 1: DMA(default) */
+#define EMAC_RX_TM (0x1 << 2)
+/* 0: Normal(default) 1: Pass all Frames */
+#define EMAC_RX_PA (0x1 << 4)
+/* 0: Normal(default) 1: Pass Control Frames */
+#define EMAC_RX_PCF (0x1 << 5)
+/* 0: Normal(default) 1: Pass Frames with CRC Error */
+#define EMAC_RX_PCRCE (0x1 << 6)
+/* 0: Normal(default) 1: Pass Frames with Length Error */
+#define EMAC_RX_PLE (0x1 << 7)
+/* 0: Normal 1: Pass Frames length out of range(default) */
+#define EMAC_RX_POR (0x1 << 8)
+/* 0: Not accept 1: Accept unicast Packets(default) */
+#define EMAC_RX_UCAD (0x1 << 16)
+/* 0: Normal(default) 1: DA Filtering */
+#define EMAC_RX_DAF (0x1 << 17)
+/* 0: Not accept 1: Accept multicast Packets(default) */
+#define EMAC_RX_MCO (0x1 << 20)
+/* 0: Disable(default) 1: Enable Hash filter */
+#define EMAC_RX_MHF (0x1 << 21)
+/* 0: Not accept 1: Accept Broadcast Packets(default) */
+#define EMAC_RX_BCO (0x1 << 22)
+/* 0: Disable(default) 1: Enable SA Filtering */
+#define EMAC_RX_SAF (0x1 << 24)
+/* 0: Normal(default) 1: Inverse Filtering */
+#define EMAC_RX_SAIF (0x1 << 25)
+
+#define EMAC_RX_SETUP (EMAC_RX_POR | EMAC_RX_UCAD | EMAC_RX_DAF | \
+ EMAC_RX_MCO | EMAC_RX_BCO)
+
+/* 0: Disable 1: Enable Receive Flow Control(default) */
+#define EMAC_MAC_CTL0_RFC (0x1 << 2)
+/* 0: Disable 1: Enable Transmit Flow Control(default) */
+#define EMAC_MAC_CTL0_TFC (0x1 << 3)
+
+#define EMAC_MAC_CTL0_SETUP (EMAC_MAC_CTL0_RFC | EMAC_MAC_CTL0_TFC)
+
+/* 0: Disable 1: Enable MAC Frame Length Checking(default) */
+#define EMAC_MAC_CTL1_FLC (0x1 << 1)
+/* 0: Disable(default) 1: Enable Huge Frame */
+#define EMAC_MAC_CTL1_HF (0x1 << 2)
+/* 0: Disable(default) 1: Enable MAC Delayed CRC */
+#define EMAC_MAC_CTL1_DCRC (0x1 << 3)
+/* 0: Disable 1: Enable MAC CRC(default) */
+#define EMAC_MAC_CTL1_CRC (0x1 << 4)
+/* 0: Disable 1: Enable MAC PAD Short frames(default) */
+#define EMAC_MAC_CTL1_PC (0x1 << 5)
+/* 0: Disable(default) 1: Enable MAC PAD Short frames and append CRC */
+#define EMAC_MAC_CTL1_VC (0x1 << 6)
+/* 0: Disable(default) 1: Enable MAC auto detect Short frames */
+#define EMAC_MAC_CTL1_ADP (0x1 << 7)
+/* 0: Disable(default) 1: Enable */
+#define EMAC_MAC_CTL1_PRE (0x1 << 8)
+/* 0: Disable(default) 1: Enable */
+#define EMAC_MAC_CTL1_LPE (0x1 << 9)
+/* 0: Disable(default) 1: Enable no back off */
+#define EMAC_MAC_CTL1_NB (0x1 << 12)
+/* 0: Disable(default) 1: Enable */
+#define EMAC_MAC_CTL1_BNB (0x1 << 13)
+/* 0: Disable(default) 1: Enable */
+#define EMAC_MAC_CTL1_ED (0x1 << 14)
+
+#define EMAC_MAC_CTL1_SETUP (EMAC_MAC_CTL1_FLC | EMAC_MAC_CTL1_CRC | \
+ EMAC_MAC_CTL1_PC)
+
+#define EMAC_MAC_IPGT 0x15
+
+#define EMAC_MAC_NBTB_IPG1 0xC
+#define EMAC_MAC_NBTB_IPG2 0x12
+
+#define EMAC_MAC_CW 0x37
+#define EMAC_MAC_RM 0xF
+
+#define EMAC_MAC_MFL 0x0600
+
+/* Receive status */
+#define EMAC_CRCERR (1 << 4)
+#define EMAC_LENERR (3 << 5)
+
+#define DMA_CPU_TRRESHOLD 2000
+
+struct wemac_eth_dev {
+ u32 speed;
+ u32 duplex;
+ u32 phy_configured;
+ int link_printed;
+};
+
+struct wemac_rxhdr {
+ s16 rx_len;
+ u16 rx_status;
+};
+
+static void wemac_inblk_32bit(void *reg, void *data, int count)
+{
+ int cnt = (count + 3) >> 2;
+
+ if (cnt) {
+ u32 *buf = data;
+
+ do {
+ u32 x = readl(reg);
+ *buf++ = x;
+ } while (--cnt);
+ }
+}
+
+static void wemac_outblk_32bit(void *reg, void *data, int count)
+{
+ int cnt = (count + 3) >> 2;
+
+ if (cnt) {
+ const u32 *buf = data;
+
+ do {
+ writel(*buf++, reg);
+ } while (--cnt);
+ }
+}
+
+/*
+ * Read a word from phyxcer
+ */
+static int wemac_phy_read(const char *devname, unsigned char addr,
+ unsigned char reg, unsigned short *value)
+{
+ struct eth_device *dev = eth_get_dev_by_name(devname);
+ struct wemac_regs *regs = (struct wemac_regs *)dev->iobase;
+
+ /* issue the phy address and reg */
+ writel(addr << 8 | reg, &regs->mac_madr);
+
+ /* pull up the phy io line */
+ writel(0x1, &regs->mac_mcmd);
+
+ /* Wait read complete */
+ mdelay(1);
+
+ /* push down the phy io line */
+ writel(0x0, &regs->mac_mcmd);
+
+ /* and write data */
+ *value = readl(&regs->mac_mrdd);
+
+ return 0;
+}
+
+/*
+ * Write a word to phyxcer
+ */
+static int wemac_phy_write(const char *devname, unsigned char addr,
+ unsigned char reg, unsigned short value)
+{
+ struct eth_device *dev = eth_get_dev_by_name(devname);
+ struct wemac_regs *regs = (struct wemac_regs *)dev->iobase;
+
+ /* issue the phy address and reg */
+ writel(addr << 8 | reg, &regs->mac_madr);
+
+ /* pull up the phy io line */
+ writel(0x1, &regs->mac_mcmd);
+
+ /* Wait write complete */
+ mdelay(1);
+
+ /* push down the phy io line */
+ writel(0x0, &regs->mac_mcmd);
+
+ /* and write data */
+ writel(value, &regs->mac_mwtd);
+
+ return 0;
+}
+
+static void emac_setup(struct eth_device *dev)
+{
+ struct wemac_regs *regs = (struct wemac_regs *)dev->iobase;
+ u32 reg_val;
+ u16 phy_val;
+ u32 duplex_flag;
+
+ /* Set up TX */
+ writel(EMAC_TX_SETUP, &regs->tx_mode);
+
+ /* Set up RX */
+ writel(EMAC_RX_SETUP, &regs->rx_ctl);
+
+ /* Set MAC */
+ /* Set MAC CTL0 */
+ writel(EMAC_MAC_CTL0_SETUP, &regs->mac_ctl0);
+
+ /* Set MAC CTL1 */
+ wemac_phy_read(dev->name, 1, 0, &phy_val);
+ debug("PHY SETUP, reg 0 value: %x\n", phy_val);
+ duplex_flag = !!(phy_val & (1 << 8));
+
+ reg_val = 0;
+ if (duplex_flag)
+ reg_val = (0x1 << 0);
+ writel(EMAC_MAC_CTL1_SETUP | reg_val, &regs->mac_ctl1);
+
+ /* Set up IPGT */
+ writel(EMAC_MAC_IPGT, &regs->mac_ipgt);
+
+ /* Set up IPGR */
+ writel(EMAC_MAC_NBTB_IPG2 | (EMAC_MAC_NBTB_IPG1 << 8), &regs->mac_ipgr);
+
+ /* Set up Collison window */
+ writel(EMAC_MAC_RM | (EMAC_MAC_CW << 8), &regs->mac_clrt);
+
+ /* Set up Max Frame Length */
+ writel(EMAC_MAC_MFL, &regs->mac_maxf);
+}
+
+static void wemac_reset(struct eth_device *dev)
+{
+ struct wemac_regs *regs = (struct wemac_regs *)dev->iobase;
+
+ debug("resetting device\n");
+
+ /* RESET device */
+ writel(0, &regs->ctl);
+ udelay(200);
+
+ writel(1, &regs->ctl);
+ udelay(200);
+}
+
+static int sunxi_wemac_eth_init(struct eth_device *dev, bd_t *bd)
+{
+ struct wemac_regs *regs = (struct wemac_regs *)dev->iobase;
+ struct wemac_eth_dev *priv = dev->priv;
+ u16 phy_reg;
+
+ /* Init EMAC */
+
+ /* Flush RX FIFO */
+ setbits_le32(&regs->rx_ctl, 0x8);
+ udelay(1);
+
+ /* Init MAC */
+
+ /* Soft reset MAC */
+ clrbits_le32(&regs->mac_ctl0, 1 << 15);
+
+ /* Set MII clock */
+ clrsetbits_le32(&regs->mac_mcfg, 0xf << 2, 0xd << 2);
+
+ /* Clear RX counter */
+ writel(0x0, &regs->rx_fbc);
+ udelay(1);
+
+ /* Set up EMAC */
+ emac_setup(dev);
+
+ writel(dev->enetaddr[0] << 16 | dev->enetaddr[1] << 8 |
+ dev->enetaddr[2], &regs->mac_a1);
+ writel(dev->enetaddr[3] << 16 | dev->enetaddr[4] << 8 |
+ dev->enetaddr[5], &regs->mac_a0);
+
+ mdelay(1);
+
+ wemac_reset(dev);
+
+ /* PHY POWER UP */
+ wemac_phy_read(dev->name, 1, 0, &phy_reg);
+ wemac_phy_write(dev->name, 1, 0, phy_reg & (~(1 << 11)));
+ mdelay(1);
+
+ wemac_phy_read(dev->name, 1, 0, &phy_reg);
+
+ priv->speed = miiphy_speed(dev->name, 0);
+ priv->duplex = miiphy_duplex(dev->name, 0);
+
+ /* Print link status only once */
+ if (!priv->link_printed) {
+ printf("ENET Speed is %d Mbps - %s duplex connection\n",
+ priv->speed, (priv->duplex == HALF) ? "HALF" : "FULL");
+ priv->link_printed = 1;
+ }
+
+ /* Set EMAC SPEED depend on PHY */
+ clrsetbits_le32(&regs->mac_supp, 1 << 8,
+ ((phy_reg & (1 << 13)) >> 13) << 8);
+
+ /* Set duplex depend on phy */
+ clrsetbits_le32(&regs->mac_ctl1, 1 << 0,
+ ((phy_reg & (1 << 8)) >> 8) << 0);
+
+ /* Enable RX/TX */
+ setbits_le32(&regs->ctl, 0x7);
+
+ return 0;
+}
+
+static void sunxi_wemac_eth_halt(struct eth_device *dev)
+{
+ /* Nothing to do here */
+}
+
+static int sunxi_wemac_eth_recv(struct eth_device *dev)
+{
+ struct wemac_regs *regs = (struct wemac_regs *)dev->iobase;
+ struct wemac_rxhdr rxhdr;
+ u32 rxcount;
+ u32 reg_val;
+ int rx_len;
+ int rx_status;
+ int good_packet;
+
+ /* Check packet ready or not */
+
+ /*
+ * Race warning: The first packet might arrive with
+ * the interrupts disabled, but the second will fix
+ */
+ rxcount = readl(&regs->rx_fbc);
+ if (!rxcount) {
+ /* Had one stuck? */
+ rxcount = readl(&regs->rx_fbc);
+ if (!rxcount)
+ return 0;
+ }
+
+ reg_val = readl(&regs->rx_io_data);
+ if (reg_val != 0x0143414d) {
+ /* Disable RX */
+ clrbits_le32(&regs->ctl, 1 << 2);
+
+ /* Flush RX FIFO */
+ setbits_le32(&regs->rx_ctl, 1 << 3);
+ while (readl(&regs->rx_ctl) & (1 << 3))
+ ;
+
+ /* Enable RX */
+ setbits_le32(&regs->ctl, 1 << 2);
+
+ return 0;
+ }
+
+ /*
+ * A packet ready now
+ * Get status/length
+ */
+ good_packet = 1;
+
+ wemac_inblk_32bit(&regs->rx_io_data, &rxhdr, sizeof(rxhdr));
+
+ rx_len = rxhdr.rx_len;
+ rx_status = rxhdr.rx_status;
+
+ /* Packet Status check */
+ if (rx_len < 0x40) {
+ good_packet = 0;
+ debug("RX: Bad Packet (runt)\n");
+ }
+
+ /* rx_status is identical to RSR register. */
+ if (0 & rx_status & (EMAC_CRCERR | EMAC_LENERR)) {
+ good_packet = 0;
+ if (rx_status & EMAC_CRCERR)
+ printf("crc error\n");
+ if (rx_status & EMAC_LENERR)
+ printf("length error\n");
+ }
+
+ /* Move data from WEMAC */
+ if (good_packet) {
+ if (rx_len > DMA_CPU_TRRESHOLD) {
+ printf("Received packet is too big (len=%d)\n", rx_len);
+ } else {
+ wemac_inblk_32bit((void *)&regs->rx_io_data,
+ NetRxPackets[0], rx_len);
+
+ /* Pass to upper layer */
+ NetReceive(NetRxPackets[0], rx_len);
+ return rx_len;
+ }
+ }
+
+ return 0;
+}
+
+static int sunxi_wemac_eth_send(struct eth_device *dev, void *packet, int len)
+{
+ struct wemac_regs *regs = (struct wemac_regs *)dev->iobase;
+
+ /* Select channel 0 */
+ writel(0, &regs->tx_ins);
+
+ /* Write packet */
+ wemac_outblk_32bit((void *)&regs->tx_io_data, packet, len);
+
+ /* Set TX len */
+ writel(len, &regs->tx_pl0);
+
+ /* Start translate from fifo to phy */
+ setbits_le32(&regs->tx_ctl0, 1);
+
+ return 0;
+}
+
+int sunxi_wemac_initialize(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ struct sunxi_sramc_regs *sram =
+ (struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE;
+ struct eth_device *dev;
+ struct wemac_eth_dev *priv;
+ int pin;
+
+ dev = malloc(sizeof(*dev));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ priv = (struct wemac_eth_dev *)malloc(sizeof(struct wemac_eth_dev));
+ if (!priv) {
+ free(dev);
+ return -ENOMEM;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+ memset(priv, 0, sizeof(struct wemac_eth_dev));
+
+ /* Map SRAM to EMAC */
+ setbits_le32(&sram->ctrl1, 0x5 << 2);
+
+ /* Configure pin mux settings for MII Ethernet */
+ for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++)
+ sunxi_gpio_set_cfgpin(pin, 2);
+
+ /* Set up clock gating */
+ setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_EMAC);
+
+ dev->iobase = SUNXI_EMAC_BASE;
+ dev->priv = priv;
+ dev->init = sunxi_wemac_eth_init;
+ dev->halt = sunxi_wemac_eth_halt;
+ dev->send = sunxi_wemac_eth_send;
+ dev->recv = sunxi_wemac_eth_recv;
+ strcpy(dev->name, "wemac");
+
+ eth_register(dev);
+
+ miiphy_register(dev->name, wemac_phy_read, wemac_phy_write);
+
+ return 0;
+}