brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 3b463fb Raw
428 lines · c
1/*2 * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers)3 *4 * Written 1997 by David Woodhouse.5 * Written 1994-1999 by Avery Pennarun.6 * Written 1999-2000 by Martin Mares <mj@ucw.cz>.7 * Derived from skeleton.c by Donald Becker.8 *9 * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)10 *  for sponsoring the further development of this driver.11 *12 * **********************13 *14 * The original copyright of skeleton.c was as follows:15 *16 * skeleton.c Written 1993 by Donald Becker.17 * Copyright 1993 United States Government as represented by the18 * Director, National Security Agency.  This software may only be used19 * and distributed according to the terms of the GNU General Public License as20 * modified by SRC, incorporated herein by reference.21 *22 * **********************23 *24 * For more details, see drivers/net/arcnet.c25 *26 * **********************27 */28 29#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt30 31#include <linux/kernel.h>32#include <linux/module.h>33#include <linux/moduleparam.h>34#include <linux/ioport.h>35#include <linux/delay.h>36#include <linux/netdevice.h>37#include <linux/memblock.h>38#include <linux/init.h>39#include <linux/interrupt.h>40#include <linux/io.h>41 42#include "arcdevice.h"43#include "com9026.h"44 45/* Internal function declarations */46 47static int com90io_found(struct net_device *dev);48static void com90io_command(struct net_device *dev, int command);49static int com90io_status(struct net_device *dev);50static void com90io_setmask(struct net_device *dev, int mask);51static int com90io_reset(struct net_device *dev, int really_reset);52static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,53				 void *buf, int count);54static void com90io_copy_from_card(struct net_device *dev, int bufnum,55				   int offset, void *buf, int count);56 57/* Handy defines for ARCnet specific stuff */58 59/* The number of low I/O ports used by the card. */60#define ARCNET_TOTAL_SIZE 1661 62/****************************************************************************63 *                                                                          *64 * IO-mapped operation routines                                             *65 *                                                                          *66 ****************************************************************************/67 68#undef ONE_AT_A_TIME_TX69#undef ONE_AT_A_TIME_RX70 71static u_char get_buffer_byte(struct net_device *dev, unsigned offset)72{73	int ioaddr = dev->base_addr;74 75	arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);76	arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);77 78	return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);79}80 81#ifdef ONE_AT_A_TIME_TX82static void put_buffer_byte(struct net_device *dev, unsigned offset,83			    u_char datum)84{85	int ioaddr = dev->base_addr;86 87	arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);88	arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);89 90	arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA);91}92 93#endif94 95static void get_whole_buffer(struct net_device *dev, unsigned offset,96			     unsigned length, char *dest)97{98	int ioaddr = dev->base_addr;99 100	arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);101	arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);102 103	while (length--)104#ifdef ONE_AT_A_TIME_RX105		*(dest++) = get_buffer_byte(dev, offset++);106#else107		*(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);108#endif109}110 111static void put_whole_buffer(struct net_device *dev, unsigned offset,112			     unsigned length, char *dest)113{114	int ioaddr = dev->base_addr;115 116	arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);117	arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO);118 119	while (length--)120#ifdef ONE_AT_A_TIME_TX121		put_buffer_byte(dev, offset++, *(dest++));122#else123		arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA);124#endif125}126 127/* We cannot probe for an IO mapped card either, although we can check that128 * it's where we were told it was, and even autoirq129 */130static int __init com90io_probe(struct net_device *dev)131{132	int ioaddr = dev->base_addr, status;133	unsigned long airqmask;134 135	if (BUGLVL(D_NORMAL)) {136		pr_info("%s\n", "COM90xx IO-mapped mode support (by David Woodhouse et el.)");137		pr_info("E-mail me if you actually test this driver, please!\n");138	}139 140	if (!ioaddr) {141		arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n");142		return -ENODEV;143	}144	if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) {145		arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n",146			   ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);147		return -ENXIO;148	}149	if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {150		arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n",151			   ioaddr);152		goto err_out;153	}154	arcnet_inb(ioaddr, COM9026_REG_R_RESET);155	mdelay(RESETtime);156 157	status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);158 159	if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {160		arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n",161			   status);162		goto err_out;163	}164	arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n", status);165 166	arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,167		    ioaddr, COM9026_REG_W_COMMAND);168 169	arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n",170		   status);171 172	status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);173 174	if (status & RESETflag) {175		arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n",176			   status);177		goto err_out;178	}179	arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag,180		    ioaddr, COM9026_REG_RW_CONFIG);181 182	/* Read first loc'n of memory */183 184	arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);185	arcnet_outb(0, ioaddr,  COM9026_REG_W_ADDR_LO);186 187	status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);188	if (status != 0xd1) {189		arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n",190			   status);191		goto err_out;192	}193	if (!dev->irq) {194		/* if we do this, we're sure to get an IRQ since the195		 * card has just reset and the NORXflag is on until196		 * we tell it to start receiving.197		 */198 199		airqmask = probe_irq_on();200		arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);201		udelay(1);202		arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);203		dev->irq = probe_irq_off(airqmask);204 205		if ((int)dev->irq <= 0) {206			arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n");207			goto err_out;208		}209	}210	release_region(ioaddr, ARCNET_TOTAL_SIZE); /* end of probing */211	return com90io_found(dev);212 213err_out:214	release_region(ioaddr, ARCNET_TOTAL_SIZE);215	return -ENODEV;216}217 218/* Set up the struct net_device associated with this card.  Called after219 * probing succeeds.220 */221static int __init com90io_found(struct net_device *dev)222{223	struct arcnet_local *lp;224	int ioaddr = dev->base_addr;225	int err;226 227	/* Reserve the irq */228	if (request_irq(dev->irq, arcnet_interrupt, 0,229			"arcnet (COM90xx-IO)", dev)) {230		arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);231		return -ENODEV;232	}233	/* Reserve the I/O region */234	if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE,235			    "arcnet (COM90xx-IO)")) {236		free_irq(dev->irq, dev);237		return -EBUSY;238	}239 240	lp = netdev_priv(dev);241	lp->card_name = "COM90xx I/O";242	lp->hw.command = com90io_command;243	lp->hw.status = com90io_status;244	lp->hw.intmask = com90io_setmask;245	lp->hw.reset = com90io_reset;246	lp->hw.owner = THIS_MODULE;247	lp->hw.copy_to_card = com90io_copy_to_card;248	lp->hw.copy_from_card = com90io_copy_from_card;249 250	lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;251	arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);252 253	/* get and check the station ID from offset 1 in shmem */254 255	arcnet_set_addr(dev, get_buffer_byte(dev, 1));256 257	err = register_netdev(dev);258	if (err) {259		arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,260			    ioaddr, COM9026_REG_RW_CONFIG);261		free_irq(dev->irq, dev);262		release_region(dev->base_addr, ARCNET_TOTAL_SIZE);263		return err;264	}265 266	arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",267		   dev->dev_addr[0], dev->base_addr, dev->irq);268 269	return 0;270}271 272/* Do a hardware reset on the card, and set up necessary registers.273 *274 * This should be called as little as possible, because it disrupts the275 * token on the network (causes a RECON) and requires a significant delay.276 *277 * However, it does make sure the card is in a defined state.278 */279static int com90io_reset(struct net_device *dev, int really_reset)280{281	struct arcnet_local *lp = netdev_priv(dev);282	short ioaddr = dev->base_addr;283 284	arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",285		   dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS));286 287	if (really_reset) {288		/* reset the card */289		arcnet_inb(ioaddr, COM9026_REG_R_RESET);290		mdelay(RESETtime);291	}292	/* Set the thing to IO-mapped, 8-bit  mode */293	lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag;294	arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);295 296	arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);297					/* clear flags & end reset */298	arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);299 300	/* verify that the ARCnet signature byte is present */301	if (get_buffer_byte(dev, 0) != TESTvalue) {302		arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");303		return 1;304	}305	/* enable extended (512-byte) packets */306	arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);307	/* done!  return success. */308	return 0;309}310 311static void com90io_command(struct net_device *dev, int cmd)312{313	short ioaddr = dev->base_addr;314 315	arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);316}317 318static int com90io_status(struct net_device *dev)319{320	short ioaddr = dev->base_addr;321 322	return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);323}324 325static void com90io_setmask(struct net_device *dev, int mask)326{327	short ioaddr = dev->base_addr;328 329	arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);330}331 332static void com90io_copy_to_card(struct net_device *dev, int bufnum,333				 int offset, void *buf, int count)334{335	TIME(dev, "put_whole_buffer", count,336	     put_whole_buffer(dev, bufnum * 512 + offset, count, buf));337}338 339static void com90io_copy_from_card(struct net_device *dev, int bufnum,340				   int offset, void *buf, int count)341{342	TIME(dev, "get_whole_buffer", count,343	     get_whole_buffer(dev, bufnum * 512 + offset, count, buf));344}345 346static int io;			/* use the insmod io= irq= shmem= options */347static int irq;348static char device[9];		/* use eg. device=arc1 to change name */349 350module_param_hw(io, int, ioport, 0);351module_param_hw(irq, int, irq, 0);352module_param_string(device, device, sizeof(device), 0);353MODULE_DESCRIPTION("ARCnet COM90xx IO mapped chipset driver");354MODULE_LICENSE("GPL");355 356#ifndef MODULE357static int __init com90io_setup(char *s)358{359	int ints[4];360 361	s = get_options(s, 4, ints);362	if (!ints[0])363		return 0;364	switch (ints[0]) {365	default:		/* ERROR */366		pr_err("Too many arguments\n");367		fallthrough;368	case 2:		/* IRQ */369		irq = ints[2];370		fallthrough;371	case 1:		/* IO address */372		io = ints[1];373	}374	if (*s)375		snprintf(device, sizeof(device), "%s", s);376	return 1;377}378__setup("com90io=", com90io_setup);379#endif380 381static struct net_device *my_dev;382 383static int __init com90io_init(void)384{385	struct net_device *dev;386	int err;387 388	dev = alloc_arcdev(device);389	if (!dev)390		return -ENOMEM;391 392	dev->base_addr = io;393	dev->irq = irq;394	if (dev->irq == 2)395		dev->irq = 9;396 397	err = com90io_probe(dev);398 399	if (err) {400		free_arcdev(dev);401		return err;402	}403 404	my_dev = dev;405	return 0;406}407 408static void __exit com90io_exit(void)409{410	struct net_device *dev = my_dev;411	int ioaddr = dev->base_addr;412 413	unregister_netdev(dev);414 415	/* In case the old driver is loaded later,416	 * set the thing back to MMAP mode417	 */418	arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,419		    ioaddr, COM9026_REG_RW_CONFIG);420 421	free_irq(dev->irq, dev);422	release_region(dev->base_addr, ARCNET_TOTAL_SIZE);423	free_arcdev(dev);424}425 426module_init(com90io_init)427module_exit(com90io_exit)428