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