162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright 1997 Alain Malek
662306a36Sopenharmony_ci *                    (Alain.Malek@cryogen.com)
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * ----------------------------------------------------------------------------
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This program is based on
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * ne.c:       A general non-shared-memory NS8390 ethernet driver for linux
1362306a36Sopenharmony_ci *             Written 1992-94 by Donald Becker.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * 8390.c:     A general NS8390 ethernet driver core for linux.
1662306a36Sopenharmony_ci *             Written 1992-94 by Donald Becker.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * cnetdevice: A Sana-II ethernet driver for AmigaOS
1962306a36Sopenharmony_ci *             Written by Bruce Abbott (bhabbott@inhb.co.nz)
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * ----------------------------------------------------------------------------
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/kernel.h>
2862306a36Sopenharmony_ci#include <linux/errno.h>
2962306a36Sopenharmony_ci#include <linux/pci.h>
3062306a36Sopenharmony_ci#include <linux/init.h>
3162306a36Sopenharmony_ci#include <linux/delay.h>
3262306a36Sopenharmony_ci#include <linux/netdevice.h>
3362306a36Sopenharmony_ci#include <linux/etherdevice.h>
3462306a36Sopenharmony_ci#include <linux/interrupt.h>
3562306a36Sopenharmony_ci#include <linux/jiffies.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <asm/io.h>
3862306a36Sopenharmony_ci#include <asm/setup.h>
3962306a36Sopenharmony_ci#include <asm/amigaints.h>
4062306a36Sopenharmony_ci#include <asm/amigahw.h>
4162306a36Sopenharmony_ci#include <asm/amigayle.h>
4262306a36Sopenharmony_ci#include <asm/amipcmcia.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include "8390.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* ---- No user-serviceable parts below ---- */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define DRV_NAME "apne"
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define NE_BASE	 (dev->base_addr)
5162306a36Sopenharmony_ci#define NE_CMD	 		0x00
5262306a36Sopenharmony_ci#define NE_DATAPORT		0x10            /* NatSemi-defined port window offset. */
5362306a36Sopenharmony_ci#define NE_RESET		0x1f            /* Issue a read to reset, a write to clear. */
5462306a36Sopenharmony_ci#define NE_IO_EXTENT	        0x20
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define NE_EN0_ISR		0x07
5762306a36Sopenharmony_ci#define NE_EN0_DCFG		0x0e
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define NE_EN0_RSARLO	        0x08
6062306a36Sopenharmony_ci#define NE_EN0_RSARHI	        0x09
6162306a36Sopenharmony_ci#define NE_EN0_RCNTLO	        0x0a
6262306a36Sopenharmony_ci#define NE_EN0_RXCR		0x0c
6362306a36Sopenharmony_ci#define NE_EN0_TXCR		0x0d
6462306a36Sopenharmony_ci#define NE_EN0_RCNTHI	        0x0b
6562306a36Sopenharmony_ci#define NE_EN0_IMR		0x0f
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define NE1SM_START_PG	0x20	/* First page of TX buffer */
6862306a36Sopenharmony_ci#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */
6962306a36Sopenharmony_ci#define NESM_START_PG	0x40	/* First page of TX buffer */
7062306a36Sopenharmony_ci#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int apne_probe1(struct net_device *dev, int ioaddr);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void apne_reset_8390(struct net_device *dev);
7662306a36Sopenharmony_cistatic void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
7762306a36Sopenharmony_ci			  int ring_page);
7862306a36Sopenharmony_cistatic void apne_block_input(struct net_device *dev, int count,
7962306a36Sopenharmony_ci								struct sk_buff *skb, int ring_offset);
8062306a36Sopenharmony_cistatic void apne_block_output(struct net_device *dev, const int count,
8162306a36Sopenharmony_ci							const unsigned char *buf, const int start_page);
8262306a36Sopenharmony_cistatic irqreturn_t apne_interrupt(int irq, void *dev_id);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int init_pcmcia(void);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* IO base address used for nic */
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define IOBASE 0x300
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci   use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand
9262306a36Sopenharmony_ci   you can find the values to use by looking at the cnet.device
9362306a36Sopenharmony_ci   config file example (the default values are for the CNET40BC card)
9462306a36Sopenharmony_ci*/
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/*
9762306a36Sopenharmony_ci#define MANUAL_CONFIG 0x20
9862306a36Sopenharmony_ci#define MANUAL_OFFSET 0x3f8
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define MANUAL_HWADDR0 0x00
10162306a36Sopenharmony_ci#define MANUAL_HWADDR1 0x12
10262306a36Sopenharmony_ci#define MANUAL_HWADDR2 0x34
10362306a36Sopenharmony_ci#define MANUAL_HWADDR3 0x56
10462306a36Sopenharmony_ci#define MANUAL_HWADDR4 0x78
10562306a36Sopenharmony_ci#define MANUAL_HWADDR5 0x9a
10662306a36Sopenharmony_ci*/
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic const char version[] =
10962306a36Sopenharmony_ci    "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int apne_owned;	/* signal if card already owned */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic u32 apne_msg_enable;
11462306a36Sopenharmony_cimodule_param_named(msg_enable, apne_msg_enable, uint, 0444);
11562306a36Sopenharmony_ciMODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct net_device * __init apne_probe(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct net_device *dev;
12062306a36Sopenharmony_ci	struct ei_device *ei_local;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#ifndef MANUAL_CONFIG
12362306a36Sopenharmony_ci	char tuple[8];
12462306a36Sopenharmony_ci#endif
12562306a36Sopenharmony_ci	int err;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (!MACH_IS_AMIGA)
12862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (apne_owned)
13162306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if ( !(AMIGAHW_PRESENT(PCMCIA)) )
13462306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	pr_info("Looking for PCMCIA ethernet card : ");
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* check if a card is inserted */
13962306a36Sopenharmony_ci	if (!(PCMCIA_INSERTED)) {
14062306a36Sopenharmony_ci		pr_cont("NO PCMCIA card inserted\n");
14162306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	dev = alloc_ei_netdev();
14562306a36Sopenharmony_ci	if (!dev)
14662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14762306a36Sopenharmony_ci	ei_local = netdev_priv(dev);
14862306a36Sopenharmony_ci	ei_local->msg_enable = apne_msg_enable;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* disable pcmcia irq for readtuple */
15162306a36Sopenharmony_ci	pcmcia_disable_irq();
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci#ifndef MANUAL_CONFIG
15462306a36Sopenharmony_ci	if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) ||
15562306a36Sopenharmony_ci		(tuple[2] != CISTPL_FUNCID_NETWORK)) {
15662306a36Sopenharmony_ci		pr_cont("not an ethernet card\n");
15762306a36Sopenharmony_ci		/* XXX: shouldn't we re-enable irq here? */
15862306a36Sopenharmony_ci		free_netdev(dev);
15962306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci#endif
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	pr_cont("ethernet PCMCIA card inserted\n");
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!init_pcmcia()) {
16662306a36Sopenharmony_ci		/* XXX: shouldn't we re-enable irq here? */
16762306a36Sopenharmony_ci		free_netdev(dev);
16862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!request_region(IOBASE, 0x20, DRV_NAME)) {
17262306a36Sopenharmony_ci		free_netdev(dev);
17362306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	err = apne_probe1(dev, IOBASE);
17762306a36Sopenharmony_ci	if (err) {
17862306a36Sopenharmony_ci		release_region(IOBASE, 0x20);
17962306a36Sopenharmony_ci		free_netdev(dev);
18062306a36Sopenharmony_ci		return ERR_PTR(err);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	err = register_netdev(dev);
18362306a36Sopenharmony_ci	if (!err)
18462306a36Sopenharmony_ci		return dev;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	pcmcia_disable_irq();
18762306a36Sopenharmony_ci	free_irq(IRQ_AMIGA_PORTS, dev);
18862306a36Sopenharmony_ci	pcmcia_reset();
18962306a36Sopenharmony_ci	release_region(IOBASE, 0x20);
19062306a36Sopenharmony_ci	free_netdev(dev);
19162306a36Sopenharmony_ci	return ERR_PTR(err);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int __init apne_probe1(struct net_device *dev, int ioaddr)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci    int i;
19762306a36Sopenharmony_ci    unsigned char SA_prom[32];
19862306a36Sopenharmony_ci    int wordlength = 2;
19962306a36Sopenharmony_ci    const char *name = NULL;
20062306a36Sopenharmony_ci    int start_page, stop_page;
20162306a36Sopenharmony_ci#ifndef MANUAL_HWADDR0
20262306a36Sopenharmony_ci    int neX000, ctron;
20362306a36Sopenharmony_ci#endif
20462306a36Sopenharmony_ci    static unsigned version_printed;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci    if ((apne_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
20762306a36Sopenharmony_ci		netdev_info(dev, version);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci    netdev_info(dev, "PCMCIA NE*000 ethercard probe");
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci    /* Reset card. Who knows what dain-bramaged state it was left in. */
21262306a36Sopenharmony_ci    {	unsigned long reset_start_time = jiffies;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	while ((inb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
21762306a36Sopenharmony_ci		if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
21862306a36Sopenharmony_ci			pr_cont(" not found (no reset ack).\n");
21962306a36Sopenharmony_ci			return -ENODEV;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	outb(0xff, ioaddr + NE_EN0_ISR);		/* Ack all intr. */
22362306a36Sopenharmony_ci    }
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci#ifndef MANUAL_HWADDR0
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci    /* Read the 16 bytes of station address PROM.
22862306a36Sopenharmony_ci       We must first initialize registers, similar to NS8390_init(eifdev, 0).
22962306a36Sopenharmony_ci       We can't reliably read the SAPROM address without this.
23062306a36Sopenharmony_ci       (I learned the hard way!). */
23162306a36Sopenharmony_ci    {
23262306a36Sopenharmony_ci	struct {unsigned long value, offset; } program_seq[] = {
23362306a36Sopenharmony_ci	    {E8390_NODMA+E8390_PAGE0+E8390_STOP, NE_CMD}, /* Select page 0*/
23462306a36Sopenharmony_ci	    {0x48,	NE_EN0_DCFG},	/* Set byte-wide (0x48) access. */
23562306a36Sopenharmony_ci	    {0x00,	NE_EN0_RCNTLO},	/* Clear the count regs. */
23662306a36Sopenharmony_ci	    {0x00,	NE_EN0_RCNTHI},
23762306a36Sopenharmony_ci	    {0x00,	NE_EN0_IMR},	/* Mask completion irq. */
23862306a36Sopenharmony_ci	    {0xFF,	NE_EN0_ISR},
23962306a36Sopenharmony_ci	    {E8390_RXOFF, NE_EN0_RXCR},	/* 0x20  Set to monitor */
24062306a36Sopenharmony_ci	    {E8390_TXOFF, NE_EN0_TXCR},	/* 0x02  and loopback mode. */
24162306a36Sopenharmony_ci	    {32,	NE_EN0_RCNTLO},
24262306a36Sopenharmony_ci	    {0x00,	NE_EN0_RCNTHI},
24362306a36Sopenharmony_ci	    {0x00,	NE_EN0_RSARLO},	/* DMA starting at 0x0000. */
24462306a36Sopenharmony_ci	    {0x00,	NE_EN0_RSARHI},
24562306a36Sopenharmony_ci	    {E8390_RREAD+E8390_START, NE_CMD},
24662306a36Sopenharmony_ci	};
24762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(program_seq); i++) {
24862306a36Sopenharmony_ci	    outb(program_seq[i].value, ioaddr + program_seq[i].offset);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci    }
25262306a36Sopenharmony_ci    for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
25362306a36Sopenharmony_ci	SA_prom[i] = inb(ioaddr + NE_DATAPORT);
25462306a36Sopenharmony_ci	SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
25562306a36Sopenharmony_ci	if (SA_prom[i] != SA_prom[i+1])
25662306a36Sopenharmony_ci	    wordlength = 1;
25762306a36Sopenharmony_ci    }
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci    /*	At this point, wordlength *only* tells us if the SA_prom is doubled
26062306a36Sopenharmony_ci	up or not because some broken PCI cards don't respect the byte-wide
26162306a36Sopenharmony_ci	request in program_seq above, and hence don't have doubled up values.
26262306a36Sopenharmony_ci	These broken cards would otherwise be detected as an ne1000.  */
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci    if (wordlength == 2)
26562306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
26662306a36Sopenharmony_ci		SA_prom[i] = SA_prom[i+i];
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci    if (wordlength == 2) {
26962306a36Sopenharmony_ci	/* We must set the 8390 for word mode. */
27062306a36Sopenharmony_ci	outb(0x49, ioaddr + NE_EN0_DCFG);
27162306a36Sopenharmony_ci	start_page = NESM_START_PG;
27262306a36Sopenharmony_ci	stop_page = NESM_STOP_PG;
27362306a36Sopenharmony_ci    } else {
27462306a36Sopenharmony_ci	start_page = NE1SM_START_PG;
27562306a36Sopenharmony_ci	stop_page = NE1SM_STOP_PG;
27662306a36Sopenharmony_ci    }
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci    neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57);
27962306a36Sopenharmony_ci    ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci    /* Set up the rest of the parameters. */
28262306a36Sopenharmony_ci    if (neX000) {
28362306a36Sopenharmony_ci	name = (wordlength == 2) ? "NE2000" : "NE1000";
28462306a36Sopenharmony_ci    } else if (ctron) {
28562306a36Sopenharmony_ci	name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
28662306a36Sopenharmony_ci	start_page = 0x01;
28762306a36Sopenharmony_ci	stop_page = (wordlength == 2) ? 0x40 : 0x20;
28862306a36Sopenharmony_ci    } else {
28962306a36Sopenharmony_ci	pr_cont(" not found.\n");
29062306a36Sopenharmony_ci	return -ENXIO;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci    }
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci#else
29562306a36Sopenharmony_ci    wordlength = 2;
29662306a36Sopenharmony_ci    /* We must set the 8390 for word mode. */
29762306a36Sopenharmony_ci    outb(0x49, ioaddr + NE_EN0_DCFG);
29862306a36Sopenharmony_ci    start_page = NESM_START_PG;
29962306a36Sopenharmony_ci    stop_page = NESM_STOP_PG;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci    SA_prom[0] = MANUAL_HWADDR0;
30262306a36Sopenharmony_ci    SA_prom[1] = MANUAL_HWADDR1;
30362306a36Sopenharmony_ci    SA_prom[2] = MANUAL_HWADDR2;
30462306a36Sopenharmony_ci    SA_prom[3] = MANUAL_HWADDR3;
30562306a36Sopenharmony_ci    SA_prom[4] = MANUAL_HWADDR4;
30662306a36Sopenharmony_ci    SA_prom[5] = MANUAL_HWADDR5;
30762306a36Sopenharmony_ci    name = "NE2000";
30862306a36Sopenharmony_ci#endif
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci    dev->base_addr = ioaddr;
31162306a36Sopenharmony_ci    dev->irq = IRQ_AMIGA_PORTS;
31262306a36Sopenharmony_ci    dev->netdev_ops = &ei_netdev_ops;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci    /* Install the Interrupt handler */
31562306a36Sopenharmony_ci    i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev);
31662306a36Sopenharmony_ci    if (i) return i;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci    eth_hw_addr_set(dev, SA_prom);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci    pr_cont(" %pM\n", dev->dev_addr);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci    netdev_info(dev, "%s found.\n", name);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci    ei_status.name = name;
32562306a36Sopenharmony_ci    ei_status.tx_start_page = start_page;
32662306a36Sopenharmony_ci    ei_status.stop_page = stop_page;
32762306a36Sopenharmony_ci    ei_status.word16 = (wordlength == 2);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci    ei_status.rx_start_page = start_page + TX_PAGES;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci    ei_status.reset_8390 = &apne_reset_8390;
33262306a36Sopenharmony_ci    ei_status.block_input = &apne_block_input;
33362306a36Sopenharmony_ci    ei_status.block_output = &apne_block_output;
33462306a36Sopenharmony_ci    ei_status.get_8390_hdr = &apne_get_8390_hdr;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci    NS8390_init(dev, 0);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci    pcmcia_ack_int(pcmcia_get_intreq());		/* ack PCMCIA int req */
33962306a36Sopenharmony_ci    pcmcia_enable_irq();
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci    apne_owned = 1;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci    return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/* Hard reset the card.  This used to pause for the same period that a
34762306a36Sopenharmony_ci   8390 reset command required, but that shouldn't be necessary. */
34862306a36Sopenharmony_cistatic void
34962306a36Sopenharmony_ciapne_reset_8390(struct net_device *dev)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci    unsigned long reset_start_time = jiffies;
35262306a36Sopenharmony_ci    struct ei_device *ei_local = netdev_priv(dev);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci    init_pcmcia();
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci    netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci    outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci    ei_status.txing = 0;
36162306a36Sopenharmony_ci    ei_status.dmaing = 0;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci    /* This check _should_not_ be necessary, omit eventually. */
36462306a36Sopenharmony_ci    while ((inb(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0)
36562306a36Sopenharmony_ci	if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
36662306a36Sopenharmony_ci		netdev_err(dev, "ne_reset_8390() did not complete.\n");
36762306a36Sopenharmony_ci		break;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci    outb(ENISR_RESET, NE_BASE + NE_EN0_ISR);	/* Ack intr. */
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/* Grab the 8390 specific header. Similar to the block_input routine, but
37362306a36Sopenharmony_ci   we don't need to be concerned with ring wrap as the header will be at
37462306a36Sopenharmony_ci   the start of a page, so we optimize accordingly. */
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic void
37762306a36Sopenharmony_ciapne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci    int nic_base = dev->base_addr;
38162306a36Sopenharmony_ci    int cnt;
38262306a36Sopenharmony_ci    char *ptrc;
38362306a36Sopenharmony_ci    short *ptrs;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
38662306a36Sopenharmony_ci    if (ei_status.dmaing) {
38762306a36Sopenharmony_ci	netdev_err(dev, "DMAing conflict in ne_get_8390_hdr "
38862306a36Sopenharmony_ci		   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
38962306a36Sopenharmony_ci		   ei_status.dmaing, ei_status.irqlock, dev->irq);
39062306a36Sopenharmony_ci	return;
39162306a36Sopenharmony_ci    }
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci    ei_status.dmaing |= 0x01;
39462306a36Sopenharmony_ci    outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
39562306a36Sopenharmony_ci    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
39662306a36Sopenharmony_ci    outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
39762306a36Sopenharmony_ci    outb(0, nic_base + NE_EN0_RCNTHI);
39862306a36Sopenharmony_ci    outb(0, nic_base + NE_EN0_RSARLO);		/* On page boundary */
39962306a36Sopenharmony_ci    outb(ring_page, nic_base + NE_EN0_RSARHI);
40062306a36Sopenharmony_ci    outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci    if (ei_status.word16) {
40362306a36Sopenharmony_ci        ptrs = (short*)hdr;
40462306a36Sopenharmony_ci        for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++)
40562306a36Sopenharmony_ci            *ptrs++ = inw(NE_BASE + NE_DATAPORT);
40662306a36Sopenharmony_ci    } else {
40762306a36Sopenharmony_ci        ptrc = (char*)hdr;
40862306a36Sopenharmony_ci        for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
40962306a36Sopenharmony_ci            *ptrc++ = inb(NE_BASE + NE_DATAPORT);
41062306a36Sopenharmony_ci    }
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci    outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
41362306a36Sopenharmony_ci    ei_status.dmaing &= ~0x01;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci    le16_to_cpus(&hdr->count);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/* Block input and output, similar to the Crynwr packet driver.  If you
41962306a36Sopenharmony_ci   are porting to a new ethercard, look at the packet driver source for hints.
42062306a36Sopenharmony_ci   The NEx000 doesn't share the on-board packet memory -- you have to put
42162306a36Sopenharmony_ci   the packet out through the "remote DMA" dataport using outb. */
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic void
42462306a36Sopenharmony_ciapne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci    int nic_base = dev->base_addr;
42762306a36Sopenharmony_ci    char *buf = skb->data;
42862306a36Sopenharmony_ci    char *ptrc;
42962306a36Sopenharmony_ci    short *ptrs;
43062306a36Sopenharmony_ci    int cnt;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
43362306a36Sopenharmony_ci    if (ei_status.dmaing) {
43462306a36Sopenharmony_ci		netdev_err(dev, "DMAing conflict in ne_block_input "
43562306a36Sopenharmony_ci			   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
43662306a36Sopenharmony_ci			   ei_status.dmaing, ei_status.irqlock, dev->irq);
43762306a36Sopenharmony_ci	return;
43862306a36Sopenharmony_ci    }
43962306a36Sopenharmony_ci    ei_status.dmaing |= 0x01;
44062306a36Sopenharmony_ci    outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
44162306a36Sopenharmony_ci    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
44262306a36Sopenharmony_ci    outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
44362306a36Sopenharmony_ci    outb(count >> 8, nic_base + NE_EN0_RCNTHI);
44462306a36Sopenharmony_ci    outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
44562306a36Sopenharmony_ci    outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
44662306a36Sopenharmony_ci    outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
44762306a36Sopenharmony_ci    if (ei_status.word16) {
44862306a36Sopenharmony_ci      ptrs = (short*)buf;
44962306a36Sopenharmony_ci      for (cnt = 0; cnt < (count>>1); cnt++)
45062306a36Sopenharmony_ci        *ptrs++ = inw(NE_BASE + NE_DATAPORT);
45162306a36Sopenharmony_ci      if (count & 0x01) {
45262306a36Sopenharmony_ci	buf[count-1] = inb(NE_BASE + NE_DATAPORT);
45362306a36Sopenharmony_ci      }
45462306a36Sopenharmony_ci    } else {
45562306a36Sopenharmony_ci      ptrc = buf;
45662306a36Sopenharmony_ci      for (cnt = 0; cnt < count; cnt++)
45762306a36Sopenharmony_ci        *ptrc++ = inb(NE_BASE + NE_DATAPORT);
45862306a36Sopenharmony_ci    }
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci    outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
46162306a36Sopenharmony_ci    ei_status.dmaing &= ~0x01;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic void
46562306a36Sopenharmony_ciapne_block_output(struct net_device *dev, int count,
46662306a36Sopenharmony_ci		const unsigned char *buf, const int start_page)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci    int nic_base = NE_BASE;
46962306a36Sopenharmony_ci    unsigned long dma_start;
47062306a36Sopenharmony_ci    char *ptrc;
47162306a36Sopenharmony_ci    short *ptrs;
47262306a36Sopenharmony_ci    int cnt;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci    /* Round the count up for word writes.  Do we need to do this?
47562306a36Sopenharmony_ci       What effect will an odd byte count have on the 8390?
47662306a36Sopenharmony_ci       I should check someday. */
47762306a36Sopenharmony_ci    if (ei_status.word16 && (count & 0x01))
47862306a36Sopenharmony_ci      count++;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
48162306a36Sopenharmony_ci    if (ei_status.dmaing) {
48262306a36Sopenharmony_ci		netdev_err(dev, "DMAing conflict in ne_block_output."
48362306a36Sopenharmony_ci			   "[DMAstat:%d][irqlock:%d][intr:%d]\n",
48462306a36Sopenharmony_ci			   ei_status.dmaing, ei_status.irqlock, dev->irq);
48562306a36Sopenharmony_ci	return;
48662306a36Sopenharmony_ci    }
48762306a36Sopenharmony_ci    ei_status.dmaing |= 0x01;
48862306a36Sopenharmony_ci    /* We should already be in page 0, but to be safe... */
48962306a36Sopenharmony_ci    outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci   /* Now the normal output. */
49462306a36Sopenharmony_ci    outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
49562306a36Sopenharmony_ci    outb(count >> 8,   nic_base + NE_EN0_RCNTHI);
49662306a36Sopenharmony_ci    outb(0x00, nic_base + NE_EN0_RSARLO);
49762306a36Sopenharmony_ci    outb(start_page, nic_base + NE_EN0_RSARHI);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci    outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
50062306a36Sopenharmony_ci    if (ei_status.word16) {
50162306a36Sopenharmony_ci        ptrs = (short*)buf;
50262306a36Sopenharmony_ci        for (cnt = 0; cnt < count>>1; cnt++)
50362306a36Sopenharmony_ci            outw(*ptrs++, NE_BASE+NE_DATAPORT);
50462306a36Sopenharmony_ci    } else {
50562306a36Sopenharmony_ci        ptrc = (char*)buf;
50662306a36Sopenharmony_ci        for (cnt = 0; cnt < count; cnt++)
50762306a36Sopenharmony_ci	    outb(*ptrc++, NE_BASE + NE_DATAPORT);
50862306a36Sopenharmony_ci    }
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci    dma_start = jiffies;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci    while ((inb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
51362306a36Sopenharmony_ci	if (time_after(jiffies, dma_start + 2*HZ/100)) {	/* 20ms */
51462306a36Sopenharmony_ci		netdev_warn(dev, "timeout waiting for Tx RDC.\n");
51562306a36Sopenharmony_ci		apne_reset_8390(dev);
51662306a36Sopenharmony_ci		NS8390_init(dev,1);
51762306a36Sopenharmony_ci		break;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci    outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
52162306a36Sopenharmony_ci    ei_status.dmaing &= ~0x01;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic irqreturn_t apne_interrupt(int irq, void *dev_id)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci    unsigned char pcmcia_intreq;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci    if (!(gayle.inten & GAYLE_IRQ_IRQ))
52962306a36Sopenharmony_ci        return IRQ_NONE;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci    pcmcia_intreq = pcmcia_get_intreq();
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci    if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) {
53462306a36Sopenharmony_ci        pcmcia_ack_int(pcmcia_intreq);
53562306a36Sopenharmony_ci        return IRQ_NONE;
53662306a36Sopenharmony_ci    }
53762306a36Sopenharmony_ci    if (apne_msg_enable & NETIF_MSG_INTR)
53862306a36Sopenharmony_ci	pr_debug("pcmcia intreq = %x\n", pcmcia_intreq);
53962306a36Sopenharmony_ci    pcmcia_disable_irq();			/* to get rid of the sti() within ei_interrupt */
54062306a36Sopenharmony_ci    ei_interrupt(irq, dev_id);
54162306a36Sopenharmony_ci    pcmcia_ack_int(pcmcia_get_intreq());
54262306a36Sopenharmony_ci    pcmcia_enable_irq();
54362306a36Sopenharmony_ci    return IRQ_HANDLED;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic struct net_device *apne_dev;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int __init apne_module_init(void)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	apne_dev = apne_probe();
55162306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(apne_dev);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic void __exit apne_module_exit(void)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	unregister_netdev(apne_dev);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	pcmcia_disable_irq();
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	free_irq(IRQ_AMIGA_PORTS, apne_dev);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	pcmcia_reset();
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	release_region(IOBASE, 0x20);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	free_netdev(apne_dev);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_cimodule_init(apne_module_init);
56962306a36Sopenharmony_cimodule_exit(apne_module_exit);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic int init_pcmcia(void)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	u_char config;
57462306a36Sopenharmony_ci#ifndef MANUAL_CONFIG
57562306a36Sopenharmony_ci	u_char tuple[32];
57662306a36Sopenharmony_ci	int offset_len;
57762306a36Sopenharmony_ci#endif
57862306a36Sopenharmony_ci	u_long offset;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	pcmcia_reset();
58162306a36Sopenharmony_ci	pcmcia_program_voltage(PCMCIA_0V);
58262306a36Sopenharmony_ci	pcmcia_access_speed(PCMCIA_SPEED_250NS);
58362306a36Sopenharmony_ci	pcmcia_write_enable();
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci#ifdef MANUAL_CONFIG
58662306a36Sopenharmony_ci	config = MANUAL_CONFIG;
58762306a36Sopenharmony_ci#else
58862306a36Sopenharmony_ci	/* get and write config byte to enable IO port */
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3)
59162306a36Sopenharmony_ci		return 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	config = tuple[2] & 0x3f;
59462306a36Sopenharmony_ci#endif
59562306a36Sopenharmony_ci#ifdef MANUAL_OFFSET
59662306a36Sopenharmony_ci	offset = MANUAL_OFFSET;
59762306a36Sopenharmony_ci#else
59862306a36Sopenharmony_ci	if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6)
59962306a36Sopenharmony_ci		return 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	offset_len = (tuple[2] & 0x3) + 1;
60262306a36Sopenharmony_ci	offset = 0;
60362306a36Sopenharmony_ci	while(offset_len--) {
60462306a36Sopenharmony_ci		offset = (offset << 8) | tuple[4+offset_len];
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci#endif
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	out_8(GAYLE_ATTRIBUTE+offset, config);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return 1;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
614