420 lines · c
1/*2 * Linux ARCnet driver - COM20020 chipset support3 *4 * Written 1997 by David Woodhouse.5 * Written 1994-1999 by Avery Pennarun.6 * Written 1999 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/module.h>32#include <linux/kernel.h>33#include <linux/types.h>34#include <linux/ioport.h>35#include <linux/errno.h>36#include <linux/delay.h>37#include <linux/netdevice.h>38#include <linux/init.h>39#include <linux/interrupt.h>40#include <linux/io.h>41 42#include "arcdevice.h"43#include "com20020.h"44 45static const char * const clockrates[] = {46 "XXXXXXX", "XXXXXXXX", "XXXXXX", "2.5 Mb/s",47 "1.25Mb/s", "625 Kb/s", "312.5 Kb/s", "156.25 Kb/s",48 "Reserved", "Reserved", "Reserved"49};50 51static void com20020_command(struct net_device *dev, int command);52static int com20020_status(struct net_device *dev);53static void com20020_setmask(struct net_device *dev, int mask);54static int com20020_reset(struct net_device *dev, int really_reset);55static void com20020_copy_to_card(struct net_device *dev, int bufnum,56 int offset, void *buf, int count);57static void com20020_copy_from_card(struct net_device *dev, int bufnum,58 int offset, void *buf, int count);59static void com20020_set_mc_list(struct net_device *dev);60static void com20020_close(struct net_device *);61 62static void com20020_copy_from_card(struct net_device *dev, int bufnum,63 int offset, void *buf, int count)64{65 int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;66 67 /* set up the address register */68 arcnet_outb((ofs >> 8) | RDDATAflag | AUTOINCflag,69 ioaddr, COM20020_REG_W_ADDR_HI);70 arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);71 72 /* copy the data */73 TIME(dev, "insb", count,74 arcnet_insb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count));75}76 77static void com20020_copy_to_card(struct net_device *dev, int bufnum,78 int offset, void *buf, int count)79{80 int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;81 82 /* set up the address register */83 arcnet_outb((ofs >> 8) | AUTOINCflag, ioaddr, COM20020_REG_W_ADDR_HI);84 arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);85 86 /* copy the data */87 TIME(dev, "outsb", count,88 arcnet_outsb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count));89}90 91/* Reset the card and check some basic stuff during the detection stage. */92int com20020_check(struct net_device *dev)93{94 int ioaddr = dev->base_addr, status;95 struct arcnet_local *lp = netdev_priv(dev);96 97 arcnet_outb(XTOcfg(3) | RESETcfg, ioaddr, COM20020_REG_W_CONFIG);98 udelay(5);99 arcnet_outb(XTOcfg(3), ioaddr, COM20020_REG_W_CONFIG);100 mdelay(RESETtime);101 102 lp->setup = lp->clockm ? 0 : (lp->clockp << 1);103 lp->setup2 = (lp->clockm << 4) | 8;104 105 /* CHECK: should we do this for SOHARD cards ? */106 /* Enable P1Mode for backplane mode */107 lp->setup = lp->setup | P1MODE;108 109 com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);110 arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);111 112 if (lp->clockm != 0) {113 com20020_set_subaddress(lp, ioaddr, SUB_SETUP2);114 arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG);115 116 /* must now write the magic "restart operation" command */117 mdelay(1);118 arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);119 }120 121 lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;122 /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */123 arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);124 arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG);125 126 status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS);127 128 if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) {129 arc_printk(D_NORMAL, dev, "status invalid (%Xh).\n", status);130 return -ENODEV;131 }132 arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status);133 134 arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,135 ioaddr, COM20020_REG_W_COMMAND);136 status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS);137 arc_printk(D_INIT_REASONS, dev, "status after reset acknowledged: %X\n",138 status);139 140 /* Read first location of memory */141 arcnet_outb(0 | RDDATAflag | AUTOINCflag,142 ioaddr, COM20020_REG_W_ADDR_HI);143 arcnet_outb(0, ioaddr, COM20020_REG_W_ADDR_LO);144 145 status = arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA);146 if (status != TESTvalue) {147 arc_printk(D_NORMAL, dev, "Signature byte not found (%02Xh != D1h).\n",148 status);149 return -ENODEV;150 }151 return 0;152}153 154static int com20020_set_hwaddr(struct net_device *dev, void *addr)155{156 int ioaddr = dev->base_addr;157 struct arcnet_local *lp = netdev_priv(dev);158 struct sockaddr *hwaddr = addr;159 160 dev_addr_set(dev, hwaddr->sa_data);161 com20020_set_subaddress(lp, ioaddr, SUB_NODE);162 arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG);163 164 return 0;165}166 167static int com20020_netdev_open(struct net_device *dev)168{169 int ioaddr = dev->base_addr;170 struct arcnet_local *lp = netdev_priv(dev);171 172 lp->config |= TXENcfg;173 arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);174 175 return arcnet_open(dev);176}177 178static int com20020_netdev_close(struct net_device *dev)179{180 int ioaddr = dev->base_addr;181 struct arcnet_local *lp = netdev_priv(dev);182 183 arcnet_close(dev);184 185 /* disable transmitter */186 lp->config &= ~TXENcfg;187 arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);188 return 0;189}190 191const struct net_device_ops com20020_netdev_ops = {192 .ndo_open = com20020_netdev_open,193 .ndo_stop = com20020_netdev_close,194 .ndo_start_xmit = arcnet_send_packet,195 .ndo_tx_timeout = arcnet_timeout,196 .ndo_set_mac_address = com20020_set_hwaddr,197 .ndo_set_rx_mode = com20020_set_mc_list,198};199 200/* Set up the struct net_device associated with this card. Called after201 * probing succeeds.202 */203int com20020_found(struct net_device *dev, int shared)204{205 struct arcnet_local *lp;206 int ioaddr = dev->base_addr;207 208 /* Initialize the rest of the device structure. */209 210 lp = netdev_priv(dev);211 212 lp->hw.owner = THIS_MODULE;213 lp->hw.command = com20020_command;214 lp->hw.status = com20020_status;215 lp->hw.intmask = com20020_setmask;216 lp->hw.reset = com20020_reset;217 lp->hw.copy_to_card = com20020_copy_to_card;218 lp->hw.copy_from_card = com20020_copy_from_card;219 lp->hw.close = com20020_close;220 221 /* FIXME: do this some other way! */222 if (!dev->dev_addr[0])223 arcnet_set_addr(dev, arcnet_inb(ioaddr, 8));224 225 com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);226 arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);227 228 if (lp->card_flags & ARC_CAN_10MBIT) {229 com20020_set_subaddress(lp, ioaddr, SUB_SETUP2);230 arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG);231 232 /* must now write the magic "restart operation" command */233 mdelay(1);234 arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);235 }236 237 lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;238 /* Default 0x38 + register: Node ID */239 arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);240 arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG);241 242 /* reserve the irq */243 if (request_irq(dev->irq, arcnet_interrupt, shared,244 "arcnet (COM20020)", dev)) {245 arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);246 return -ENODEV;247 }248 249 arc_printk(D_NORMAL, dev, "%s: station %02Xh found at %03lXh, IRQ %d.\n",250 lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq);251 252 if (lp->backplane)253 arc_printk(D_NORMAL, dev, "Using backplane mode.\n");254 255 if (lp->timeout != 3)256 arc_printk(D_NORMAL, dev, "Using extended timeout value of %d\n",257 lp->timeout);258 259 arc_printk(D_NORMAL, dev, "Using CKP %d - data rate %s\n",260 lp->setup >> 1,261 clockrates[3 -262 ((lp->setup2 & 0xF0) >> 4) +263 ((lp->setup & 0x0F) >> 1)]);264 /* The clockrates array index looks very fragile.265 * It seems like it could have negative indexing.266 */267 268 if (register_netdev(dev)) {269 free_irq(dev->irq, dev);270 return -EIO;271 }272 return 0;273}274 275/* Do a hardware reset on the card, and set up necessary registers.276 *277 * This should be called as little as possible, because it disrupts the278 * token on the network (causes a RECON) and requires a significant delay.279 *280 * However, it does make sure the card is in a defined state.281 */282static int com20020_reset(struct net_device *dev, int really_reset)283{284 struct arcnet_local *lp = netdev_priv(dev);285 u_int ioaddr = dev->base_addr;286 u_char inbyte;287 288 arc_printk(D_DEBUG, dev, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n",289 __FILE__, __LINE__, __func__, dev, lp, dev->name);290 arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",291 dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS));292 293 arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);294 lp->config |= (lp->timeout << 3) | (lp->backplane << 2);295 /* power-up defaults */296 arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);297 arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);298 299 if (really_reset) {300 /* reset the card */301 arcnet_outb(lp->config | RESETcfg, ioaddr, COM20020_REG_W_CONFIG);302 udelay(5);303 arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);304 mdelay(RESETtime * 2);305 /* COM20020 seems to be slower sometimes */306 }307 /* clear flags & end reset */308 arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);309 arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,310 ioaddr, COM20020_REG_W_COMMAND);311 312 /* verify that the ARCnet signature byte is present */313 arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);314 315 com20020_copy_from_card(dev, 0, 0, &inbyte, 1);316 arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);317 if (inbyte != TESTvalue) {318 arc_printk(D_DEBUG, dev, "%s: %d: %s\n",319 __FILE__, __LINE__, __func__);320 arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");321 return 1;322 }323 /* enable extended (512-byte) packets */324 arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM20020_REG_W_COMMAND);325 326 arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);327 328 /* done! return success. */329 return 0;330}331 332static void com20020_setmask(struct net_device *dev, int mask)333{334 u_int ioaddr = dev->base_addr;335 336 arc_printk(D_DURING, dev, "Setting mask to %x at %x\n", mask, ioaddr);337 arcnet_outb(mask, ioaddr, COM20020_REG_W_INTMASK);338}339 340static void com20020_command(struct net_device *dev, int cmd)341{342 u_int ioaddr = dev->base_addr;343 344 arcnet_outb(cmd, ioaddr, COM20020_REG_W_COMMAND);345}346 347static int com20020_status(struct net_device *dev)348{349 u_int ioaddr = dev->base_addr;350 351 return arcnet_inb(ioaddr, COM20020_REG_R_STATUS) +352 (arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT) << 8);353}354 355static void com20020_close(struct net_device *dev)356{357 struct arcnet_local *lp = netdev_priv(dev);358 int ioaddr = dev->base_addr;359 360 /* disable transmitter */361 lp->config &= ~TXENcfg;362 arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);363}364 365/* Set or clear the multicast filter for this adaptor.366 * num_addrs == -1 Promiscuous mode, receive all packets367 * num_addrs == 0 Normal mode, clear multicast list368 * num_addrs > 0 Multicast mode, receive normal and MC packets, and do369 * best-effort filtering.370 * FIXME - do multicast stuff, not just promiscuous.371 */372static void com20020_set_mc_list(struct net_device *dev)373{374 struct arcnet_local *lp = netdev_priv(dev);375 int ioaddr = dev->base_addr;376 377 if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) {378 /* Enable promiscuous mode */379 if (!(lp->setup & PROMISCset))380 arc_printk(D_NORMAL, dev, "Setting promiscuous flag...\n");381 com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);382 lp->setup |= PROMISCset;383 arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);384 } else {385 /* Disable promiscuous mode, use normal mode */386 if ((lp->setup & PROMISCset))387 arc_printk(D_NORMAL, dev, "Resetting promiscuous flag...\n");388 com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);389 lp->setup &= ~PROMISCset;390 arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);391 }392}393 394#if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \395 defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \396 defined(CONFIG_ARCNET_COM20020_CS_MODULE)397EXPORT_SYMBOL(com20020_check);398EXPORT_SYMBOL(com20020_found);399EXPORT_SYMBOL(com20020_netdev_ops);400#endif401 402MODULE_DESCRIPTION("ARCnet COM20020 chipset core driver");403MODULE_LICENSE("GPL");404 405#ifdef MODULE406 407static int __init com20020_module_init(void)408{409 if (BUGLVL(D_NORMAL))410 pr_info("%s\n", "COM20020 chipset support (by David Woodhouse et al.)");411 return 0;412}413 414static void __exit com20020_module_exit(void)415{416}417module_init(com20020_module_init);418module_exit(com20020_module_exit);419#endif /* MODULE */420