X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fnet%2Frtl8169.c;h=cea6701203815e05f37d46040b3fb76d18bed5bf;hb=bd4f706aa872fd5793f150fc98a8131e9b4e911d;hp=e45d1a52212d5739c096dcc1b105d5816666f128;hpb=d65e34d12514de2bbe3b8f519761d641c081bad0;p=u-boot diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index e45d1a5221..cea6701203 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -11,19 +11,7 @@ * r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit * Written 2003 by Timothy Legge * -* 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. + * SPDX-License-Identifier: GPL-2.0+ * * Portions of this code based on: * r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver @@ -53,6 +41,7 @@ * Modified to use le32_to_cpu and cpu_to_le32 properly */ #include +#include #include #include #include @@ -91,7 +80,11 @@ static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ #define NUM_TX_DESC 1 /* Number of Tx descriptor registers */ -#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#ifdef CONFIG_SYS_RX_ETH_BUFFER + #define NUM_RX_DESC CONFIG_SYS_RX_ETH_BUFFER +#else + #define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#endif #define RX_BUF_SIZE 1536 /* Rx Buffer size */ #define RX_BUF_LEN 8192 @@ -258,6 +251,9 @@ static struct { {"RTL-8169sc/8110sc", 0x18, 0xff7e1880,}, {"RTL-8168b/8111sb", 0x30, 0xff7e1880,}, {"RTL-8168b/8111sb", 0x38, 0xff7e1880,}, + {"RTL-8168d/8111d", 0x28, 0xff7e1880,}, + {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880,}, + {"RTL-8168/8111g", 0x4c, 0xff7e1880,}, {"RTL-8101e", 0x34, 0xff7e1880,}, {"RTL-8100e", 0x32, 0xff7e1880,}, }; @@ -283,23 +279,40 @@ struct RxDesc { u32 buf_Haddr; }; -/* Define the TX Descriptor */ -static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; -/* __attribute__ ((aligned(256))); */ +#define RTL8169_DESC_SIZE 16 -/* Create a static buffer of size RX_BUF_SZ for each -TX Descriptor. All descriptors point to a -part of this buffer */ -static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; +#if ARCH_DMA_MINALIGN > 256 +# define RTL8169_ALIGN ARCH_DMA_MINALIGN +#else +# define RTL8169_ALIGN 256 +#endif -/* Define the RX Descriptor */ -static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256]; - /* __attribute__ ((aligned(256))); */ +/* + * Warn if the cache-line size is larger than the descriptor size. In such + * cases the driver will likely fail because the CPU needs to flush the cache + * when requeuing RX buffers, therefore descriptors written by the hardware + * may be discarded. + * + * This can be fixed by defining CONFIG_SYS_NONCACHED_MEMORY which will cause + * the driver to allocate descriptors from a pool of non-cached memory. + */ +#if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN +#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && !defined(CONFIG_SYS_DCACHE_OFF) +#warning cache-line size is larger than descriptor size +#endif +#endif -/* Create a static buffer of size RX_BUF_SZ for each -RX Descriptor All descriptors point to a -part of this buffer */ -static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; +/* + * Create a static buffer of size RX_BUF_SZ for each TX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, txb, NUM_TX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); + +/* + * Create a static buffer of size RX_BUF_SZ for each RX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, rxb, NUM_RX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); struct rtl8169_private { void *mmio_addr; /* memory map physical address */ @@ -307,8 +320,6 @@ struct rtl8169_private { unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ unsigned long dirty_tx; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ unsigned char *RxBufferRings; /* Index of Rx Buffer */ @@ -326,6 +337,7 @@ static const unsigned int rtl8169_rx_config = static struct pci_device_id supported[] = { {PCI_VENDOR_ID_REALTEK, 0x8167}, + {PCI_VENDOR_ID_REALTEK, 0x8168}, {PCI_VENDOR_ID_REALTEK, 0x8169}, {} }; @@ -406,6 +418,87 @@ match: return 0; } +/* + * TX and RX descriptors are 16 bytes. This causes problems with the cache + * maintenance on CPUs where the cache-line size exceeds the size of these + * descriptors. What will happen is that when the driver receives a packet + * it will be immediately requeued for the hardware to reuse. The CPU will + * therefore need to flush the cache-line containing the descriptor, which + * will cause all other descriptors in the same cache-line to be flushed + * along with it. If one of those descriptors had been written to by the + * device those changes (and the associated packet) will be lost. + * + * To work around this, we make use of non-cached memory if available. If + * descriptors are mapped uncached there's no need to manually flush them + * or invalidate them. + * + * Note that this only applies to descriptors. The packet data buffers do + * not have the same constraints since they are 1536 bytes large, so they + * are unlikely to share cache-lines. + */ +static void *rtl_alloc_descs(unsigned int num) +{ + size_t size = num * RTL8169_DESC_SIZE; + +#ifdef CONFIG_SYS_NONCACHED_MEMORY + return (void *)noncached_alloc(size, RTL8169_ALIGN); +#else + return memalign(RTL8169_ALIGN, size); +#endif +} + +/* + * Cache maintenance functions. These are simple wrappers around the more + * general purpose flush_cache() and invalidate_dcache_range() functions. + */ + +static void rtl_inval_rx_desc(struct RxDesc *desc) +{ +#ifndef CONFIG_SYS_NONCACHED_MEMORY + unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +#endif +} + +static void rtl_flush_rx_desc(struct RxDesc *desc) +{ +#ifndef CONFIG_SYS_NONCACHED_MEMORY + flush_cache((unsigned long)desc, sizeof(*desc)); +#endif +} + +static void rtl_inval_tx_desc(struct TxDesc *desc) +{ +#ifndef CONFIG_SYS_NONCACHED_MEMORY + unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +#endif +} + +static void rtl_flush_tx_desc(struct TxDesc *desc) +{ +#ifndef CONFIG_SYS_NONCACHED_MEMORY + flush_cache((unsigned long)desc, sizeof(*desc)); +#endif +} + +static void rtl_inval_buffer(void *buf, size_t size) +{ + unsigned long start = (unsigned long)buf & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + size, ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +} + +static void rtl_flush_buffer(void *buf, size_t size) +{ + flush_cache((unsigned long)buf, size); +} + /************************************************************************** RECV - Receive a frame ***************************************************************************/ @@ -423,16 +516,17 @@ static int rtl_recv(struct eth_device *dev) ioaddr = dev->iobase; cur_rx = tpc->cur_rx; - flush_cache((unsigned long)&tpc->RxDescArray[cur_rx], - sizeof(struct RxDesc)); + + rtl_inval_rx_desc(&tpc->RxDescArray[cur_rx]); + if ((le32_to_cpu(tpc->RxDescArray[cur_rx].status) & OWNbit) == 0) { if (!(le32_to_cpu(tpc->RxDescArray[cur_rx].status) & RxRES)) { unsigned char rxdata[RX_BUF_LEN]; length = (int) (le32_to_cpu(tpc->RxDescArray[cur_rx]. status) & 0x00001FFF) - 4; + rtl_inval_buffer(tpc->RxBufferRing[cur_rx], length); memcpy(rxdata, tpc->RxBufferRing[cur_rx], length); - NetReceive(rxdata, length); if (cur_rx == NUM_RX_DESC - 1) tpc->RxDescArray[cur_rx].status = @@ -442,8 +536,9 @@ static int rtl_recv(struct eth_device *dev) cpu_to_le32(OWNbit + RX_BUF_SIZE); tpc->RxDescArray[cur_rx].buf_addr = cpu_to_le32(bus_to_phys(tpc->RxBufferRing[cur_rx])); - flush_cache((unsigned long)tpc->RxBufferRing[cur_rx], - RX_BUF_SIZE); + rtl_flush_rx_desc(&tpc->RxDescArray[cur_rx]); + + NetReceive(rxdata, length); } else { puts("Error Rx"); } @@ -464,7 +559,7 @@ static int rtl_recv(struct eth_device *dev) /************************************************************************** SEND - Transmit a frame ***************************************************************************/ -static int rtl_send(struct eth_device *dev, volatile void *packet, int length) +static int rtl_send(struct eth_device *dev, void *packet, int length) { /* send the packet to destination */ @@ -485,7 +580,7 @@ static int rtl_send(struct eth_device *dev, volatile void *packet, int length) /* point to the current txb incase multiple tx_rings are used */ ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE]; memcpy(ptxb, (char *)packet, (int)length); - flush_cache((unsigned long)ptxb, length); + rtl_flush_buffer(ptxb, length); while (len < ETH_ZLEN) ptxb[len++] = '\0'; @@ -501,20 +596,20 @@ static int rtl_send(struct eth_device *dev, volatile void *packet, int length) cpu_to_le32((OWNbit | EORbit | FSbit | LSbit) | ((len > ETH_ZLEN) ? len : ETH_ZLEN)); } + rtl_flush_tx_desc(&tpc->TxDescArray[entry]); RTL_W8(TxPoll, 0x40); /* set polling bit */ tpc->cur_tx++; to = currticks() + TX_TIMEOUT; do { - flush_cache((unsigned long)&tpc->TxDescArray[entry], - sizeof(struct TxDesc)); + rtl_inval_tx_desc(&tpc->TxDescArray[entry]); } while ((le32_to_cpu(tpc->TxDescArray[entry].status) & OWNbit) && (currticks() < to)); /* wait */ if (currticks() >= to) { #ifdef DEBUG_RTL8169_TX - puts ("tx timeout/error\n"); - printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); + puts("tx timeout/error\n"); + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); #endif ret = 0; } else { @@ -616,7 +711,7 @@ static void rtl8169_hw_start(struct eth_device *dev) RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); #ifdef DEBUG_RTL8169 - printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); #endif } @@ -650,11 +745,11 @@ static void rtl8169_init_ring(struct eth_device *dev) tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE]; tpc->RxDescArray[i].buf_addr = cpu_to_le32(bus_to_phys(tpc->RxBufferRing[i])); - flush_cache((unsigned long)tpc->RxBufferRing[i], RX_BUF_SIZE); + rtl_flush_rx_desc(&tpc->RxDescArray[i]); } #ifdef DEBUG_RTL8169 - printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); #endif } @@ -670,16 +765,6 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis) printf ("%s\n", __FUNCTION__); #endif - tpc->TxDescArrays = tx_ring; - /* Tx Desscriptor needs 256 bytes alignment; */ - tpc->TxDescArray = (struct TxDesc *) ((unsigned long)(tpc->TxDescArrays + - 255) & ~255); - - tpc->RxDescArrays = rx_ring; - /* Rx Desscriptor needs 256 bytes alignment; */ - tpc->RxDescArray = (struct RxDesc *) ((unsigned long)(tpc->RxDescArrays + - 255) & ~255); - rtl8169_init_ring(dev); rtl8169_hw_start(dev); /* Construct a perfect filter frame with the mac address as first match @@ -695,7 +780,7 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis) txb[5] = dev->enetaddr[5]; #ifdef DEBUG_RTL8169 - printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); #endif return 0; } @@ -721,10 +806,6 @@ static void rtl_halt(struct eth_device *dev) RTL_W32(RxMissed, 0); - tpc->TxDescArrays = NULL; - tpc->RxDescArrays = NULL; - tpc->TxDescArray = NULL; - tpc->RxDescArray = NULL; for (i = 0; i < NUM_RX_DESC; i++) { tpc->RxBufferRing[i] = NULL; } @@ -739,7 +820,6 @@ INIT - Look for an adapter, this routine's visible to the outside static int rtl_init(struct eth_device *dev, bd_t *bis) { static int board_idx = -1; - static int printed_version = 0; int i, rc; int option = -1, Cap10_100 = 0, Cap1000 = 0; @@ -751,8 +831,6 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) board_idx++; - printed_version = 1; - /* point to private storage */ tpc = &tpx; @@ -872,7 +950,16 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) #endif } - return 1; + + tpc->RxDescArray = rtl_alloc_descs(NUM_RX_DESC); + if (!tpc->RxDescArray) + return -ENOMEM; + + tpc->TxDescArray = rtl_alloc_descs(NUM_TX_DESC); + if (!tpc->TxDescArray) + return -ENOMEM; + + return 0; } int rtl8169_initialize(bd_t *bis) @@ -884,17 +971,37 @@ int rtl8169_initialize(bd_t *bis) int idx=0; while(1){ + unsigned int region; + u16 device; + int err; + /* Find RTL8169 */ if ((devno = pci_find_devices(supported, idx++)) < 0) break; - pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + pci_read_config_word(devno, PCI_DEVICE_ID, &device); + switch (device) { + case 0x8168: + region = 2; + break; + + default: + region = 1; + break; + } + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_0 + (region * 4), &iobase); iobase &= ~0xf; debug ("rtl8169: REALTEK RTL8169 @0x%x\n", iobase); dev = (struct eth_device *)malloc(sizeof *dev); + if (!dev) { + printf("Can not allocate memory of rtl8169\n"); + break; + } + memset(dev, 0, sizeof(*dev)); sprintf (dev->name, "RTL8169#%d", card_number); dev->priv = (void *) devno; @@ -905,9 +1012,14 @@ int rtl8169_initialize(bd_t *bis) dev->send = rtl_send; dev->recv = rtl_recv; - eth_register (dev); + err = rtl_init(dev, bis); + if (err < 0) { + printf(pr_fmt("failed to initialize card: %d\n"), err); + free(dev); + continue; + } - rtl_init(dev, bis); + eth_register (dev); card_number++; }