// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/net/ether1.c
*
* Copyright (C) 1996-2000 Russell King
*
* Acorn ether1 driver (82586 chip) for Acorn machines
*
* We basically keep two queues in the cards memory - one for transmit
* and one for receive. Each has a head and a tail. The head is where
* we/the chip adds packets to be transmitted/received, and the tail
* is where the transmitter has got to/where the receiver will stop.
* Both of these queues are circular, and since the chip is running
* all the time, we have to be careful when we modify the pointers etc
* so that the buffer memory contents is valid all the time.
*
* Change log:
* 1.00 RMK Released
* 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now.
* 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready
* 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt.
* Should prevent lockup.
* 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong.
* TDR now only reports failure when chip reports non-zero
* TDR time-distance.
* 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1
* 1.06 RMK 10/02/2000 Updated for 2.3.43
* 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ecard.h>
#define __ETHER1_C
#include "ether1.h"
static unsigned int net_debug = NET_DEBUG;
#define BUFFER_SIZE 0x10000
#define TX_AREA_START 0x00100
#define TX_AREA_END 0x05000
#define RX_AREA_START 0x05000
#define RX_AREA_END 0x0fc00
static int ether1_open(struct net_device *dev);
static netdev_tx_t ether1_sendpacket(struct sk_buff *skb,
struct net_device *dev);
static irqreturn_t ether1_interrupt(int irq, void *dev_id);
static int ether1_close(struct net_device *dev);
static void ether1_setmulticastlist(struct net_device *dev);
static void ether1_timeout(struct net_device *dev);
/* ------------------------------------------------------------------------- */
static char version[] = "ether1 ethernet driver (c) 2000 Russell King v1.07\n";
#define BUS_16 16
#define BUS_8 8
/* ------------------------------------------------------------------------- */
#define DISABLEIRQS 1
#define NORMALIRQS 0
#define ether1_readw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs)
#define ether1_writew(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs)
static inline unsigned short
ether1_inw_p (struct net_device *dev, int addr, int svflgs)
{
unsigned long flags;
unsigned short ret;
if (svflgs)
local_irq_save (flags);
writeb(addr >> 12, REG_PAGE);
ret = readw(ETHER1_RAM + ((addr & 4095) << 1));
if (svflgs)
local_irq_restore (flags);
return ret;
}
static inline void
ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs)
{
unsigned long flags;
if (svflgs)
local_irq_save (flags);
writeb(addr >> 12, REG_PAGE);
writew(val, ETHER1_RAM + ((addr & 4095) << 1));
if (svflgs)
local_irq_restore (flags);
}
/*
* Some inline assembler to allow fast transfers on to/off of the card.
* Since this driver depends on some features presented by the ARM
* specific architecture, and that you can't configure this driver
* without specifiing ARM mode, this is not a problem.
*
* This routine is essentially an optimised memcpy from the card's
* onboard RAM to kernel memory.
*/
static void
ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length)
{
unsigned int page, thislen, offset;
void __iomem *addr;
offset = start & 4095;
page = start >> 12;
addr = ETHER1_RAM + (offset << 1);
if (offset + length > 4096)
thislen = 4096 - offset;
else
thislen = length;
do {
int used;
writeb(page, REG_PAGE);
length -= thislen;
__asm__ __volatile__(
"subs %3, %3, #2\n\
bmi 2f\n\
1: ldr %0, [%1], #2\n\
mov %0, %0, lsl #16\n\
orr %0, %0, %0, lsr #16\n\
str %0, [%2], #4\n\
subs %3, %3, #2\n\
bmi 2f\n\
ldr %0, [%1], #2\n\
mov %0, %0, lsl #16\n\
orr %0, %0, %0, lsr #16\n\
str %0, [%2], #4\n\
subs %3, %3, #2\n\
bmi 2f\n\
ldr %0, [%1], #2\n\