[U-Boot] [PATCH 0/7] net/ethoc improvements

Hello,
this series does the following improvements to the OpenCores 10/100 Mbps driver: - add Kconfig symbol for the driver; - add DM_ETH support; - add device tree support; - add optional phylib support; - add support for configurations with private packet memory; - clean up virtual/physical address usage.
Please review.
Max Filippov (7): net/ethoc: add Kconfig entry for the driver net/ethoc: use priv instead of dev internally net/ethoc: add CONFIG_DM_ETH support net/ethoc: support device tree net/ethoc: don't mix virtual and physical addresses net/ethoc: support private memory configurations net/ethoc: implement MDIO bus and support phylib
configs/openrisc-generic_defconfig | 2 + drivers/net/Kconfig | 5 + drivers/net/ethoc.c | 522 ++++++++++++++++++++++++++++------- include/configs/openrisc-generic.h | 1 - include/dm/platform_data/net_ethoc.h | 21 ++ 5 files changed, 450 insertions(+), 101 deletions(-) create mode 100644 include/dm/platform_data/net_ethoc.h

Add Kconfig entry for the driver, remove #define CONFIG_ETHOC from the only board configuration that uses it and put it into that board's defconfig.
Cc: Stefan Kristiansson stefan.kristiansson@saunalahti.fi Signed-off-by: Max Filippov jcmvbkbc@gmail.com --- configs/openrisc-generic_defconfig | 2 ++ drivers/net/Kconfig | 5 +++++ include/configs/openrisc-generic.h | 1 - 3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/configs/openrisc-generic_defconfig b/configs/openrisc-generic_defconfig index 14923c0..5bc81cc 100644 --- a/configs/openrisc-generic_defconfig +++ b/configs/openrisc-generic_defconfig @@ -6,5 +6,7 @@ CONFIG_TARGET_OPENRISC_GENERIC=y CONFIG_CMD_DHCP=y CONFIG_CMD_MII=y CONFIG_CMD_PING=y +CONFIG_NETDEVICES=y +CONFIG_ETHOC=y CONFIG_SYS_NS16550=y # CONFIG_AUTOBOOT is not set diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 88d8e83..be3ed73 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -124,6 +124,11 @@ config ETH_DESIGNWARE 100Mbit and 1 Gbit operation. You must enable CONFIG_PHYLIB to provide the PHY (physical media interface).
+config ETHOC + bool "OpenCores 10/100 Mbps Ethernet MAC" + help + This MAC is present in OpenRISC and Xtensa XTFPGA boards. + config MVPP2 bool "Marvell Armada 375 network interface support" depends on ARMADA_375 diff --git a/include/configs/openrisc-generic.h b/include/configs/openrisc-generic.h index 913256a..227c0ca 100644 --- a/include/configs/openrisc-generic.h +++ b/include/configs/openrisc-generic.h @@ -44,7 +44,6 @@ /* * Ethernet */ -#define CONFIG_ETHOC #define CONFIG_SYS_ETHOC_BASE 0x92000000
#define CONFIG_BOOTFILE "boot.img"

On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Add Kconfig entry for the driver, remove #define CONFIG_ETHOC from the only board configuration that uses it and put it into that board's defconfig.
Cc: Stefan Kristiansson stefan.kristiansson@saunalahti.fi Signed-off-by: Max Filippov jcmvbkbc@gmail.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

