// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aeroflex Gaisler GRETH 10/100/1G Ethernet MAC.
*
* 2005-2010 (c) Aeroflex Gaisler AB
*
* This driver supports GRETH 10/100 and GRETH 10/100/1G Ethernet MACs
* available in the GRLIB VHDL IP core library.
*
* Full documentation of both cores can be found here:
* https://www.gaisler.com/products/grlib/grip.pdf
*
* The Gigabit version supports scatter/gather DMA, any alignment of
* buffers and checksum offloading.
*
* Contributors: Kristoffer Glembo
* Daniel Hellstrom
* Marko Isomaki
*/
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/skbuff.h>
#include <linux/io.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include <asm/byteorder.h>
#ifdef CONFIG_SPARC
#include <asm/idprom.h>
#endif
#include "greth.h"
#define GRETH_DEF_MSG_ENABLE \
(NETIF_MSG_DRV | \
NETIF_MSG_PROBE | \
NETIF_MSG_LINK | \
NETIF_MSG_IFDOWN | \
NETIF_MSG_IFUP | \
NETIF_MSG_RX_ERR | \
NETIF_MSG_TX_ERR)
static int greth_debug = -1; /* -1 == use GRETH_DEF_MSG_ENABLE as value */
module_param(greth_debug, int, 0);
MODULE_PARM_DESC(greth_debug, "GRETH bitmapped debugging message enable value");
/* Accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */
static int macaddr[6];
module_param_array(macaddr, int, NULL, 0);
MODULE_PARM_DESC(macaddr, "GRETH Ethernet MAC address");
static int greth_edcl = 1;
module_param(greth_edcl, int, 0);
MODULE_PARM_DESC(greth_edcl, "GRETH EDCL usage indicator. Set to 1 if EDCL is used.");
static int greth_open(struct net_device *dev);
static netdev_tx_t greth_start_xmit(struct sk_buff *skb,
struct net_device *dev);
static netdev_tx_t greth_start_xmit_gbit(struct sk_buff *skb,
struct net_device *dev);
static int greth_rx(struct net_device *dev, int limit);
static int greth_rx_gbit(struct net_device *dev, int limit);
static void greth_clean_tx(struct net_device *dev);
static void greth_clean_tx_gbit(struct net_device *dev);
static irqreturn_t greth_interrupt(int irq, void *dev_id);
static int greth_close(struct net_device *dev);
static int greth_set_mac_add(struct net_device *dev, void *p);
static void greth_set_multicast_list(struct net_device *dev);
#define GRETH_REGLOAD(a) (be32_to_cpu(__raw_readl(&(a))))
#define GRETH_REGSAVE(a, v) (__raw_writel(cpu_to_be32(v), &(a)))
#define GRETH_REGORIN(a, v) (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) | (v))))
#define GRETH_REGANDIN(a, v) (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) & (v))))
#define NEXT_TX(N) (((N) + 1) & GRETH_TXBD_NUM_MASK)
#define SKIP_TX(N, C) (((N) + C) & GRETH_TXBD_NUM_MASK)
#define NEXT_RX(N) (((N) + 1) & GRETH_RXBD_NUM_MASK)
static void greth_print_rx_packet(void *addr, int len)
{
print_hex_dump(KERN_DEBUG, "RX: ", DUMP_PREFIX_OFFSET, 16, 1,
addr, len, true);
}
static void greth_print_tx_packet(struct sk_buff *skb)
{
int i;
int length;
if (skb_shinfo(skb)->nr_frags == 0)
length = skb->len;
else
length = skb_headlen(skb);
print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, length, true);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
skb_frag_address(&skb_shinfo(skb)->frags[i]),
skb_frag_size(&skb_shinfo(skb)->frags[i]), true);
}
}
static inline void greth_enable_tx(struct greth_private *greth)
{
wmb();
GRETH_REGORIN(greth->regs->control, GRETH_TXEN);
}
static inline void greth_enable_tx_and_irq(struct greth_private *greth)
{
wmb(); /* BDs must been written to memory before enabling TX */
GRETH_REGORIN(greth->regs->control, GRETH_TXEN | GRETH_TXI);
}
static inline void greth_disable_tx(struct greth_private *greth)
{
GRETH_REGANDIN(greth->regs->control, ~GRETH_TXEN);
}
static inline void greth_enable_rx(struct greth_private *greth)
{
wmb();
GRETH_REGORIN(greth->regs->control, GRETH_RXEN);
}
static inline void greth_disable_rx(struct greth_private *greth)
{
GRETH_REGANDIN(greth->regs->control, ~GRETH_RXEN);
}
static inline void greth_enable_irqs(struct greth_private *greth)
{
GRETH_REGORIN(greth->regs->control, GRETH_RXI | GRETH_TXI);
}
static inline void greth_disable_irqs(struct greth_private *greth)
{
GRETH_REGANDIN(greth->regs->control, ~(GRETH_RXI|GRETH_TXI));
}
static inline void greth_write_bd(u32 *bd, u32 val)
{
__raw_writel(cpu_to_be32(val), bd);
}
static inline u32 greth_read_bd(u32 *bd)
{
return be32_to_cpu(__raw_readl(bd));
}
static void greth_clean_rings(struct greth_private *greth)
{
int i;
struct greth_bd *rx_bdp = greth->rx_bd_base;
struct greth_bd *tx_bdp = greth->tx_bd_base;
if (greth->gbit_mac) {
/* Free and unmap RX buffers */
for (i = 0; i < GRETH_RXBD_NUM; i++, rx_bdp++) {
if (greth->rx_skbuff[i] != NULL) {
dev_kfree_skb(greth->rx_skbuff[i]);
dma_unmap_single(greth->dev,
greth_read_bd(&rx_bdp->addr),
MAX_FRAME_SIZE+NET_IP_ALIGN,
DMA_FROM_DEVICE);
}
}
/* TX buffers */
while (greth->tx_free < GRETH_TXBD_NUM) {
struct sk_buff
|