From a84a757ae7cf844a5ec3e7c32226b48667dedac2 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 5 Aug 2016 18:26:16 +0300 Subject: net/ethoc: use priv instead of dev internally 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 Acked-by: Joe Hershberger --- drivers/net/ethoc.c | 111 ++++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 55 deletions(-) (limited to 'drivers/net/ethoc.c') diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index edb3c808fa..f5bd1abb7d 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -12,11 +12,10 @@ */ #include -#include +#include #include #include #include -#include #include /* 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; -- cgit v1.2.3 From 5d43feabf3707ae4e879b54450e5a3c3c664b2b9 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 5 Aug 2016 18:26:17 +0300 Subject: net/ethoc: add CONFIG_DM_ETH support 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 Reviewed-by: Simon Glass Acked-by: Joe Hershberger --- 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 (limited to 'drivers/net/ethoc.c') diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index f5bd1abb7d..4846c58ecc 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -5,13 +5,14 @@ * Copyright (C) 2008-2009 Avionic Design GmbH * Thierry Reding * Copyright (C) 2010 Thomas Chou + * 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 +#include +#include #include #include #include @@ -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_write_hwaddr_common(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_is_new_packet_received(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,126 @@ 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; +} + +#ifdef CONFIG_DM_ETH + +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_write_hwaddr_common(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) + if (!ethoc_is_new_packet_received(priv)) + return -EAGAIN; + + 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), +}; + +#else + +static int ethoc_init(struct eth_device *dev, bd_t *bd) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + + 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_write_hwaddr_common(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 +589,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_is_new_packet_received(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 +628,12 @@ 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; } + +#endif diff --git a/include/dm/platform_data/net_ethoc.h b/include/dm/platform_data/net_ethoc.h new file mode 100644 index 0000000000..1d8c73c037 --- /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 + +#ifdef CONFIG_DM_ETH + +struct ethoc_eth_pdata { + struct eth_pdata eth_pdata; +}; + +#endif + +#endif /* _ETHOC_H */ -- cgit v1.2.3 From 2de18c8d77b026115dbe6b3e1a35446e31d3dbad Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 5 Aug 2016 18:26:18 +0300 Subject: net/ethoc: support device tree Add .of_match table and .ofdata_to_platdata callback to allow for ethoc device configuration from the device tree. Signed-off-by: Max Filippov Acked-by: Joe Hershberger --- drivers/net/ethoc.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/net/ethoc.c') diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index 4846c58ecc..a5c4b4621d 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -523,6 +523,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); @@ -549,9 +557,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, -- cgit v1.2.3 From 02a888b567df92904eb0783f4f19a3f744064159 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 5 Aug 2016 18:26:19 +0300 Subject: net/ethoc: don't mix virtual and physical addresses 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 Acked-by: Joe Hershberger --- drivers/net/ethoc.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/net/ethoc.c') diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index a5c4b4621d..198827f912 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); -- cgit v1.2.3 From 59b7dfa0d1dd54752427fc5f8b80e3f72e847298 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 5 Aug 2016 18:26:20 +0300 Subject: net/ethoc: support private memory configurations 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 Acked-by: Joe Hershberger --- drivers/net/ethoc.c | 46 ++++++++++++++++++++++++++++++++---- include/dm/platform_data/net_ethoc.h | 1 + 2 files changed, 42 insertions(+), 5 deletions(-) (limited to 'drivers/net/ethoc.c') diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index 198827f912..d825aaa887 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); @@ -529,8 +556,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; } @@ -540,6 +571,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 1d8c73c037..3f94bde7b2 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 -- cgit v1.2.3 From 0d0779c14128d561b9ce26efd414b5358197e52c Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 5 Aug 2016 18:26:21 +0300 Subject: net/ethoc: implement MDIO bus and support phylib 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 Signed-off-by: Max Filippov Acked-by: Joe Hershberger --- drivers/net/ethoc.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 6 deletions(-) (limited to 'drivers/net/ethoc.c') diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index d825aaa887..ad8c462a60 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -18,6 +18,7 @@ #include #include #include +#include /* register offsets */ #define MODER 0x00 @@ -181,6 +182,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 }; /** @@ -193,14 +199,19 @@ struct ethoc_bd { u32 addr; }; +static inline u32 *ethoc_reg(struct ethoc *priv, size_t offset) +{ + return priv->iobase + offset; +} + static inline u32 ethoc_read(struct ethoc *priv, size_t offset) { - return readl(priv->iobase + offset); + return readl(ethoc_reg(priv, offset)); } static inline void ethoc_write(struct ethoc *priv, size_t offset, u32 data) { - writel(data, priv->iobase + offset); + writel(data, ethoc_reg(priv, offset)); } static inline void ethoc_read_bd(struct ethoc *priv, int index, @@ -319,13 +330,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,6 +538,110 @@ 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; + int rc; + + ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); + ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ); + + rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + + if (rc == 0) { + u32 data = ethoc_read(priv, MIIRX_DATA); + + /* reset MII command register */ + ethoc_write(priv, MIICOMMAND, 0); + return data; + } + return rc; +} + +static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct ethoc *priv = bus->priv; + int rc; + + ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); + ethoc_write(priv, MIITX_DATA, val); + ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE); + + rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + + if (rc == 0) { + /* reset MII command register */ + ethoc_write(priv, MIICOMMAND, 0); + } + return rc; +} + +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 + #ifdef CONFIG_DM_ETH static int ethoc_write_hwaddr(struct udevice *dev) @@ -548,9 +681,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) @@ -576,6 +707,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; } @@ -583,6 +718,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; } @@ -687,6 +827,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; } -- cgit v1.2.3