Don't use physical base address of registers directly, ioremap it first. Save pointer in private struct ethoc and use that struct in all internal functions.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com --- drivers/net/ethoc.c | 111 ++++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 55 deletions(-)
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index edb3c80..f5bd1ab 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -12,11 +12,10 @@ */
#include <common.h> -#include <command.h> +#include <linux/io.h> #include <malloc.h> #include <net.h> #include <miiphy.h> -#include <asm/io.h> #include <asm/cache.h>
/* register offsets */ @@ -162,6 +161,7 @@ #define ETHOC_BD_BASE 0x400 #define ETHOC_TIMEOUT (HZ / 2) #define ETHOC_MII_TIMEOUT (1 + (HZ / 5)) +#define ETHOC_IOSIZE 0x54
/** * struct ethoc - driver-private device structure @@ -177,6 +177,7 @@ struct ethoc { u32 dty_tx; u32 num_rx; u32 cur_rx; + void __iomem *iobase; };
/** @@ -189,64 +190,64 @@ struct ethoc_bd { u32 addr; };
-static inline u32 ethoc_read(struct eth_device *dev, size_t offset) +static inline u32 ethoc_read(struct ethoc *priv, size_t offset) { - return readl(dev->iobase + offset); + return readl(priv->iobase + offset); }
-static inline void ethoc_write(struct eth_device *dev, size_t offset, u32 data) +static inline void ethoc_write(struct ethoc *priv, size_t offset, u32 data) { - writel(data, dev->iobase + offset); + writel(data, priv->iobase + offset); }
-static inline void ethoc_read_bd(struct eth_device *dev, int index, +static inline void ethoc_read_bd(struct ethoc *priv, int index, struct ethoc_bd *bd) { size_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); - bd->stat = ethoc_read(dev, offset + 0); - bd->addr = ethoc_read(dev, offset + 4); + bd->stat = ethoc_read(priv, offset + 0); + bd->addr = ethoc_read(priv, offset + 4); }
-static inline void ethoc_write_bd(struct eth_device *dev, int index, +static inline void ethoc_write_bd(struct ethoc *priv, int index, const struct ethoc_bd *bd) { size_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); - ethoc_write(dev, offset + 0, bd->stat); - ethoc_write(dev, offset + 4, bd->addr); + ethoc_write(priv, offset + 0, bd->stat); + ethoc_write(priv, offset + 4, bd->addr); }
static int ethoc_set_mac_address(struct eth_device *dev) { + struct ethoc *priv = (struct ethoc *)dev->priv; u8 *mac = dev->enetaddr;
- ethoc_write(dev, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | + ethoc_write(priv, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | (mac[5] << 0)); - ethoc_write(dev, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); + ethoc_write(priv, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); return 0; }
-static inline void ethoc_ack_irq(struct eth_device *dev, u32 mask) +static inline void ethoc_ack_irq(struct ethoc *priv, u32 mask) { - ethoc_write(dev, INT_SOURCE, mask); + ethoc_write(priv, INT_SOURCE, mask); }
-static inline void ethoc_enable_rx_and_tx(struct eth_device *dev) +static inline void ethoc_enable_rx_and_tx(struct ethoc *priv) { - u32 mode = ethoc_read(dev, MODER); + u32 mode = ethoc_read(priv, MODER); mode |= MODER_RXEN | MODER_TXEN; - ethoc_write(dev, MODER, mode); + ethoc_write(priv, MODER, mode); }
-static inline void ethoc_disable_rx_and_tx(struct eth_device *dev) +static inline void ethoc_disable_rx_and_tx(struct ethoc *priv) { - u32 mode = ethoc_read(dev, MODER); + u32 mode = ethoc_read(priv, MODER); mode &= ~(MODER_RXEN | MODER_TXEN); - ethoc_write(dev, MODER, mode); + ethoc_write(priv, MODER, mode); }
-static int ethoc_init_ring(struct eth_device *dev) +static int ethoc_init_ring(struct ethoc *priv) { - struct ethoc *priv = (struct ethoc *)dev->priv; struct ethoc_bd bd; int i;
@@ -261,7 +262,7 @@ static int ethoc_init_ring(struct eth_device *dev) if (i == priv->num_tx - 1) bd.stat |= TX_BD_WRAP;
- ethoc_write_bd(dev, i, &bd); + ethoc_write_bd(priv, i, &bd); }
bd.stat = RX_BD_EMPTY | RX_BD_IRQ; @@ -272,35 +273,35 @@ static int ethoc_init_ring(struct eth_device *dev) bd.stat |= RX_BD_WRAP;
flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); - ethoc_write_bd(dev, priv->num_tx + i, &bd); + ethoc_write_bd(priv, priv->num_tx + i, &bd); }
return 0; }
-static int ethoc_reset(struct eth_device *dev) +static int ethoc_reset(struct ethoc *priv) { u32 mode;
/* TODO: reset controller? */
- ethoc_disable_rx_and_tx(dev); + ethoc_disable_rx_and_tx(priv);
/* TODO: setup registers */
/* enable FCS generation and automatic padding */ - mode = ethoc_read(dev, MODER); + mode = ethoc_read(priv, MODER); mode |= MODER_CRC | MODER_PAD; - ethoc_write(dev, MODER, mode); + ethoc_write(priv, MODER, mode);
/* set full-duplex mode */ - mode = ethoc_read(dev, MODER); + mode = ethoc_read(priv, MODER); mode |= MODER_FULLD; - ethoc_write(dev, MODER, mode); - ethoc_write(dev, IPGT, 0x15); + ethoc_write(priv, MODER, mode); + ethoc_write(priv, IPGT, 0x15);
- ethoc_ack_irq(dev, INT_MASK_ALL); - ethoc_enable_rx_and_tx(dev); + ethoc_ack_irq(priv, INT_MASK_ALL); + ethoc_enable_rx_and_tx(priv); return 0; }
@@ -311,9 +312,9 @@ static int ethoc_init(struct eth_device *dev, bd_t * bd)
priv->num_tx = 1; priv->num_rx = PKTBUFSRX; - ethoc_write(dev, TX_BD_NUM, priv->num_tx); - ethoc_init_ring(dev); - ethoc_reset(dev); + ethoc_write(priv, TX_BD_NUM, priv->num_tx); + ethoc_init_ring(priv); + ethoc_reset(priv);
return 0; } @@ -353,9 +354,8 @@ static int ethoc_update_rx_stats(struct ethoc_bd *bd) return ret; }
-static int ethoc_rx(struct eth_device *dev, int limit) +static int ethoc_rx(struct ethoc *priv, int limit) { - struct ethoc *priv = (struct ethoc *)dev->priv; int count;
for (count = 0; count < limit; ++count) { @@ -363,7 +363,7 @@ static int ethoc_rx(struct eth_device *dev, int limit) struct ethoc_bd bd;
entry = priv->num_tx + (priv->cur_rx % priv->num_rx); - ethoc_read_bd(dev, entry, &bd); + ethoc_read_bd(priv, entry, &bd); if (bd.stat & RX_BD_EMPTY) break;
@@ -379,7 +379,7 @@ static int ethoc_rx(struct eth_device *dev, int limit) flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); bd.stat &= ~RX_BD_STATS; bd.stat |= RX_BD_EMPTY; - ethoc_write_bd(dev, entry, &bd); + ethoc_write_bd(priv, entry, &bd); priv->cur_rx++; }
@@ -403,13 +403,12 @@ static int ethoc_update_tx_stats(struct ethoc_bd *bd) return 0; }
-static void ethoc_tx(struct eth_device *dev) +static void ethoc_tx(struct ethoc *priv) { - struct ethoc *priv = (struct ethoc *)dev->priv; u32 entry = priv->dty_tx % priv->num_tx; struct ethoc_bd bd;
- ethoc_read_bd(dev, entry, &bd); + ethoc_read_bd(priv, entry, &bd); if ((bd.stat & TX_BD_READY) == 0) (void)ethoc_update_tx_stats(&bd); } @@ -423,7 +422,7 @@ static int ethoc_send(struct eth_device *dev, void *packet, int length) int tmo;
entry = priv->cur_tx % priv->num_tx; - ethoc_read_bd(dev, entry, &bd); + ethoc_read_bd(priv, entry, &bd); if (unlikely(length < ETHOC_ZLEN)) bd.stat |= TX_BD_PAD; else @@ -433,22 +432,22 @@ static int ethoc_send(struct eth_device *dev, void *packet, int length) flush_dcache_range(bd.addr, bd.addr + length); bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); bd.stat |= TX_BD_LEN(length); - ethoc_write_bd(dev, entry, &bd); + ethoc_write_bd(priv, entry, &bd);
/* start transmit */ bd.stat |= TX_BD_READY; - ethoc_write_bd(dev, entry, &bd); + ethoc_write_bd(priv, entry, &bd);
/* wait for transfer to succeed */ tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; while (1) { - pending = ethoc_read(dev, INT_SOURCE); - ethoc_ack_irq(dev, pending & ~INT_MASK_RX); + pending = ethoc_read(priv, INT_SOURCE); + ethoc_ack_irq(priv, pending & ~INT_MASK_RX); if (pending & INT_MASK_BUSY) debug("%s(): packet dropped\n", __func__);
if (pending & INT_MASK_TX) { - ethoc_tx(dev); + ethoc_tx(priv); break; } if (get_timer(0) >= tmo) { @@ -463,20 +462,21 @@ static int ethoc_send(struct eth_device *dev, void *packet, int length)
static void ethoc_halt(struct eth_device *dev) { - ethoc_disable_rx_and_tx(dev); + ethoc_disable_rx_and_tx(dev->priv); }
static int ethoc_recv(struct eth_device *dev) { + struct ethoc *priv = (struct ethoc *)dev->priv; u32 pending;
- pending = ethoc_read(dev, INT_SOURCE); - ethoc_ack_irq(dev, pending); + pending = ethoc_read(priv, INT_SOURCE); + ethoc_ack_irq(priv, pending); if (pending & INT_MASK_BUSY) debug("%s(): packet dropped\n", __func__); if (pending & INT_MASK_RX) { debug("%s(): rx irq\n", __func__); - ethoc_rx(dev, PKTBUFSRX); + ethoc_rx(priv, PKTBUFSRX); }
return 0; @@ -505,6 +505,7 @@ int ethoc_initialize(u8 dev_num, int base_addr) dev->recv = ethoc_recv; dev->write_hwaddr = ethoc_set_mac_address; sprintf(dev->name, "%s-%hu", "ETHOC", dev_num); + priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE);
eth_register(dev); return 1;

On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Don't use physical base address of registers directly, ioremap it first. Save pointer in private struct ethoc and use that struct in all internal functions.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

Extract reusable parts from ethoc_init, ethoc_set_mac_address, ethoc_send and ethoc_receive, move the rest under #ifdef CONFIG_DM_ETH. Add U_BOOT_DRIVER, eth_ops structure and implement required methods.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com --- drivers/net/ethoc.c | 221 +++++++++++++++++++++++++++-------- include/dm/platform_data/net_ethoc.h | 20 ++++ 2 files changed, 194 insertions(+), 47 deletions(-) create mode 100644 include/dm/platform_data/net_ethoc.h
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index f5bd1ab..0225595 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -5,13 +5,14 @@ * Copyright (C) 2008-2009 Avionic Design GmbH * Thierry Reding thierry.reding@avionic-design.de * Copyright (C) 2010 Thomas Chou thomas@wytron.com.tw + * Copyright (C) 2016 Cadence Design Systems Inc. * - * 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. + * SPDX-License-Identifier: GPL-2.0 */
#include <common.h> +#include <dm/device.h> +#include <dm/platform_data/net_ethoc.h> #include <linux/io.h> #include <malloc.h> #include <net.h> @@ -216,11 +217,8 @@ static inline void ethoc_write_bd(struct ethoc *priv, int index, ethoc_write(priv, offset + 4, bd->addr); }
-static int ethoc_set_mac_address(struct eth_device *dev) +static int ethoc_set_mac_address(struct ethoc *priv, u8 *mac) { - struct ethoc *priv = (struct ethoc *)dev->priv; - u8 *mac = dev->enetaddr; - ethoc_write(priv, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | (mac[5] << 0)); ethoc_write(priv, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); @@ -305,11 +303,8 @@ static int ethoc_reset(struct ethoc *priv) return 0; }
-static int ethoc_init(struct eth_device *dev, bd_t * bd) +static int ethoc_init_common(struct ethoc *priv) { - struct ethoc *priv = (struct ethoc *)dev->priv; - printf("ethoc\n"); - priv->num_tx = 1; priv->num_rx = PKTBUFSRX; ethoc_write(priv, TX_BD_NUM, priv->num_tx); @@ -354,36 +349,43 @@ static int ethoc_update_rx_stats(struct ethoc_bd *bd) return ret; }
-static int ethoc_rx(struct ethoc *priv, int limit) +static int ethoc_rx_common(struct ethoc *priv, uchar **packetp) { - int count; - - for (count = 0; count < limit; ++count) { - u32 entry; - struct ethoc_bd bd; + u32 entry; + struct ethoc_bd bd;
- entry = priv->num_tx + (priv->cur_rx % priv->num_rx); - ethoc_read_bd(priv, entry, &bd); - if (bd.stat & RX_BD_EMPTY) - break; + entry = priv->num_tx + (priv->cur_rx % priv->num_rx); + ethoc_read_bd(priv, entry, &bd); + if (bd.stat & RX_BD_EMPTY) + return -EAGAIN; + + debug("%s(): RX buffer %d, %x received\n", + __func__, priv->cur_rx, bd.stat); + if (ethoc_update_rx_stats(&bd) == 0) { + int size = bd.stat >> 16; + + size -= 4; /* strip the CRC */ + *packetp = (void *)bd.addr; + return size; + } else { + return 0; + } +}
- debug("%s(): RX buffer %d, %x received\n", - __func__, priv->cur_rx, bd.stat); - if (ethoc_update_rx_stats(&bd) == 0) { - int size = bd.stat >> 16; - size -= 4; /* strip the CRC */ - net_process_received_packet((void *)bd.addr, size); - } +static int ethoc_recv_common(struct ethoc *priv) +{ + u32 pending;
- /* clear the buffer descriptor so it can be reused */ - flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); - bd.stat &= ~RX_BD_STATS; - bd.stat |= RX_BD_EMPTY; - ethoc_write_bd(priv, entry, &bd); - priv->cur_rx++; + pending = ethoc_read(priv, INT_SOURCE); + ethoc_ack_irq(priv, pending); + if (pending & INT_MASK_BUSY) + debug("%s(): packet dropped\n", __func__); + if (pending & INT_MASK_RX) { + debug("%s(): rx irq\n", __func__); + return 1; }
- return count; + return 0; }
static int ethoc_update_tx_stats(struct ethoc_bd *bd) @@ -413,9 +415,8 @@ static void ethoc_tx(struct ethoc *priv) (void)ethoc_update_tx_stats(&bd); }
-static int ethoc_send(struct eth_device *dev, void *packet, int length) +static int ethoc_send_common(struct ethoc *priv, void *packet, int length) { - struct ethoc *priv = (struct ethoc *)dev->priv; struct ethoc_bd bd; u32 entry; u32 pending; @@ -460,6 +461,47 @@ static int ethoc_send(struct eth_device *dev, void *packet, int length) return 0; }
+static int ethoc_free_pkt_common(struct ethoc *priv) +{ + u32 entry; + struct ethoc_bd bd; + + entry = priv->num_tx + (priv->cur_rx % priv->num_rx); + ethoc_read_bd(priv, entry, &bd); + + /* clear the buffer descriptor so it can be reused */ + flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); + bd.stat &= ~RX_BD_STATS; + bd.stat |= RX_BD_EMPTY; + ethoc_write_bd(priv, entry, &bd); + priv->cur_rx++; + + return 0; +} + +#ifndef CONFIG_DM_ETH + +static int ethoc_init(struct eth_device *dev, bd_t *bd) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + + priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE); + return ethoc_init_common(priv); +} + +static int ethoc_write_hwaddr(struct eth_device *dev) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + u8 *mac = dev->enetaddr; + + return ethoc_set_mac_address(priv, mac); +} + +static int ethoc_send(struct eth_device *dev, void *packet, int length) +{ + return ethoc_send_common(dev->priv, packet, length); +} + static void ethoc_halt(struct eth_device *dev) { ethoc_disable_rx_and_tx(dev->priv); @@ -468,17 +510,21 @@ static void ethoc_halt(struct eth_device *dev) static int ethoc_recv(struct eth_device *dev) { struct ethoc *priv = (struct ethoc *)dev->priv; - u32 pending; + int count;
- pending = ethoc_read(priv, INT_SOURCE); - ethoc_ack_irq(priv, pending); - if (pending & INT_MASK_BUSY) - debug("%s(): packet dropped\n", __func__); - if (pending & INT_MASK_RX) { - debug("%s(): rx irq\n", __func__); - ethoc_rx(priv, PKTBUFSRX); - } + if (!ethoc_recv_common(priv)) + return 0;
+ for (count = 0; count < PKTBUFSRX; ++count) { + uchar *packetp; + int size = ethoc_rx_common(priv, &packetp); + + if (size < 0) + break; + if (size > 0) + net_process_received_packet(packetp, size); + ethoc_free_pkt_common(priv); + } return 0; }
@@ -503,10 +549,91 @@ int ethoc_initialize(u8 dev_num, int base_addr) dev->halt = ethoc_halt; dev->send = ethoc_send; dev->recv = ethoc_recv; - dev->write_hwaddr = ethoc_set_mac_address; + dev->write_hwaddr = ethoc_write_hwaddr; sprintf(dev->name, "%s-%hu", "ETHOC", dev_num); priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE);
eth_register(dev); return 1; } + +#else + +static int ethoc_write_hwaddr(struct udevice *dev) +{ + struct ethoc_eth_pdata *pdata = dev_get_platdata(dev); + struct ethoc *priv = dev_get_priv(dev); + u8 *mac = pdata->eth_pdata.enetaddr; + + return ethoc_set_mac_address(priv, mac); +} + +static int ethoc_send(struct udevice *dev, void *packet, int length) +{ + return ethoc_send_common(dev_get_priv(dev), packet, length); +} + +static int ethoc_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + return ethoc_free_pkt_common(dev_get_priv(dev)); +} + +static int ethoc_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct ethoc *priv = dev_get_priv(dev); + + if (flags & ETH_RECV_CHECK_DEVICE) + ethoc_recv_common(priv); + + return ethoc_rx_common(priv, packetp); +} + +static int ethoc_start(struct udevice *dev) +{ + return ethoc_init_common(dev_get_priv(dev)); +} + +static void ethoc_stop(struct udevice *dev) +{ + struct ethoc *priv = dev_get_priv(dev); + + ethoc_disable_rx_and_tx(priv); +} + +static int ethoc_probe(struct udevice *dev) +{ + struct ethoc_eth_pdata *pdata = dev_get_platdata(dev); + struct ethoc *priv = dev_get_priv(dev); + + priv->iobase = ioremap(pdata->eth_pdata.iobase, ETHOC_IOSIZE); + return 0; +} + +static int ethoc_remove(struct udevice *dev) +{ + struct ethoc *priv = dev_get_priv(dev); + + iounmap(priv->iobase); + return 0; +} + +static const struct eth_ops ethoc_ops = { + .start = ethoc_start, + .stop = ethoc_stop, + .send = ethoc_send, + .recv = ethoc_recv, + .free_pkt = ethoc_free_pkt, + .write_hwaddr = ethoc_write_hwaddr, +}; + +U_BOOT_DRIVER(ethoc) = { + .name = "ethoc", + .id = UCLASS_ETH, + .probe = ethoc_probe, + .remove = ethoc_remove, + .ops = ðoc_ops, + .priv_auto_alloc_size = sizeof(struct ethoc), + .platdata_auto_alloc_size = sizeof(struct ethoc_eth_pdata), +}; + +#endif diff --git a/include/dm/platform_data/net_ethoc.h b/include/dm/platform_data/net_ethoc.h new file mode 100644 index 0000000..1d8c73c --- /dev/null +++ b/include/dm/platform_data/net_ethoc.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 Cadence Design Systems Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _ETHOC_H +#define _ETHOC_H + +#include <net.h> + +#ifdef CONFIG_DM_ETH + +struct ethoc_eth_pdata { + struct eth_pdata eth_pdata; +}; + +#endif + +#endif /* _ETHOC_H */

