18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Linux ARCnet driver - COM90xx chipset (memory-mapped buffers)
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Written 1994-1999 by Avery Pennarun.
58c2ecf20Sopenharmony_ci * Written 1999 by Martin Mares <mj@ucw.cz>.
68c2ecf20Sopenharmony_ci * Derived from skeleton.c by Donald Becker.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
98c2ecf20Sopenharmony_ci *  for sponsoring the further development of this driver.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * **********************
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * The original copyright of skeleton.c was as follows:
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * skeleton.c Written 1993 by Donald Becker.
168c2ecf20Sopenharmony_ci * Copyright 1993 United States Government as represented by the
178c2ecf20Sopenharmony_ci * Director, National Security Agency.  This software may only be used
188c2ecf20Sopenharmony_ci * and distributed according to the terms of the GNU General Public License as
198c2ecf20Sopenharmony_ci * modified by SRC, incorporated herein by reference.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * **********************
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * For more details, see drivers/net/arcnet.c
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * **********************
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <linux/module.h>
318c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
328c2ecf20Sopenharmony_ci#include <linux/init.h>
338c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
348c2ecf20Sopenharmony_ci#include <linux/ioport.h>
358c2ecf20Sopenharmony_ci#include <linux/delay.h>
368c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
378c2ecf20Sopenharmony_ci#include <linux/slab.h>
388c2ecf20Sopenharmony_ci#include <linux/io.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "arcdevice.h"
418c2ecf20Sopenharmony_ci#include "com9026.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Define this to speed up the autoprobe by assuming if only one io port and
448c2ecf20Sopenharmony_ci * shmem are left in the list at Stage 5, they must correspond to each
458c2ecf20Sopenharmony_ci * other.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * This is undefined by default because it might not always be true, and the
488c2ecf20Sopenharmony_ci * extra check makes the autoprobe even more careful.  Speed demons can turn
498c2ecf20Sopenharmony_ci * it on - I think it should be fine if you only have one ARCnet card
508c2ecf20Sopenharmony_ci * installed.
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * If no ARCnet cards are installed, this delay never happens anyway and thus
538c2ecf20Sopenharmony_ci * the option has no effect.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_ci#undef FAST_PROBE
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* Internal function declarations */
588c2ecf20Sopenharmony_cistatic int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *);
598c2ecf20Sopenharmony_cistatic void com90xx_command(struct net_device *dev, int command);
608c2ecf20Sopenharmony_cistatic int com90xx_status(struct net_device *dev);
618c2ecf20Sopenharmony_cistatic void com90xx_setmask(struct net_device *dev, int mask);
628c2ecf20Sopenharmony_cistatic int com90xx_reset(struct net_device *dev, int really_reset);
638c2ecf20Sopenharmony_cistatic void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
648c2ecf20Sopenharmony_ci				 void *buf, int count);
658c2ecf20Sopenharmony_cistatic void com90xx_copy_from_card(struct net_device *dev, int bufnum,
668c2ecf20Sopenharmony_ci				   int offset, void *buf, int count);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Known ARCnet cards */
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic struct net_device *cards[16];
718c2ecf20Sopenharmony_cistatic int numcards;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* Handy defines for ARCnet specific stuff */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* The number of low I/O ports used by the card */
768c2ecf20Sopenharmony_ci#define ARCNET_TOTAL_SIZE	16
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* Amount of I/O memory used by the card */
798c2ecf20Sopenharmony_ci#define BUFFER_SIZE (512)
808c2ecf20Sopenharmony_ci#define MIRROR_SIZE (BUFFER_SIZE * 4)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int com90xx_skip_probe __initdata = 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* Module parameters */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int io;			/* use the insmod io= irq= shmem= options */
878c2ecf20Sopenharmony_cistatic int irq;
888c2ecf20Sopenharmony_cistatic int shmem;
898c2ecf20Sopenharmony_cistatic char device[9];		/* use eg. device=arc1 to change name */
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cimodule_param_hw(io, int, ioport, 0);
928c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0);
938c2ecf20Sopenharmony_cimodule_param(shmem, int, 0);
948c2ecf20Sopenharmony_cimodule_param_string(device, device, sizeof(device), 0);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void __init com90xx_probe(void)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	int count, status, ioaddr, numprint, airq, openparen = 0;
998c2ecf20Sopenharmony_ci	unsigned long airqmask;
1008c2ecf20Sopenharmony_ci	int ports[(0x3f0 - 0x200) / 16 + 1] = {	0 };
1018c2ecf20Sopenharmony_ci	unsigned long *shmems;
1028c2ecf20Sopenharmony_ci	void __iomem **iomem;
1038c2ecf20Sopenharmony_ci	int numports, numshmems, *port;
1048c2ecf20Sopenharmony_ci	u_long *p;
1058c2ecf20Sopenharmony_ci	int index;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
1088c2ecf20Sopenharmony_ci		return;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	shmems = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(unsigned long),
1118c2ecf20Sopenharmony_ci			 GFP_KERNEL);
1128c2ecf20Sopenharmony_ci	if (!shmems)
1138c2ecf20Sopenharmony_ci		return;
1148c2ecf20Sopenharmony_ci	iomem = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(void __iomem *),
1158c2ecf20Sopenharmony_ci			GFP_KERNEL);
1168c2ecf20Sopenharmony_ci	if (!iomem) {
1178c2ecf20Sopenharmony_ci		kfree(shmems);
1188c2ecf20Sopenharmony_ci		return;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (BUGLVL(D_NORMAL))
1228c2ecf20Sopenharmony_ci		pr_info("%s\n", "COM90xx chipset support");
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* set up the arrays where we'll store the possible probe addresses */
1258c2ecf20Sopenharmony_ci	numports = numshmems = 0;
1268c2ecf20Sopenharmony_ci	if (io)
1278c2ecf20Sopenharmony_ci		ports[numports++] = io;
1288c2ecf20Sopenharmony_ci	else
1298c2ecf20Sopenharmony_ci		for (count = 0x200; count <= 0x3f0; count += 16)
1308c2ecf20Sopenharmony_ci			ports[numports++] = count;
1318c2ecf20Sopenharmony_ci	if (shmem)
1328c2ecf20Sopenharmony_ci		shmems[numshmems++] = shmem;
1338c2ecf20Sopenharmony_ci	else
1348c2ecf20Sopenharmony_ci		for (count = 0xA0000; count <= 0xFF800; count += 2048)
1358c2ecf20Sopenharmony_ci			shmems[numshmems++] = count;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* Stage 1: abandon any reserved ports, or ones with status==0xFF
1388c2ecf20Sopenharmony_ci	 * (empty), and reset any others by reading the reset port.
1398c2ecf20Sopenharmony_ci	 */
1408c2ecf20Sopenharmony_ci	numprint = -1;
1418c2ecf20Sopenharmony_ci	for (port = &ports[0]; port - ports < numports; port++) {
1428c2ecf20Sopenharmony_ci		numprint++;
1438c2ecf20Sopenharmony_ci		numprint %= 8;
1448c2ecf20Sopenharmony_ci		if (!numprint) {
1458c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "\n");
1468c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "S1: ");
1478c2ecf20Sopenharmony_ci		}
1488c2ecf20Sopenharmony_ci		arc_cont(D_INIT, "%Xh ", *port);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		ioaddr = *port;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		if (!request_region(*port, ARCNET_TOTAL_SIZE,
1538c2ecf20Sopenharmony_ci				    "arcnet (90xx)")) {
1548c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "(request_region)\n");
1558c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "S1: ");
1568c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS))
1578c2ecf20Sopenharmony_ci				numprint = 0;
1588c2ecf20Sopenharmony_ci			*port-- = ports[--numports];
1598c2ecf20Sopenharmony_ci			continue;
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci		if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
1628c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "(empty)\n");
1638c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "S1: ");
1648c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS))
1658c2ecf20Sopenharmony_ci				numprint = 0;
1668c2ecf20Sopenharmony_ci			release_region(*port, ARCNET_TOTAL_SIZE);
1678c2ecf20Sopenharmony_ci			*port-- = ports[--numports];
1688c2ecf20Sopenharmony_ci			continue;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci		/* begin resetting card */
1718c2ecf20Sopenharmony_ci		arcnet_inb(ioaddr, COM9026_REG_R_RESET);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		arc_cont(D_INIT_REASONS, "\n");
1748c2ecf20Sopenharmony_ci		arc_cont(D_INIT_REASONS, "S1: ");
1758c2ecf20Sopenharmony_ci		if (BUGLVL(D_INIT_REASONS))
1768c2ecf20Sopenharmony_ci			numprint = 0;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	arc_cont(D_INIT, "\n");
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (!numports) {
1818c2ecf20Sopenharmony_ci		arc_cont(D_NORMAL, "S1: No ARCnet cards found.\n");
1828c2ecf20Sopenharmony_ci		kfree(shmems);
1838c2ecf20Sopenharmony_ci		kfree(iomem);
1848c2ecf20Sopenharmony_ci		return;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	/* Stage 2: we have now reset any possible ARCnet cards, so we can't
1878c2ecf20Sopenharmony_ci	 * do anything until they finish.  If D_INIT, print the list of
1888c2ecf20Sopenharmony_ci	 * cards that are left.
1898c2ecf20Sopenharmony_ci	 */
1908c2ecf20Sopenharmony_ci	numprint = -1;
1918c2ecf20Sopenharmony_ci	for (port = &ports[0]; port < ports + numports; port++) {
1928c2ecf20Sopenharmony_ci		numprint++;
1938c2ecf20Sopenharmony_ci		numprint %= 8;
1948c2ecf20Sopenharmony_ci		if (!numprint) {
1958c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "\n");
1968c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "S2: ");
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci		arc_cont(D_INIT, "%Xh ", *port);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	arc_cont(D_INIT, "\n");
2018c2ecf20Sopenharmony_ci	mdelay(RESETtime);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* Stage 3: abandon any shmem addresses that don't have the signature
2048c2ecf20Sopenharmony_ci	 * 0xD1 byte in the right place, or are read-only.
2058c2ecf20Sopenharmony_ci	 */
2068c2ecf20Sopenharmony_ci	numprint = -1;
2078c2ecf20Sopenharmony_ci	for (index = 0, p = &shmems[0]; index < numshmems; p++, index++) {
2088c2ecf20Sopenharmony_ci		void __iomem *base;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		numprint++;
2118c2ecf20Sopenharmony_ci		numprint %= 8;
2128c2ecf20Sopenharmony_ci		if (!numprint) {
2138c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "\n");
2148c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "S3: ");
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci		arc_cont(D_INIT, "%lXh ", *p);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) {
2198c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "(request_mem_region)\n");
2208c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "Stage 3: ");
2218c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS))
2228c2ecf20Sopenharmony_ci				numprint = 0;
2238c2ecf20Sopenharmony_ci			goto out;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci		base = ioremap(*p, MIRROR_SIZE);
2268c2ecf20Sopenharmony_ci		if (!base) {
2278c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "(ioremap)\n");
2288c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "Stage 3: ");
2298c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS))
2308c2ecf20Sopenharmony_ci				numprint = 0;
2318c2ecf20Sopenharmony_ci			goto out1;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci		if (arcnet_readb(base, COM9026_REG_R_STATUS) != TESTvalue) {
2348c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
2358c2ecf20Sopenharmony_ci				 arcnet_readb(base, COM9026_REG_R_STATUS),
2368c2ecf20Sopenharmony_ci				 TESTvalue);
2378c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "S3: ");
2388c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS))
2398c2ecf20Sopenharmony_ci				numprint = 0;
2408c2ecf20Sopenharmony_ci			goto out2;
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci		/* By writing 0x42 to the TESTvalue location, we also make
2438c2ecf20Sopenharmony_ci		 * sure no "mirror" shmem areas show up - if they occur
2448c2ecf20Sopenharmony_ci		 * in another pass through this loop, they will be discarded
2458c2ecf20Sopenharmony_ci		 * because *cptr != TESTvalue.
2468c2ecf20Sopenharmony_ci		 */
2478c2ecf20Sopenharmony_ci		arcnet_writeb(0x42, base, COM9026_REG_W_INTMASK);
2488c2ecf20Sopenharmony_ci		if (arcnet_readb(base, COM9026_REG_R_STATUS) != 0x42) {
2498c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "(read only)\n");
2508c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "S3: ");
2518c2ecf20Sopenharmony_ci			goto out2;
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci		arc_cont(D_INIT_REASONS, "\n");
2548c2ecf20Sopenharmony_ci		arc_cont(D_INIT_REASONS, "S3: ");
2558c2ecf20Sopenharmony_ci		if (BUGLVL(D_INIT_REASONS))
2568c2ecf20Sopenharmony_ci			numprint = 0;
2578c2ecf20Sopenharmony_ci		iomem[index] = base;
2588c2ecf20Sopenharmony_ci		continue;
2598c2ecf20Sopenharmony_ci	out2:
2608c2ecf20Sopenharmony_ci		iounmap(base);
2618c2ecf20Sopenharmony_ci	out1:
2628c2ecf20Sopenharmony_ci		release_mem_region(*p, MIRROR_SIZE);
2638c2ecf20Sopenharmony_ci	out:
2648c2ecf20Sopenharmony_ci		*p-- = shmems[--numshmems];
2658c2ecf20Sopenharmony_ci		index--;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	arc_cont(D_INIT, "\n");
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (!numshmems) {
2708c2ecf20Sopenharmony_ci		arc_cont(D_NORMAL, "S3: No ARCnet cards found.\n");
2718c2ecf20Sopenharmony_ci		for (port = &ports[0]; port < ports + numports; port++)
2728c2ecf20Sopenharmony_ci			release_region(*port, ARCNET_TOTAL_SIZE);
2738c2ecf20Sopenharmony_ci		kfree(shmems);
2748c2ecf20Sopenharmony_ci		kfree(iomem);
2758c2ecf20Sopenharmony_ci		return;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci	/* Stage 4: something of a dummy, to report the shmems that are
2788c2ecf20Sopenharmony_ci	 * still possible after stage 3.
2798c2ecf20Sopenharmony_ci	 */
2808c2ecf20Sopenharmony_ci	numprint = -1;
2818c2ecf20Sopenharmony_ci	for (p = &shmems[0]; p < shmems + numshmems; p++) {
2828c2ecf20Sopenharmony_ci		numprint++;
2838c2ecf20Sopenharmony_ci		numprint %= 8;
2848c2ecf20Sopenharmony_ci		if (!numprint) {
2858c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "\n");
2868c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "S4: ");
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci		arc_cont(D_INIT, "%lXh ", *p);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	arc_cont(D_INIT, "\n");
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* Stage 5: for any ports that have the correct status, can disable
2938c2ecf20Sopenharmony_ci	 * the RESET flag, and (if no irq is given) generate an autoirq,
2948c2ecf20Sopenharmony_ci	 * register an ARCnet device.
2958c2ecf20Sopenharmony_ci	 *
2968c2ecf20Sopenharmony_ci	 * Currently, we can only register one device per probe, so quit
2978c2ecf20Sopenharmony_ci	 * after the first one is found.
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	numprint = -1;
3008c2ecf20Sopenharmony_ci	for (port = &ports[0]; port < ports + numports; port++) {
3018c2ecf20Sopenharmony_ci		int found = 0;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		numprint++;
3048c2ecf20Sopenharmony_ci		numprint %= 8;
3058c2ecf20Sopenharmony_ci		if (!numprint) {
3068c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "\n");
3078c2ecf20Sopenharmony_ci			arc_cont(D_INIT, "S5: ");
3088c2ecf20Sopenharmony_ci		}
3098c2ecf20Sopenharmony_ci		arc_cont(D_INIT, "%Xh ", *port);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		ioaddr = *port;
3128c2ecf20Sopenharmony_ci		status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		if ((status & 0x9D)
3158c2ecf20Sopenharmony_ci		    != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
3168c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "(status=%Xh)\n", status);
3178c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "S5: ");
3188c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS))
3198c2ecf20Sopenharmony_ci				numprint = 0;
3208c2ecf20Sopenharmony_ci			release_region(*port, ARCNET_TOTAL_SIZE);
3218c2ecf20Sopenharmony_ci			*port-- = ports[--numports];
3228c2ecf20Sopenharmony_ci			continue;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci		arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
3258c2ecf20Sopenharmony_ci			    ioaddr, COM9026_REG_W_COMMAND);
3268c2ecf20Sopenharmony_ci		status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
3278c2ecf20Sopenharmony_ci		if (status & RESETflag) {
3288c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
3298c2ecf20Sopenharmony_ci				 status);
3308c2ecf20Sopenharmony_ci			arc_cont(D_INIT_REASONS, "S5: ");
3318c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS))
3328c2ecf20Sopenharmony_ci				numprint = 0;
3338c2ecf20Sopenharmony_ci			release_region(*port, ARCNET_TOTAL_SIZE);
3348c2ecf20Sopenharmony_ci			*port-- = ports[--numports];
3358c2ecf20Sopenharmony_ci			continue;
3368c2ecf20Sopenharmony_ci		}
3378c2ecf20Sopenharmony_ci		/* skip this completely if an IRQ was given, because maybe
3388c2ecf20Sopenharmony_ci		 * we're on a machine that locks during autoirq!
3398c2ecf20Sopenharmony_ci		 */
3408c2ecf20Sopenharmony_ci		if (!irq) {
3418c2ecf20Sopenharmony_ci			/* if we do this, we're sure to get an IRQ since the
3428c2ecf20Sopenharmony_ci			 * card has just reset and the NORXflag is on until
3438c2ecf20Sopenharmony_ci			 * we tell it to start receiving.
3448c2ecf20Sopenharmony_ci			 */
3458c2ecf20Sopenharmony_ci			airqmask = probe_irq_on();
3468c2ecf20Sopenharmony_ci			arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
3478c2ecf20Sopenharmony_ci			udelay(1);
3488c2ecf20Sopenharmony_ci			arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
3498c2ecf20Sopenharmony_ci			airq = probe_irq_off(airqmask);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci			if (airq <= 0) {
3528c2ecf20Sopenharmony_ci				arc_cont(D_INIT_REASONS, "(airq=%d)\n", airq);
3538c2ecf20Sopenharmony_ci				arc_cont(D_INIT_REASONS, "S5: ");
3548c2ecf20Sopenharmony_ci				if (BUGLVL(D_INIT_REASONS))
3558c2ecf20Sopenharmony_ci					numprint = 0;
3568c2ecf20Sopenharmony_ci				release_region(*port, ARCNET_TOTAL_SIZE);
3578c2ecf20Sopenharmony_ci				*port-- = ports[--numports];
3588c2ecf20Sopenharmony_ci				continue;
3598c2ecf20Sopenharmony_ci			}
3608c2ecf20Sopenharmony_ci		} else {
3618c2ecf20Sopenharmony_ci			airq = irq;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		arc_cont(D_INIT, "(%d,", airq);
3658c2ecf20Sopenharmony_ci		openparen = 1;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		/* Everything seems okay.  But which shmem, if any, puts
3688c2ecf20Sopenharmony_ci		 * back its signature byte when the card is reset?
3698c2ecf20Sopenharmony_ci		 *
3708c2ecf20Sopenharmony_ci		 * If there are multiple cards installed, there might be
3718c2ecf20Sopenharmony_ci		 * multiple shmems still in the list.
3728c2ecf20Sopenharmony_ci		 */
3738c2ecf20Sopenharmony_ci#ifdef FAST_PROBE
3748c2ecf20Sopenharmony_ci		if (numports > 1 || numshmems > 1) {
3758c2ecf20Sopenharmony_ci			arcnet_inb(ioaddr, COM9026_REG_R_RESET);
3768c2ecf20Sopenharmony_ci			mdelay(RESETtime);
3778c2ecf20Sopenharmony_ci		} else {
3788c2ecf20Sopenharmony_ci			/* just one shmem and port, assume they match */
3798c2ecf20Sopenharmony_ci			arcnet_writeb(TESTvalue, iomem[0],
3808c2ecf20Sopenharmony_ci				      COM9026_REG_W_INTMASK);
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci#else
3838c2ecf20Sopenharmony_ci		arcnet_inb(ioaddr, COM9026_REG_R_RESET);
3848c2ecf20Sopenharmony_ci		mdelay(RESETtime);
3858c2ecf20Sopenharmony_ci#endif
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		for (index = 0; index < numshmems; index++) {
3888c2ecf20Sopenharmony_ci			u_long ptr = shmems[index];
3898c2ecf20Sopenharmony_ci			void __iomem *base = iomem[index];
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci			if (arcnet_readb(base, COM9026_REG_R_STATUS) == TESTvalue) {	/* found one */
3928c2ecf20Sopenharmony_ci				arc_cont(D_INIT, "%lXh)\n", *p);
3938c2ecf20Sopenharmony_ci				openparen = 0;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci				/* register the card */
3968c2ecf20Sopenharmony_ci				if (com90xx_found(*port, airq, ptr, base) == 0)
3978c2ecf20Sopenharmony_ci					found = 1;
3988c2ecf20Sopenharmony_ci				numprint = -1;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci				/* remove shmem from the list */
4018c2ecf20Sopenharmony_ci				shmems[index] = shmems[--numshmems];
4028c2ecf20Sopenharmony_ci				iomem[index] = iomem[numshmems];
4038c2ecf20Sopenharmony_ci				break;	/* go to the next I/O port */
4048c2ecf20Sopenharmony_ci			} else {
4058c2ecf20Sopenharmony_ci				arc_cont(D_INIT_REASONS, "%Xh-",
4068c2ecf20Sopenharmony_ci					 arcnet_readb(base, COM9026_REG_R_STATUS));
4078c2ecf20Sopenharmony_ci			}
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		if (openparen) {
4118c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT))
4128c2ecf20Sopenharmony_ci				pr_cont("no matching shmem)\n");
4138c2ecf20Sopenharmony_ci			if (BUGLVL(D_INIT_REASONS)) {
4148c2ecf20Sopenharmony_ci				pr_cont("S5: ");
4158c2ecf20Sopenharmony_ci				numprint = 0;
4168c2ecf20Sopenharmony_ci			}
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci		if (!found)
4198c2ecf20Sopenharmony_ci			release_region(*port, ARCNET_TOTAL_SIZE);
4208c2ecf20Sopenharmony_ci		*port-- = ports[--numports];
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (BUGLVL(D_INIT_REASONS))
4248c2ecf20Sopenharmony_ci		pr_cont("\n");
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* Now put back TESTvalue on all leftover shmems. */
4278c2ecf20Sopenharmony_ci	for (index = 0; index < numshmems; index++) {
4288c2ecf20Sopenharmony_ci		arcnet_writeb(TESTvalue, iomem[index], COM9026_REG_W_INTMASK);
4298c2ecf20Sopenharmony_ci		iounmap(iomem[index]);
4308c2ecf20Sopenharmony_ci		release_mem_region(shmems[index], MIRROR_SIZE);
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci	kfree(shmems);
4338c2ecf20Sopenharmony_ci	kfree(iomem);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int __init check_mirror(unsigned long addr, size_t size)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	void __iomem *p;
4398c2ecf20Sopenharmony_ci	int res = -1;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (!request_mem_region(addr, size, "arcnet (90xx)"))
4428c2ecf20Sopenharmony_ci		return -1;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	p = ioremap(addr, size);
4458c2ecf20Sopenharmony_ci	if (p) {
4468c2ecf20Sopenharmony_ci		if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
4478c2ecf20Sopenharmony_ci			res = 1;
4488c2ecf20Sopenharmony_ci		else
4498c2ecf20Sopenharmony_ci			res = 0;
4508c2ecf20Sopenharmony_ci		iounmap(p);
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	release_mem_region(addr, size);
4548c2ecf20Sopenharmony_ci	return res;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci/* Set up the struct net_device associated with this card.  Called after
4588c2ecf20Sopenharmony_ci * probing succeeds.
4598c2ecf20Sopenharmony_ci */
4608c2ecf20Sopenharmony_cistatic int __init com90xx_found(int ioaddr, int airq, u_long shmem,
4618c2ecf20Sopenharmony_ci				void __iomem *p)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct net_device *dev = NULL;
4648c2ecf20Sopenharmony_ci	struct arcnet_local *lp;
4658c2ecf20Sopenharmony_ci	u_long first_mirror, last_mirror;
4668c2ecf20Sopenharmony_ci	int mirror_size;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* allocate struct net_device */
4698c2ecf20Sopenharmony_ci	dev = alloc_arcdev(device);
4708c2ecf20Sopenharmony_ci	if (!dev) {
4718c2ecf20Sopenharmony_ci		arc_cont(D_NORMAL, "com90xx: Can't allocate device!\n");
4728c2ecf20Sopenharmony_ci		iounmap(p);
4738c2ecf20Sopenharmony_ci		release_mem_region(shmem, MIRROR_SIZE);
4748c2ecf20Sopenharmony_ci		return -ENOMEM;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	lp = netdev_priv(dev);
4778c2ecf20Sopenharmony_ci	/* find the real shared memory start/end points, including mirrors */
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* guess the actual size of one "memory mirror" - the number of
4808c2ecf20Sopenharmony_ci	 * bytes between copies of the shared memory.  On most cards, it's
4818c2ecf20Sopenharmony_ci	 * 2k (or there are no mirrors at all) but on some, it's 4k.
4828c2ecf20Sopenharmony_ci	 */
4838c2ecf20Sopenharmony_ci	mirror_size = MIRROR_SIZE;
4848c2ecf20Sopenharmony_ci	if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
4858c2ecf20Sopenharmony_ci	    check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
4868c2ecf20Sopenharmony_ci	    check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
4878c2ecf20Sopenharmony_ci		mirror_size = 2 * MIRROR_SIZE;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	first_mirror = shmem - mirror_size;
4908c2ecf20Sopenharmony_ci	while (check_mirror(first_mirror, mirror_size) == 1)
4918c2ecf20Sopenharmony_ci		first_mirror -= mirror_size;
4928c2ecf20Sopenharmony_ci	first_mirror += mirror_size;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	last_mirror = shmem + mirror_size;
4958c2ecf20Sopenharmony_ci	while (check_mirror(last_mirror, mirror_size) == 1)
4968c2ecf20Sopenharmony_ci		last_mirror += mirror_size;
4978c2ecf20Sopenharmony_ci	last_mirror -= mirror_size;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	dev->mem_start = first_mirror;
5008c2ecf20Sopenharmony_ci	dev->mem_end = last_mirror + MIRROR_SIZE - 1;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	iounmap(p);
5038c2ecf20Sopenharmony_ci	release_mem_region(shmem, MIRROR_SIZE);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (!request_mem_region(dev->mem_start,
5068c2ecf20Sopenharmony_ci				dev->mem_end - dev->mem_start + 1,
5078c2ecf20Sopenharmony_ci				"arcnet (90xx)"))
5088c2ecf20Sopenharmony_ci		goto err_free_dev;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* reserve the irq */
5118c2ecf20Sopenharmony_ci	if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
5128c2ecf20Sopenharmony_ci		arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", airq);
5138c2ecf20Sopenharmony_ci		goto err_release_mem;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci	dev->irq = airq;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* Initialize the rest of the device structure. */
5188c2ecf20Sopenharmony_ci	lp->card_name = "COM90xx";
5198c2ecf20Sopenharmony_ci	lp->hw.command = com90xx_command;
5208c2ecf20Sopenharmony_ci	lp->hw.status = com90xx_status;
5218c2ecf20Sopenharmony_ci	lp->hw.intmask = com90xx_setmask;
5228c2ecf20Sopenharmony_ci	lp->hw.reset = com90xx_reset;
5238c2ecf20Sopenharmony_ci	lp->hw.owner = THIS_MODULE;
5248c2ecf20Sopenharmony_ci	lp->hw.copy_to_card = com90xx_copy_to_card;
5258c2ecf20Sopenharmony_ci	lp->hw.copy_from_card = com90xx_copy_from_card;
5268c2ecf20Sopenharmony_ci	lp->mem_start = ioremap(dev->mem_start,
5278c2ecf20Sopenharmony_ci				dev->mem_end - dev->mem_start + 1);
5288c2ecf20Sopenharmony_ci	if (!lp->mem_start) {
5298c2ecf20Sopenharmony_ci		arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
5308c2ecf20Sopenharmony_ci		goto err_free_irq;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* get and check the station ID from offset 1 in shmem */
5348c2ecf20Sopenharmony_ci	dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	dev->base_addr = ioaddr;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	arc_printk(D_NORMAL, dev, "COM90xx station %02Xh found at %03lXh, IRQ %d, ShMem %lXh (%ld*%xh).\n",
5398c2ecf20Sopenharmony_ci		   dev->dev_addr[0],
5408c2ecf20Sopenharmony_ci		   dev->base_addr, dev->irq, dev->mem_start,
5418c2ecf20Sopenharmony_ci		   (dev->mem_end - dev->mem_start + 1) / mirror_size,
5428c2ecf20Sopenharmony_ci		   mirror_size);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (register_netdev(dev))
5458c2ecf20Sopenharmony_ci		goto err_unmap;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	cards[numcards++] = dev;
5488c2ecf20Sopenharmony_ci	return 0;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cierr_unmap:
5518c2ecf20Sopenharmony_ci	iounmap(lp->mem_start);
5528c2ecf20Sopenharmony_cierr_free_irq:
5538c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
5548c2ecf20Sopenharmony_cierr_release_mem:
5558c2ecf20Sopenharmony_ci	release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
5568c2ecf20Sopenharmony_cierr_free_dev:
5578c2ecf20Sopenharmony_ci	free_arcdev(dev);
5588c2ecf20Sopenharmony_ci	return -EIO;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void com90xx_command(struct net_device *dev, int cmd)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	short ioaddr = dev->base_addr;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic int com90xx_status(struct net_device *dev)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	short ioaddr = dev->base_addr;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic void com90xx_setmask(struct net_device *dev, int mask)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	short ioaddr = dev->base_addr;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci/* Do a hardware reset on the card, and set up necessary registers.
5838c2ecf20Sopenharmony_ci *
5848c2ecf20Sopenharmony_ci * This should be called as little as possible, because it disrupts the
5858c2ecf20Sopenharmony_ci * token on the network (causes a RECON) and requires a significant delay.
5868c2ecf20Sopenharmony_ci *
5878c2ecf20Sopenharmony_ci * However, it does make sure the card is in a defined state.
5888c2ecf20Sopenharmony_ci */
5898c2ecf20Sopenharmony_cistatic int com90xx_reset(struct net_device *dev, int really_reset)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
5928c2ecf20Sopenharmony_ci	short ioaddr = dev->base_addr;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	arc_printk(D_INIT, dev, "Resetting (status=%02Xh)\n",
5958c2ecf20Sopenharmony_ci		   arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (really_reset) {
5988c2ecf20Sopenharmony_ci		/* reset the card */
5998c2ecf20Sopenharmony_ci		arcnet_inb(ioaddr, COM9026_REG_R_RESET);
6008c2ecf20Sopenharmony_ci		mdelay(RESETtime);
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci	/* clear flags & end reset */
6038c2ecf20Sopenharmony_ci	arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
6048c2ecf20Sopenharmony_ci	arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci#if 0
6078c2ecf20Sopenharmony_ci	/* don't do this until we verify that it doesn't hurt older cards! */
6088c2ecf20Sopenharmony_ci	arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) | ENABLE16flag,
6098c2ecf20Sopenharmony_ci		    ioaddr, COM9026_REG_RW_CONFIG);
6108c2ecf20Sopenharmony_ci#endif
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* verify that the ARCnet signature byte is present */
6138c2ecf20Sopenharmony_ci	if (arcnet_readb(lp->mem_start, COM9026_REG_R_STATUS) != TESTvalue) {
6148c2ecf20Sopenharmony_ci		if (really_reset)
6158c2ecf20Sopenharmony_ci			arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
6168c2ecf20Sopenharmony_ci		return 1;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci	/* enable extended (512-byte) packets */
6198c2ecf20Sopenharmony_ci	arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* clean out all the memory to make debugging make more sense :) */
6228c2ecf20Sopenharmony_ci	if (BUGLVL(D_DURING))
6238c2ecf20Sopenharmony_ci		memset_io(lp->mem_start, 0x42, 2048);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	/* done!  return success. */
6268c2ecf20Sopenharmony_ci	return 0;
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic void com90xx_copy_to_card(struct net_device *dev, int bufnum,
6308c2ecf20Sopenharmony_ci				 int offset, void *buf, int count)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
6338c2ecf20Sopenharmony_ci	void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic void com90xx_copy_from_card(struct net_device *dev, int bufnum,
6398c2ecf20Sopenharmony_ci				   int offset, void *buf, int count)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
6428c2ecf20Sopenharmony_ci	void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic int __init com90xx_init(void)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	if (irq == 2)
6528c2ecf20Sopenharmony_ci		irq = 9;
6538c2ecf20Sopenharmony_ci	com90xx_probe();
6548c2ecf20Sopenharmony_ci	if (!numcards)
6558c2ecf20Sopenharmony_ci		return -EIO;
6568c2ecf20Sopenharmony_ci	return 0;
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic void __exit com90xx_exit(void)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct net_device *dev;
6628c2ecf20Sopenharmony_ci	struct arcnet_local *lp;
6638c2ecf20Sopenharmony_ci	int count;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	for (count = 0; count < numcards; count++) {
6668c2ecf20Sopenharmony_ci		dev = cards[count];
6678c2ecf20Sopenharmony_ci		lp = netdev_priv(dev);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci		unregister_netdev(dev);
6708c2ecf20Sopenharmony_ci		free_irq(dev->irq, dev);
6718c2ecf20Sopenharmony_ci		iounmap(lp->mem_start);
6728c2ecf20Sopenharmony_ci		release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
6738c2ecf20Sopenharmony_ci		release_mem_region(dev->mem_start,
6748c2ecf20Sopenharmony_ci				   dev->mem_end - dev->mem_start + 1);
6758c2ecf20Sopenharmony_ci		free_arcdev(dev);
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cimodule_init(com90xx_init);
6808c2ecf20Sopenharmony_cimodule_exit(com90xx_exit);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci#ifndef MODULE
6838c2ecf20Sopenharmony_cistatic int __init com90xx_setup(char *s)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	int ints[8];
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	s = get_options(s, 8, ints);
6888c2ecf20Sopenharmony_ci	if (!ints[0] && !*s) {
6898c2ecf20Sopenharmony_ci		pr_notice("Disabled\n");
6908c2ecf20Sopenharmony_ci		return 1;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	switch (ints[0]) {
6948c2ecf20Sopenharmony_ci	default:		/* ERROR */
6958c2ecf20Sopenharmony_ci		pr_err("Too many arguments\n");
6968c2ecf20Sopenharmony_ci		fallthrough;
6978c2ecf20Sopenharmony_ci	case 3:		/* Mem address */
6988c2ecf20Sopenharmony_ci		shmem = ints[3];
6998c2ecf20Sopenharmony_ci		fallthrough;
7008c2ecf20Sopenharmony_ci	case 2:		/* IRQ */
7018c2ecf20Sopenharmony_ci		irq = ints[2];
7028c2ecf20Sopenharmony_ci		fallthrough;
7038c2ecf20Sopenharmony_ci	case 1:		/* IO address */
7048c2ecf20Sopenharmony_ci		io = ints[1];
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (*s)
7088c2ecf20Sopenharmony_ci		snprintf(device, sizeof(device), "%s", s);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return 1;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci__setup("com90xx=", com90xx_setup);
7148c2ecf20Sopenharmony_ci#endif
715