On 2 August 2016 at 05:31, Max Filippov jcmvbkbc@gmail.com wrote:
Extract reusable parts from ethoc_init, ethoc_set_mac_address, ethoc_send and ethoc_receive, move the rest under #ifdef CONFIG_DM_ETH. Add U_BOOT_DRIVER, eth_ops structure and implement required methods.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com
drivers/net/ethoc.c | 221 +++++++++++++++++++++++++++-------- include/dm/platform_data/net_ethoc.h | 20 ++++ 2 files changed, 194 insertions(+), 47 deletions(-) create mode 100644 include/dm/platform_data/net_ethoc.h
Reviewed-by: Simon Glass sjg@chromium.org

On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Extract reusable parts from ethoc_init, ethoc_set_mac_address, ethoc_send and ethoc_receive, move the rest under #ifdef CONFIG_DM_ETH. Add U_BOOT_DRIVER, eth_ops structure and implement required methods.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com
A few small nits below, but otherwise,
Acked-by: Joe Hershberger joe.hershberger@ni.com
drivers/net/ethoc.c | 221 +++++++++++++++++++++++++++-------- include/dm/platform_data/net_ethoc.h | 20 ++++ 2 files changed, 194 insertions(+), 47 deletions(-) create mode 100644 include/dm/platform_data/net_ethoc.h
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index f5bd1ab..0225595 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -5,13 +5,14 @@
- Copyright (C) 2008-2009 Avionic Design GmbH
- Thierry Reding thierry.reding@avionic-design.de
- Copyright (C) 2010 Thomas Chou thomas@wytron.com.tw
- Copyright (C) 2016 Cadence Design Systems Inc.
- 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.
*/
- SPDX-License-Identifier: GPL-2.0
#include <common.h> +#include <dm/device.h> +#include <dm/platform_data/net_ethoc.h> #include <linux/io.h> #include <malloc.h> #include <net.h> @@ -216,11 +217,8 @@ static inline void ethoc_write_bd(struct ethoc *priv, int index, ethoc_write(priv, offset + 4, bd->addr); }
-static int ethoc_set_mac_address(struct eth_device *dev) +static int ethoc_set_mac_address(struct ethoc *priv, u8 *mac)
Please use ethoc_write_hwaddr_common() instead.
{
struct ethoc *priv = (struct ethoc *)dev->priv;
u8 *mac = dev->enetaddr;
ethoc_write(priv, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | (mac[5] << 0)); ethoc_write(priv, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0));
@@ -305,11 +303,8 @@ static int ethoc_reset(struct ethoc *priv) return 0; }
-static int ethoc_init(struct eth_device *dev, bd_t * bd) +static int ethoc_init_common(struct ethoc *priv) {
struct ethoc *priv = (struct ethoc *)dev->priv;
printf("ethoc\n");
priv->num_tx = 1; priv->num_rx = PKTBUFSRX; ethoc_write(priv, TX_BD_NUM, priv->num_tx);
@@ -354,36 +349,43 @@ static int ethoc_update_rx_stats(struct ethoc_bd *bd) return ret; }
-static int ethoc_rx(struct ethoc *priv, int limit) +static int ethoc_rx_common(struct ethoc *priv, uchar **packetp) {
int count;
for (count = 0; count < limit; ++count) {
u32 entry;
struct ethoc_bd bd;
u32 entry;
struct ethoc_bd bd;
entry = priv->num_tx + (priv->cur_rx % priv->num_rx);
ethoc_read_bd(priv, entry, &bd);
if (bd.stat & RX_BD_EMPTY)
break;
entry = priv->num_tx + (priv->cur_rx % priv->num_rx);
ethoc_read_bd(priv, entry, &bd);
if (bd.stat & RX_BD_EMPTY)
return -EAGAIN;
debug("%s(): RX buffer %d, %x received\n",
__func__, priv->cur_rx, bd.stat);
if (ethoc_update_rx_stats(&bd) == 0) {
int size = bd.stat >> 16;
size -= 4; /* strip the CRC */
*packetp = (void *)bd.addr;
return size;
} else {
return 0;
}
+}
debug("%s(): RX buffer %d, %x received\n",
__func__, priv->cur_rx, bd.stat);
if (ethoc_update_rx_stats(&bd) == 0) {
int size = bd.stat >> 16;
size -= 4; /* strip the CRC */
net_process_received_packet((void *)bd.addr, size);
}
+static int ethoc_recv_common(struct ethoc *priv)
Please use a better name for this, something like ethoc_is_rx_pkt_rdy().
+{
u32 pending;
/* clear the buffer descriptor so it can be reused */
flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN);
bd.stat &= ~RX_BD_STATS;
bd.stat |= RX_BD_EMPTY;
ethoc_write_bd(priv, entry, &bd);
priv->cur_rx++;
pending = ethoc_read(priv, INT_SOURCE);
ethoc_ack_irq(priv, pending);
if (pending & INT_MASK_BUSY)
debug("%s(): packet dropped\n", __func__);
if (pending & INT_MASK_RX) {
debug("%s(): rx irq\n", __func__);
return 1; }
return count;
return 0;
}
static int ethoc_update_tx_stats(struct ethoc_bd *bd) @@ -413,9 +415,8 @@ static void ethoc_tx(struct ethoc *priv) (void)ethoc_update_tx_stats(&bd); }
-static int ethoc_send(struct eth_device *dev, void *packet, int length) +static int ethoc_send_common(struct ethoc *priv, void *packet, int length) {
struct ethoc *priv = (struct ethoc *)dev->priv; struct ethoc_bd bd; u32 entry; u32 pending;
@@ -460,6 +461,47 @@ static int ethoc_send(struct eth_device *dev, void *packet, int length) return 0; }
+static int ethoc_free_pkt_common(struct ethoc *priv) +{
u32 entry;
struct ethoc_bd bd;
entry = priv->num_tx + (priv->cur_rx % priv->num_rx);
ethoc_read_bd(priv, entry, &bd);
/* clear the buffer descriptor so it can be reused */
flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN);
bd.stat &= ~RX_BD_STATS;
bd.stat |= RX_BD_EMPTY;
ethoc_write_bd(priv, entry, &bd);
priv->cur_rx++;
return 0;
+}
+#ifndef CONFIG_DM_ETH
Please use positive logic and reverse the order of these code snippets.
#ifdef CONFIG_DM_ETH ... #else ... #endif
+static int ethoc_init(struct eth_device *dev, bd_t *bd) +{
struct ethoc *priv = (struct ethoc *)dev->priv;
priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE);
return ethoc_init_common(priv);
+}
+static int ethoc_write_hwaddr(struct eth_device *dev) +{
struct ethoc *priv = (struct ethoc *)dev->priv;
u8 *mac = dev->enetaddr;
return ethoc_set_mac_address(priv, mac);
+}
+static int ethoc_send(struct eth_device *dev, void *packet, int length) +{
return ethoc_send_common(dev->priv, packet, length);
+}
static void ethoc_halt(struct eth_device *dev) { ethoc_disable_rx_and_tx(dev->priv); @@ -468,17 +510,21 @@ static void ethoc_halt(struct eth_device *dev) static int ethoc_recv(struct eth_device *dev) { struct ethoc *priv = (struct ethoc *)dev->priv;
u32 pending;
int count;
pending = ethoc_read(priv, INT_SOURCE);
ethoc_ack_irq(priv, pending);
if (pending & INT_MASK_BUSY)
debug("%s(): packet dropped\n", __func__);
if (pending & INT_MASK_RX) {
debug("%s(): rx irq\n", __func__);
ethoc_rx(priv, PKTBUFSRX);
}
if (!ethoc_recv_common(priv))
return 0;
for (count = 0; count < PKTBUFSRX; ++count) {
uchar *packetp;
int size = ethoc_rx_common(priv, &packetp);
if (size < 0)
break;
if (size > 0)
net_process_received_packet(packetp, size);
ethoc_free_pkt_common(priv);
} return 0;
}
@@ -503,10 +549,91 @@ int ethoc_initialize(u8 dev_num, int base_addr) dev->halt = ethoc_halt; dev->send = ethoc_send; dev->recv = ethoc_recv;
dev->write_hwaddr = ethoc_set_mac_address;
dev->write_hwaddr = ethoc_write_hwaddr; sprintf(dev->name, "%s-%hu", "ETHOC", dev_num); priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE); eth_register(dev); return 1;
}
+#else
+static int ethoc_write_hwaddr(struct udevice *dev) +{
struct ethoc_eth_pdata *pdata = dev_get_platdata(dev);
struct ethoc *priv = dev_get_priv(dev);
u8 *mac = pdata->eth_pdata.enetaddr;
return ethoc_set_mac_address(priv, mac);
+}
+static int ethoc_send(struct udevice *dev, void *packet, int length) +{
return ethoc_send_common(dev_get_priv(dev), packet, length);
+}
+static int ethoc_free_pkt(struct udevice *dev, uchar *packet, int length) +{
return ethoc_free_pkt_common(dev_get_priv(dev));
+}
+static int ethoc_recv(struct udevice *dev, int flags, uchar **packetp) +{
struct ethoc *priv = dev_get_priv(dev);
if (flags & ETH_RECV_CHECK_DEVICE)
ethoc_recv_common(priv);
Kinda seems like you should skip the next call (especially when this is renamed to be clearer). The code flow seems OK, though. Up to you.
return ethoc_rx_common(priv, packetp);
+}
+static int ethoc_start(struct udevice *dev) +{
return ethoc_init_common(dev_get_priv(dev));
+}
+static void ethoc_stop(struct udevice *dev) +{
struct ethoc *priv = dev_get_priv(dev);
ethoc_disable_rx_and_tx(priv);
+}
+static int ethoc_probe(struct udevice *dev) +{
struct ethoc_eth_pdata *pdata = dev_get_platdata(dev);
struct ethoc *priv = dev_get_priv(dev);
priv->iobase = ioremap(pdata->eth_pdata.iobase, ETHOC_IOSIZE);
return 0;
+}
+static int ethoc_remove(struct udevice *dev) +{
struct ethoc *priv = dev_get_priv(dev);
iounmap(priv->iobase);
return 0;
+}
+static const struct eth_ops ethoc_ops = {
.start = ethoc_start,
.stop = ethoc_stop,
.send = ethoc_send,
.recv = ethoc_recv,
.free_pkt = ethoc_free_pkt,
.write_hwaddr = ethoc_write_hwaddr,
+};
+U_BOOT_DRIVER(ethoc) = {
.name = "ethoc",
.id = UCLASS_ETH,
.probe = ethoc_probe,
.remove = ethoc_remove,
.ops = ðoc_ops,
.priv_auto_alloc_size = sizeof(struct ethoc),
.platdata_auto_alloc_size = sizeof(struct ethoc_eth_pdata),
+};
+#endif diff --git a/include/dm/platform_data/net_ethoc.h b/include/dm/platform_data/net_ethoc.h new file mode 100644 index 0000000..1d8c73c --- /dev/null +++ b/include/dm/platform_data/net_ethoc.h @@ -0,0 +1,20 @@ +/*
- Copyright (C) 2016 Cadence Design Systems Inc.
- SPDX-License-Identifier: GPL-2.0
- */
+#ifndef _ETHOC_H +#define _ETHOC_H
+#include <net.h>
+#ifdef CONFIG_DM_ETH
+struct ethoc_eth_pdata {
struct eth_pdata eth_pdata;
+};
+#endif
+#endif /* _ETHOC_H */
Thanks, -Joe

On Thu, Aug 4, 2016 at 8:51 PM, Joe Hershberger joe.hershberger@gmail.com wrote:
On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Extract reusable parts from ethoc_init, ethoc_set_mac_address, ethoc_send and ethoc_receive, move the rest under #ifdef CONFIG_DM_ETH. Add U_BOOT_DRIVER, eth_ops structure and implement required methods.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com
A few small nits below, but otherwise,
Acked-by: Joe Hershberger joe.hershberger@ni.com
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index f5bd1ab..0225595 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c
[...]
@@ -216,11 +217,8 @@ static inline void ethoc_write_bd(struct ethoc *priv, int index, ethoc_write(priv, offset + 4, bd->addr); }
-static int ethoc_set_mac_address(struct eth_device *dev) +static int ethoc_set_mac_address(struct ethoc *priv, u8 *mac)
Please use ethoc_write_hwaddr_common() instead.
Ok.
[...]
+static int ethoc_recv_common(struct ethoc *priv)
Please use a better name for this, something like ethoc_is_rx_pkt_rdy().
It's a bit more complex than that, it would only return 1 if new packets arrived since its last invocation. When it returns 0 there still may be received packets in the RX queue. I'll call it ethoc_is_new_packet_received.
[...]
+#ifndef CONFIG_DM_ETH
Please use positive logic and reverse the order of these code snippets.
#ifdef CONFIG_DM_ETH ... #else ... #endif
Ok.
[...]
+static int ethoc_recv(struct udevice *dev, int flags, uchar **packetp) +{
struct ethoc *priv = dev_get_priv(dev);
if (flags & ETH_RECV_CHECK_DEVICE)
ethoc_recv_common(priv);
Kinda seems like you should skip the next call (especially when this is renamed to be clearer). The code flow seems OK, though. Up to you.
Ok.

Add .of_match table and .ofdata_to_platdata callback to allow for ethoc device configuration from the device tree.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com --- drivers/net/ethoc.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index 0225595..8cb15c7 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -600,6 +600,14 @@ static void ethoc_stop(struct udevice *dev) ethoc_disable_rx_and_tx(priv); }
+static int ethoc_ofdata_to_platdata(struct udevice *dev) +{ + struct ethoc_eth_pdata *pdata = dev_get_platdata(dev); + + pdata->eth_pdata.iobase = dev_get_addr(dev); + return 0; +} + static int ethoc_probe(struct udevice *dev) { struct ethoc_eth_pdata *pdata = dev_get_platdata(dev); @@ -626,9 +634,16 @@ static const struct eth_ops ethoc_ops = { .write_hwaddr = ethoc_write_hwaddr, };
+static const struct udevice_id ethoc_ids[] = { + { .compatible = "opencores,ethoc" }, + { } +}; + U_BOOT_DRIVER(ethoc) = { .name = "ethoc", .id = UCLASS_ETH, + .of_match = ethoc_ids, + .ofdata_to_platdata = ethoc_ofdata_to_platdata, .probe = ethoc_probe, .remove = ethoc_remove, .ops = ðoc_ops,

On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Add .of_match table and .ofdata_to_platdata callback to allow for ethoc device configuration from the device tree.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

Addresses used in buffer descriptors and passed in platform data or device tree are physical. Addresses used by CPU to access packet data and registers are virtual. Don't mix these addresses and use virt_to_phys for translation.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com --- drivers/net/ethoc.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index 8cb15c7..e25dd1b 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -255,6 +255,7 @@ static int ethoc_init_ring(struct ethoc *priv)
/* setup transmission buffers */ bd.stat = TX_BD_IRQ | TX_BD_CRC; + bd.addr = 0;
for (i = 0; i < priv->num_tx; i++) { if (i == priv->num_tx - 1) @@ -266,11 +267,12 @@ static int ethoc_init_ring(struct ethoc *priv) bd.stat = RX_BD_EMPTY | RX_BD_IRQ;
for (i = 0; i < priv->num_rx; i++) { - bd.addr = (u32)net_rx_packets[i]; + bd.addr = virt_to_phys(net_rx_packets[i]); if (i == priv->num_rx - 1) bd.stat |= RX_BD_WRAP;
- flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); + flush_dcache_range((ulong)net_rx_packets[i], + (ulong)net_rx_packets[i] + PKTSIZE_ALIGN); ethoc_write_bd(priv, priv->num_tx + i, &bd); }
@@ -351,10 +353,10 @@ static int ethoc_update_rx_stats(struct ethoc_bd *bd)
static int ethoc_rx_common(struct ethoc *priv, uchar **packetp) { - u32 entry; struct ethoc_bd bd; + u32 i = priv->cur_rx % priv->num_rx; + u32 entry = priv->num_tx + i;
- entry = priv->num_tx + (priv->cur_rx % priv->num_rx); ethoc_read_bd(priv, entry, &bd); if (bd.stat & RX_BD_EMPTY) return -EAGAIN; @@ -365,7 +367,7 @@ static int ethoc_rx_common(struct ethoc *priv, uchar **packetp) int size = bd.stat >> 16;
size -= 4; /* strip the CRC */ - *packetp = (void *)bd.addr; + *packetp = net_rx_packets[i]; return size; } else { return 0; @@ -428,9 +430,9 @@ static int ethoc_send_common(struct ethoc *priv, void *packet, int length) bd.stat |= TX_BD_PAD; else bd.stat &= ~TX_BD_PAD; - bd.addr = (u32)packet; + bd.addr = virt_to_phys(packet);
- flush_dcache_range(bd.addr, bd.addr + length); + flush_dcache_range((ulong)packet, (ulong)packet + length); bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); bd.stat |= TX_BD_LEN(length); ethoc_write_bd(priv, entry, &bd); @@ -463,14 +465,15 @@ static int ethoc_send_common(struct ethoc *priv, void *packet, int length)
static int ethoc_free_pkt_common(struct ethoc *priv) { - u32 entry; struct ethoc_bd bd; + u32 i = priv->cur_rx % priv->num_rx; + u32 entry = priv->num_tx + i;
- entry = priv->num_tx + (priv->cur_rx % priv->num_rx); ethoc_read_bd(priv, entry, &bd);
/* clear the buffer descriptor so it can be reused */ - flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); + flush_dcache_range((ulong)net_rx_packets[i], + (ulong)net_rx_packets[i] + PKTSIZE_ALIGN); bd.stat &= ~RX_BD_STATS; bd.stat |= RX_BD_EMPTY; ethoc_write_bd(priv, entry, &bd);

On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Addresses used in buffer descriptors and passed in platform data or device tree are physical. Addresses used by CPU to access packet data and registers are virtual. Don't mix these addresses and use virt_to_phys for translation.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

The ethoc device can be configured to have a private memory region instead of having access to the main memory. In that case egress packets must be copied into that memory for transmission and pointers to that memory need to be passed to net_process_received_packet or returned from the recv callback.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com --- drivers/net/ethoc.c | 46 ++++++++++++++++++++++++++++++++---- include/dm/platform_data/net_ethoc.h | 1 + 2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index e25dd1b..fa623d5 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -179,6 +179,8 @@ struct ethoc { u32 num_rx; u32 cur_rx; void __iomem *iobase; + void __iomem *packet; + phys_addr_t packet_phys; };
/** @@ -247,6 +249,7 @@ static inline void ethoc_disable_rx_and_tx(struct ethoc *priv) static int ethoc_init_ring(struct ethoc *priv) { struct ethoc_bd bd; + phys_addr_t addr = priv->packet_phys; int i;
priv->cur_tx = 0; @@ -258,6 +261,10 @@ static int ethoc_init_ring(struct ethoc *priv) bd.addr = 0;
for (i = 0; i < priv->num_tx; i++) { + if (addr) { + bd.addr = addr; + addr += PKTSIZE_ALIGN; + } if (i == priv->num_tx - 1) bd.stat |= TX_BD_WRAP;
@@ -267,7 +274,12 @@ static int ethoc_init_ring(struct ethoc *priv) bd.stat = RX_BD_EMPTY | RX_BD_IRQ;
for (i = 0; i < priv->num_rx; i++) { - bd.addr = virt_to_phys(net_rx_packets[i]); + if (addr) { + bd.addr = addr; + addr += PKTSIZE_ALIGN; + } else { + bd.addr = virt_to_phys(net_rx_packets[i]); + } if (i == priv->num_rx - 1) bd.stat |= RX_BD_WRAP;
@@ -367,7 +379,10 @@ static int ethoc_rx_common(struct ethoc *priv, uchar **packetp) int size = bd.stat >> 16;
size -= 4; /* strip the CRC */ - *packetp = net_rx_packets[i]; + if (priv->packet) + *packetp = priv->packet + entry * PKTSIZE_ALIGN; + else + *packetp = net_rx_packets[i]; return size; } else { return 0; @@ -430,8 +445,15 @@ static int ethoc_send_common(struct ethoc *priv, void *packet, int length) bd.stat |= TX_BD_PAD; else bd.stat &= ~TX_BD_PAD; - bd.addr = virt_to_phys(packet);
+ if (priv->packet) { + void *p = priv->packet + entry * PKTSIZE_ALIGN; + + memcpy(p, packet, length); + packet = p; + } else { + bd.addr = virt_to_phys(packet); + } flush_dcache_range((ulong)packet, (ulong)packet + length); bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); bd.stat |= TX_BD_LEN(length); @@ -468,12 +490,17 @@ static int ethoc_free_pkt_common(struct ethoc *priv) struct ethoc_bd bd; u32 i = priv->cur_rx % priv->num_rx; u32 entry = priv->num_tx + i; + void *src;
ethoc_read_bd(priv, entry, &bd);
+ if (priv->packet) + src = priv->packet + entry * PKTSIZE_ALIGN; + else + src = net_rx_packets[i]; /* clear the buffer descriptor so it can be reused */ - flush_dcache_range((ulong)net_rx_packets[i], - (ulong)net_rx_packets[i] + PKTSIZE_ALIGN); + flush_dcache_range((ulong)src, + (ulong)src + PKTSIZE_ALIGN); bd.stat &= ~RX_BD_STATS; bd.stat |= RX_BD_EMPTY; ethoc_write_bd(priv, entry, &bd); @@ -606,8 +633,12 @@ static void ethoc_stop(struct udevice *dev) static int ethoc_ofdata_to_platdata(struct udevice *dev) { struct ethoc_eth_pdata *pdata = dev_get_platdata(dev); + fdt_addr_t addr;
pdata->eth_pdata.iobase = dev_get_addr(dev); + addr = dev_get_addr_index(dev, 1); + if (addr != FDT_ADDR_T_NONE) + pdata->packet_base = addr; return 0; }
@@ -617,6 +648,11 @@ static int ethoc_probe(struct udevice *dev) struct ethoc *priv = dev_get_priv(dev);
priv->iobase = ioremap(pdata->eth_pdata.iobase, ETHOC_IOSIZE); + if (pdata->packet_base) { + priv->packet_phys = pdata->packet_base; + priv->packet = ioremap(pdata->packet_base, + (1 + PKTBUFSRX) * PKTSIZE_ALIGN); + } return 0; }
diff --git a/include/dm/platform_data/net_ethoc.h b/include/dm/platform_data/net_ethoc.h index 1d8c73c..3f94bde 100644 --- a/include/dm/platform_data/net_ethoc.h +++ b/include/dm/platform_data/net_ethoc.h @@ -13,6 +13,7 @@
struct ethoc_eth_pdata { struct eth_pdata eth_pdata; + phys_addr_t packet_base; };
#endif

On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
The ethoc device can be configured to have a private memory region instead of having access to the main memory. In that case egress packets must be copied into that memory for transmission and pointers to that memory need to be passed to net_process_received_packet or returned from the recv callback.
Signed-off-by: Max Filippov jcmvbkbc@gmail.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

Implement MDIO bus read/write functions, initialize the bus and scan for the PHY when phylib is enabled. Limit PHY speeds to 10/100 Mbps.
Cc: Michal Simek monstr@monstr.eu Signed-off-by: Max Filippov jcmvbkbc@gmail.com --- drivers/net/ethoc.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index fa623d5..fe04396 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -181,6 +181,11 @@ struct ethoc { void __iomem *iobase; void __iomem *packet; phys_addr_t packet_phys; + +#ifdef CONFIG_PHYLIB + struct mii_dev *bus; + struct phy_device *phydev; +#endif };
/** @@ -319,13 +324,31 @@ static int ethoc_reset(struct ethoc *priv)
static int ethoc_init_common(struct ethoc *priv) { + int ret = 0; + priv->num_tx = 1; priv->num_rx = PKTBUFSRX; ethoc_write(priv, TX_BD_NUM, priv->num_tx); ethoc_init_ring(priv); ethoc_reset(priv);
- return 0; +#ifdef CONFIG_PHYLIB + ret = phy_startup(priv->phydev); + if (ret) { + printf("Could not initialize PHY %s\n", + priv->phydev->dev->name); + return ret; + } +#endif + return ret; +} + +static void ethoc_stop_common(struct ethoc *priv) +{ + ethoc_disable_rx_and_tx(priv); +#ifdef CONFIG_PHYLIB + phy_shutdown(priv->phydev); +#endif }
static int ethoc_update_rx_stats(struct ethoc_bd *bd) @@ -509,13 +532,119 @@ static int ethoc_free_pkt_common(struct ethoc *priv) return 0; }
+#ifdef CONFIG_PHYLIB + +static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct ethoc *priv = bus->priv; + ulong tmo = get_timer(0); + + ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); + ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ); + + while (get_timer(tmo) < CONFIG_SYS_HZ) { + u32 status = ethoc_read(priv, MIISTATUS); + + if (!(status & MIISTATUS_BUSY)) { + u32 data = ethoc_read(priv, MIIRX_DATA); + + /* reset MII command register */ + ethoc_write(priv, MIICOMMAND, 0); + return data; + } + } + return -ETIMEDOUT; +} + +static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct ethoc *priv = bus->priv; + ulong tmo = get_timer(0); + + ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); + ethoc_write(priv, MIITX_DATA, val); + ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE); + + while (get_timer(tmo) < CONFIG_SYS_HZ) { + u32 stat = ethoc_read(priv, MIISTATUS); + + if (!(stat & MIISTATUS_BUSY)) { + /* reset MII command register */ + ethoc_write(priv, MIICOMMAND, 0); + return 0; + } + } + return -ETIMEDOUT; +} + +static int ethoc_mdio_init(const char *name, struct ethoc *priv) +{ + struct mii_dev *bus = mdio_alloc(); + int ret; + + if (!bus) { + printf("Failed to allocate MDIO bus\n"); + return -ENOMEM; + } + + bus->read = ethoc_mdio_read; + bus->write = ethoc_mdio_write; + snprintf(bus->name, sizeof(bus->name), "%s", name); + bus->priv = priv; + + ret = mdio_register(bus); + if (ret < 0) + return ret; + + priv->bus = miiphy_get_dev_by_name(name); + return 0; +} + +static int ethoc_phy_init(struct ethoc *priv, void *dev) +{ + struct phy_device *phydev; + int mask = 0xffffffff; + +#ifdef CONFIG_PHY_ADDR + mask = 1 << CONFIG_PHY_ADDR; +#endif + + phydev = phy_find_by_mask(priv->bus, mask, PHY_INTERFACE_MODE_MII); + if (!phydev) + return -ENODEV; + + phy_connect_dev(phydev, dev); + + phydev->supported &= PHY_BASIC_FEATURES; + phydev->advertising = phydev->supported; + + priv->phydev = phydev; + phy_config(phydev); + + return 0; +} + +#else + +static inline int ethoc_mdio_init(const char *name, struct ethoc *priv) +{ + return 0; +} + +static inline int ethoc_phy_init(struct ethoc *priv, void *dev) +{ + return 0; +} + +#endif + #ifndef CONFIG_DM_ETH
static int ethoc_init(struct eth_device *dev, bd_t *bd) { struct ethoc *priv = (struct ethoc *)dev->priv;
- priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE); return ethoc_init_common(priv); }
@@ -534,7 +663,7 @@ static int ethoc_send(struct eth_device *dev, void *packet, int length)
static void ethoc_halt(struct eth_device *dev) { - ethoc_disable_rx_and_tx(dev->priv); + ethoc_stop_common(dev->priv); }
static int ethoc_recv(struct eth_device *dev) @@ -584,6 +713,10 @@ int ethoc_initialize(u8 dev_num, int base_addr) priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE);
eth_register(dev); + + ethoc_mdio_init(dev->name, priv); + ethoc_phy_init(priv, dev); + return 1; }
@@ -625,9 +758,7 @@ static int ethoc_start(struct udevice *dev)
static void ethoc_stop(struct udevice *dev) { - struct ethoc *priv = dev_get_priv(dev); - - ethoc_disable_rx_and_tx(priv); + ethoc_stop_common(dev_get_priv(dev)); }
static int ethoc_ofdata_to_platdata(struct udevice *dev) @@ -653,6 +784,10 @@ static int ethoc_probe(struct udevice *dev) priv->packet = ioremap(pdata->packet_base, (1 + PKTBUFSRX) * PKTSIZE_ALIGN); } + + ethoc_mdio_init(dev->name, priv); + ethoc_phy_init(priv, dev); + return 0; }
@@ -660,6 +795,11 @@ static int ethoc_remove(struct udevice *dev) { struct ethoc *priv = dev_get_priv(dev);
+#ifdef CONFIG_PHYLIB + free(priv->phydev); + mdio_unregister(priv->bus); + mdio_free(priv->bus); +#endif iounmap(priv->iobase); return 0; }

Hi Max,
On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Implement MDIO bus read/write functions, initialize the bus and scan for the PHY when phylib is enabled. Limit PHY speeds to 10/100 Mbps.
Cc: Michal Simek monstr@monstr.eu Signed-off-by: Max Filippov jcmvbkbc@gmail.com
drivers/net/ethoc.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index fa623d5..fe04396 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -181,6 +181,11 @@ struct ethoc { void __iomem *iobase; void __iomem *packet; phys_addr_t packet_phys;
+#ifdef CONFIG_PHYLIB
struct mii_dev *bus;
struct phy_device *phydev;
+#endif };
/** @@ -319,13 +324,31 @@ static int ethoc_reset(struct ethoc *priv)
static int ethoc_init_common(struct ethoc *priv) {
int ret = 0;
priv->num_tx = 1; priv->num_rx = PKTBUFSRX; ethoc_write(priv, TX_BD_NUM, priv->num_tx); ethoc_init_ring(priv); ethoc_reset(priv);
return 0;
+#ifdef CONFIG_PHYLIB
ret = phy_startup(priv->phydev);
if (ret) {
printf("Could not initialize PHY %s\n",
priv->phydev->dev->name);
return ret;
}
+#endif
return ret;
+}
+static void ethoc_stop_common(struct ethoc *priv) +{
ethoc_disable_rx_and_tx(priv);
+#ifdef CONFIG_PHYLIB
phy_shutdown(priv->phydev);
+#endif }
static int ethoc_update_rx_stats(struct ethoc_bd *bd) @@ -509,13 +532,119 @@ static int ethoc_free_pkt_common(struct ethoc *priv) return 0; }
+#ifdef CONFIG_PHYLIB
+static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{
struct ethoc *priv = bus->priv;
ulong tmo = get_timer(0);
ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg));
ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
while (get_timer(tmo) < CONFIG_SYS_HZ) {
u32 status = ethoc_read(priv, MIISTATUS);
if (!(status & MIISTATUS_BUSY)) {
It would be good to use wait_for_bit(). You could add a small helper to this file that adds the iobase to the addr and then calls wait_for_bit().
u32 data = ethoc_read(priv, MIIRX_DATA);
/* reset MII command register */
ethoc_write(priv, MIICOMMAND, 0);
return data;
}
}
return -ETIMEDOUT;
+}
+static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
+{
struct ethoc *priv = bus->priv;
ulong tmo = get_timer(0);
ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg));
ethoc_write(priv, MIITX_DATA, val);
ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
while (get_timer(tmo) < CONFIG_SYS_HZ) {
u32 stat = ethoc_read(priv, MIISTATUS);
if (!(stat & MIISTATUS_BUSY)) {
/* reset MII command register */
ethoc_write(priv, MIICOMMAND, 0);
return 0;
}
}
return -ETIMEDOUT;
+}
+static int ethoc_mdio_init(const char *name, struct ethoc *priv) +{
struct mii_dev *bus = mdio_alloc();
int ret;
if (!bus) {
printf("Failed to allocate MDIO bus\n");
return -ENOMEM;
}
bus->read = ethoc_mdio_read;
bus->write = ethoc_mdio_write;
snprintf(bus->name, sizeof(bus->name), "%s", name);
bus->priv = priv;
ret = mdio_register(bus);
if (ret < 0)
return ret;
priv->bus = miiphy_get_dev_by_name(name);
return 0;
+}
+static int ethoc_phy_init(struct ethoc *priv, void *dev) +{
struct phy_device *phydev;
int mask = 0xffffffff;
+#ifdef CONFIG_PHY_ADDR
mask = 1 << CONFIG_PHY_ADDR;
+#endif
phydev = phy_find_by_mask(priv->bus, mask, PHY_INTERFACE_MODE_MII);
if (!phydev)
return -ENODEV;
phy_connect_dev(phydev, dev);
phydev->supported &= PHY_BASIC_FEATURES;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
phy_config(phydev);
return 0;
+}
+#else
+static inline int ethoc_mdio_init(const char *name, struct ethoc *priv) +{
return 0;
+}
+static inline int ethoc_phy_init(struct ethoc *priv, void *dev) +{
return 0;
+}
+#endif
#ifndef CONFIG_DM_ETH
static int ethoc_init(struct eth_device *dev, bd_t *bd) { struct ethoc *priv = (struct ethoc *)dev->priv;
priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE);
Why? Is this an accident? At the very least it seems unrelated and should be a separate patch.
return ethoc_init_common(priv);
}
@@ -534,7 +663,7 @@ static int ethoc_send(struct eth_device *dev, void *packet, int length)
static void ethoc_halt(struct eth_device *dev) {
ethoc_disable_rx_and_tx(dev->priv);
ethoc_stop_common(dev->priv);
}
static int ethoc_recv(struct eth_device *dev) @@ -584,6 +713,10 @@ int ethoc_initialize(u8 dev_num, int base_addr) priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE);
eth_register(dev);
ethoc_mdio_init(dev->name, priv);
ethoc_phy_init(priv, dev);
return 1;
}
@@ -625,9 +758,7 @@ static int ethoc_start(struct udevice *dev)
static void ethoc_stop(struct udevice *dev) {
struct ethoc *priv = dev_get_priv(dev);
ethoc_disable_rx_and_tx(priv);
ethoc_stop_common(dev_get_priv(dev));
}
static int ethoc_ofdata_to_platdata(struct udevice *dev) @@ -653,6 +784,10 @@ static int ethoc_probe(struct udevice *dev) priv->packet = ioremap(pdata->packet_base, (1 + PKTBUFSRX) * PKTSIZE_ALIGN); }
ethoc_mdio_init(dev->name, priv);
ethoc_phy_init(priv, dev);
return 0;
}
@@ -660,6 +795,11 @@ static int ethoc_remove(struct udevice *dev) { struct ethoc *priv = dev_get_priv(dev);
+#ifdef CONFIG_PHYLIB
free(priv->phydev);
mdio_unregister(priv->bus);
mdio_free(priv->bus);
+#endif iounmap(priv->iobase); return 0; } -- 2.1.4
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On Thu, Aug 4, 2016 at 11:48 PM, Joe Hershberger joe.hershberger@gmail.com wrote:
On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Implement MDIO bus read/write functions, initialize the bus and scan for the PHY when phylib is enabled. Limit PHY speeds to 10/100 Mbps.
Cc: Michal Simek monstr@monstr.eu Signed-off-by: Max Filippov jcmvbkbc@gmail.com
[...]
+static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{
struct ethoc *priv = bus->priv;
ulong tmo = get_timer(0);
ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg));
ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
while (get_timer(tmo) < CONFIG_SYS_HZ) {
u32 status = ethoc_read(priv, MIISTATUS);
if (!(status & MIISTATUS_BUSY)) {
It would be good to use wait_for_bit(). You could add a small helper to this file that adds the iobase to the addr and then calls wait_for_bit().
Ok.
[...]
static int ethoc_init(struct eth_device *dev, bd_t *bd) { struct ethoc *priv = (struct ethoc *)dev->priv;
priv->iobase = ioremap(dev->iobase, ETHOC_IOSIZE);
Why? Is this an accident? At the very least it seems unrelated and should be a separate patch.
Oops, right. I've been moving this bit around several times and apparently haven't cleaned up all of its traces.

Hi Tom,
On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Hello,
this series does the following improvements to the OpenCores 10/100 Mbps driver:
- add Kconfig symbol for the driver;
- add DM_ETH support;
- add device tree support;
- add optional phylib support;
- add support for configurations with private packet memory;
- clean up virtual/physical address usage.
Please review.
Max Filippov (7): net/ethoc: add Kconfig entry for the driver net/ethoc: use priv instead of dev internally net/ethoc: add CONFIG_DM_ETH support net/ethoc: support device tree net/ethoc: don't mix virtual and physical addresses net/ethoc: support private memory configurations net/ethoc: implement MDIO bus and support phylib
I'm gonna put these in my queue on PW if that's OK with you.
-Joe

On Thu, Aug 04, 2016 at 12:10:33PM -0500, Joe Hershberger wrote:
Hi Tom,
On Tue, Aug 2, 2016 at 6:31 AM, Max Filippov jcmvbkbc@gmail.com wrote:
Hello,
this series does the following improvements to the OpenCores 10/100 Mbps driver:
- add Kconfig symbol for the driver;
- add DM_ETH support;
- add device tree support;
- add optional phylib support;
- add support for configurations with private packet memory;
- clean up virtual/physical address usage.
Please review.
Max Filippov (7): net/ethoc: add Kconfig entry for the driver net/ethoc: use priv instead of dev internally net/ethoc: add CONFIG_DM_ETH support net/ethoc: support device tree net/ethoc: don't mix virtual and physical addresses net/ethoc: support private memory configurations net/ethoc: implement MDIO bus and support phylib
I'm gonna put these in my queue on PW if that's OK with you.
Sounds good, thanks.
participants (4)
-
Joe Hershberger
-
Max Filippov
-
Simon Glass
-
Tom Rini