2005-04-16 15:20:36 -07:00
/*
* Linux ARCnet driver - COM90xx chipset ( IO - mapped buffers )
2015-05-05 10:05:47 -07:00
*
2005-04-16 15:20:36 -07:00
* Written 1997 by David Woodhouse .
* Written 1994 - 1999 by Avery Pennarun .
* Written 1999 - 2000 by Martin Mares < mj @ ucw . cz > .
* Derived from skeleton . c by Donald Becker .
*
* Special thanks to Contemporary Controls , Inc . ( www . ccontrols . com )
* for sponsoring the further development of this driver .
*
* * * * * * * * * * * * * * * * * * * * * * *
*
* The original copyright of skeleton . c was as follows :
*
* skeleton . c Written 1993 by Donald Becker .
* Copyright 1993 United States Government as represented by the
* Director , National Security Agency . This software may only be used
* and distributed according to the terms of the GNU General Public License as
* modified by SRC , incorporated herein by reference .
*
* * * * * * * * * * * * * * * * * * * * * * *
*
* For more details , see drivers / net / arcnet . c
*
* * * * * * * * * * * * * * * * * * * * * * *
*/
2015-05-05 10:05:56 -07:00
# define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/netdevice.h>
2018-10-30 15:09:49 -07:00
# include <linux/memblock.h>
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
2011-06-06 10:43:46 +00:00
# include <linux/interrupt.h>
2015-05-05 10:05:51 -07:00
# include <linux/io.h>
2015-05-05 10:06:03 -07:00
# include "arcdevice.h"
2015-05-05 10:06:13 -07:00
# include "com9026.h"
2005-04-16 15:20:36 -07:00
/* Internal function declarations */
static int com90io_found ( struct net_device * dev ) ;
static void com90io_command ( struct net_device * dev , int command ) ;
static int com90io_status ( struct net_device * dev ) ;
static void com90io_setmask ( struct net_device * dev , int mask ) ;
static int com90io_reset ( struct net_device * dev , int really_reset ) ;
static void com90io_copy_to_card ( struct net_device * dev , int bufnum , int offset ,
void * buf , int count ) ;
2015-05-05 10:06:02 -07:00
static void com90io_copy_from_card ( struct net_device * dev , int bufnum ,
int offset , void * buf , int count ) ;
2005-04-16 15:20:36 -07:00
/* Handy defines for ARCnet specific stuff */
/* The number of low I/O ports used by the card. */
# define ARCNET_TOTAL_SIZE 16
/****************************************************************************
* *
* IO - mapped operation routines *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# undef ONE_AT_A_TIME_TX
# undef ONE_AT_A_TIME_RX
static u_char get_buffer_byte ( struct net_device * dev , unsigned offset )
{
int ioaddr = dev - > base_addr ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( offset > > 8 , ioaddr , COM9026_REG_W_ADDR_HI ) ;
arcnet_outb ( offset & 0xff , ioaddr , COM9026_REG_W_ADDR_LO ) ;
2005-04-16 15:20:36 -07:00
2015-05-05 10:06:07 -07:00
return arcnet_inb ( ioaddr , COM9026_REG_RW_MEMDATA ) ;
2005-04-16 15:20:36 -07:00
}
# ifdef ONE_AT_A_TIME_TX
2015-05-05 10:06:02 -07:00
static void put_buffer_byte ( struct net_device * dev , unsigned offset ,
u_char datum )
2005-04-16 15:20:36 -07:00
{
int ioaddr = dev - > base_addr ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( offset > > 8 , ioaddr , COM9026_REG_W_ADDR_HI ) ;
arcnet_outb ( offset & 0xff , ioaddr , COM9026_REG_W_ADDR_LO ) ;
2005-04-16 15:20:36 -07:00
2015-05-05 10:06:07 -07:00
arcnet_outb ( datum , ioaddr , COM9026_REG_RW_MEMDATA ) ;
2005-04-16 15:20:36 -07:00
}
# endif
2015-05-05 10:06:02 -07:00
static void get_whole_buffer ( struct net_device * dev , unsigned offset ,
unsigned length , char * dest )
2005-04-16 15:20:36 -07:00
{
int ioaddr = dev - > base_addr ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( ( offset > > 8 ) | AUTOINCflag , ioaddr , COM9026_REG_W_ADDR_HI ) ;
arcnet_outb ( offset & 0xff , ioaddr , COM9026_REG_W_ADDR_LO ) ;
2005-04-16 15:20:36 -07:00
while ( length - - )
# ifdef ONE_AT_A_TIME_RX
* ( dest + + ) = get_buffer_byte ( dev , offset + + ) ;
# else
2015-05-05 10:06:07 -07:00
* ( dest + + ) = arcnet_inb ( ioaddr , COM9026_REG_RW_MEMDATA ) ;
2005-04-16 15:20:36 -07:00
# endif
}
2015-05-05 10:06:02 -07:00
static void put_whole_buffer ( struct net_device * dev , unsigned offset ,
unsigned length , char * dest )
2005-04-16 15:20:36 -07:00
{
int ioaddr = dev - > base_addr ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( ( offset > > 8 ) | AUTOINCflag , ioaddr , COM9026_REG_W_ADDR_HI ) ;
arcnet_outb ( offset & 0xff , ioaddr , COM9026_REG_W_ADDR_LO ) ;
2005-04-16 15:20:36 -07:00
while ( length - - )
# ifdef ONE_AT_A_TIME_TX
put_buffer_byte ( dev , offset + + , * ( dest + + ) ) ;
# else
2015-05-05 10:06:07 -07:00
arcnet_outb ( * ( dest + + ) , ioaddr , COM9026_REG_RW_MEMDATA ) ;
2005-04-16 15:20:36 -07:00
# endif
}
2015-05-05 10:05:52 -07:00
/* We cannot probe for an IO mapped card either, although we can check that
2005-04-16 15:20:36 -07:00
* it ' s where we were told it was , and even autoirq
*/
static int __init com90io_probe ( struct net_device * dev )
{
int ioaddr = dev - > base_addr , status ;
unsigned long airqmask ;
2015-05-05 10:05:54 -07:00
if ( BUGLVL ( D_NORMAL ) ) {
2015-05-05 10:05:56 -07:00
pr_info ( " %s \n " , " COM90xx IO-mapped mode support (by David Woodhouse et el.) " ) ;
pr_info ( " E-mail me if you actually test this driver, please! \n " ) ;
2015-05-05 10:05:54 -07:00
}
2005-04-16 15:20:36 -07:00
if ( ! ioaddr ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_NORMAL , dev , " No autoprobe for IO mapped cards; you must specify the base address! \n " ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
if ( ! request_region ( ioaddr , ARCNET_TOTAL_SIZE , " com90io probe " ) ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " IO request_region %x-%x failed \n " ,
ioaddr , ioaddr + ARCNET_TOTAL_SIZE - 1 ) ;
2005-04-16 15:20:36 -07:00
return - ENXIO ;
}
2015-05-05 10:06:07 -07:00
if ( arcnet_inb ( ioaddr , COM9026_REG_R_STATUS ) = = 0xFF ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " IO address %x empty \n " ,
ioaddr ) ;
2005-04-16 15:20:36 -07:00
goto err_out ;
}
2015-05-05 10:06:07 -07:00
arcnet_inb ( ioaddr , COM9026_REG_R_RESET ) ;
2005-04-16 15:20:36 -07:00
mdelay ( RESETtime ) ;
2015-05-05 10:06:07 -07:00
status = arcnet_inb ( ioaddr , COM9026_REG_R_STATUS ) ;
2005-04-16 15:20:36 -07:00
if ( ( status & 0x9D ) ! = ( NORXflag | RECONflag | TXFREEflag | RESETflag ) ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " Status invalid (%Xh) \n " ,
status ) ;
2005-04-16 15:20:36 -07:00
goto err_out ;
}
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " Status after reset: %X \n " , status ) ;
2005-04-16 15:20:36 -07:00
2015-05-05 10:06:07 -07:00
arcnet_outb ( CFLAGScmd | RESETclear | CONFIGclear ,
ioaddr , COM9026_REG_W_COMMAND ) ;
2005-04-16 15:20:36 -07:00
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " Status after reset acknowledged: %X \n " ,
status ) ;
2005-04-16 15:20:36 -07:00
2015-05-05 10:06:07 -07:00
status = arcnet_inb ( ioaddr , COM9026_REG_R_STATUS ) ;
2005-04-16 15:20:36 -07:00
if ( status & RESETflag ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " Eternal reset (status=%Xh) \n " ,
status ) ;
2005-04-16 15:20:36 -07:00
goto err_out ;
}
2015-05-05 10:06:07 -07:00
arcnet_outb ( ( 0x16 | IOMAPflag ) & ~ ENABLE16flag ,
ioaddr , COM9026_REG_RW_CONFIG ) ;
2005-04-16 15:20:36 -07:00
/* Read first loc'n of memory */
2015-05-05 10:06:07 -07:00
arcnet_outb ( AUTOINCflag , ioaddr , COM9026_REG_W_ADDR_HI ) ;
arcnet_outb ( 0 , ioaddr , COM9026_REG_W_ADDR_LO ) ;
2005-04-16 15:20:36 -07:00
2015-05-05 10:06:07 -07:00
status = arcnet_inb ( ioaddr , COM9026_REG_RW_MEMDATA ) ;
2015-05-05 10:05:59 -07:00
if ( status ! = 0xd1 ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " Signature byte not found (%Xh instead). \n " ,
status ) ;
2005-04-16 15:20:36 -07:00
goto err_out ;
}
if ( ! dev - > irq ) {
2015-05-05 10:05:52 -07:00
/* if we do this, we're sure to get an IRQ since the
2005-04-16 15:20:36 -07:00
* card has just reset and the NORXflag is on until
* we tell it to start receiving .
*/
airqmask = probe_irq_on ( ) ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( NORXflag , ioaddr , COM9026_REG_W_INTMASK ) ;
2005-04-16 15:20:36 -07:00
udelay ( 1 ) ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( 0 , ioaddr , COM9026_REG_W_INTMASK ) ;
2005-04-16 15:20:36 -07:00
dev - > irq = probe_irq_off ( airqmask ) ;
2010-07-17 07:21:28 +00:00
if ( ( int ) dev - > irq < = 0 ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT_REASONS , dev , " Autoprobe IRQ failed \n " ) ;
2005-04-16 15:20:36 -07:00
goto err_out ;
}
}
release_region ( ioaddr , ARCNET_TOTAL_SIZE ) ; /* end of probing */
return com90io_found ( dev ) ;
err_out :
release_region ( ioaddr , ARCNET_TOTAL_SIZE ) ;
return - ENODEV ;
}
/* Set up the struct net_device associated with this card. Called after
* probing succeeds .
*/
static int __init com90io_found ( struct net_device * dev )
{
struct arcnet_local * lp ;
int ioaddr = dev - > base_addr ;
int err ;
/* Reserve the irq */
2015-05-05 10:06:02 -07:00
if ( request_irq ( dev - > irq , arcnet_interrupt , 0 ,
" arcnet (COM90xx-IO) " , dev ) ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_NORMAL , dev , " Can't get IRQ %d! \n " , dev - > irq ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
2005-09-13 01:25:15 -07:00
/* Reserve the I/O region */
2015-05-05 10:06:02 -07:00
if ( ! request_region ( dev - > base_addr , ARCNET_TOTAL_SIZE ,
" arcnet (COM90xx-IO) " ) ) {
2005-04-16 15:20:36 -07:00
free_irq ( dev - > irq , dev ) ;
return - EBUSY ;
}
2008-11-12 23:37:49 -08:00
lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
lp - > card_name = " COM90xx I/O " ;
lp - > hw . command = com90io_command ;
lp - > hw . status = com90io_status ;
lp - > hw . intmask = com90io_setmask ;
lp - > hw . reset = com90io_reset ;
lp - > hw . owner = THIS_MODULE ;
lp - > hw . copy_to_card = com90io_copy_to_card ;
lp - > hw . copy_from_card = com90io_copy_from_card ;
lp - > config = ( 0x16 | IOMAPflag ) & ~ ENABLE16flag ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( lp - > config , ioaddr , COM9026_REG_RW_CONFIG ) ;
2005-04-16 15:20:36 -07:00
/* get and check the station ID from offset 1 in shmem */
dev - > dev_addr [ 0 ] = get_buffer_byte ( dev , 1 ) ;
err = register_netdev ( dev ) ;
if ( err ) {
2015-05-05 10:06:07 -07:00
arcnet_outb ( arcnet_inb ( ioaddr , COM9026_REG_RW_CONFIG ) & ~ IOMAPflag ,
ioaddr , COM9026_REG_RW_CONFIG ) ;
2005-04-16 15:20:36 -07:00
free_irq ( dev - > irq , dev ) ;
release_region ( dev - > base_addr , ARCNET_TOTAL_SIZE ) ;
return err ;
}
2015-05-05 10:05:55 -07:00
arc_printk ( D_NORMAL , dev , " COM90IO: station %02Xh found at %03lXh, IRQ %d. \n " ,
dev - > dev_addr [ 0 ] , dev - > base_addr , dev - > irq ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2015-05-05 10:05:52 -07:00
/* Do a hardware reset on the card, and set up necessary registers.
2005-04-16 15:20:36 -07:00
*
* This should be called as little as possible , because it disrupts the
* token on the network ( causes a RECON ) and requires a significant delay .
*
* However , it does make sure the card is in a defined state .
*/
static int com90io_reset ( struct net_device * dev , int really_reset )
{
2008-11-12 23:37:49 -08:00
struct arcnet_local * lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
short ioaddr = dev - > base_addr ;
2015-05-05 10:05:55 -07:00
arc_printk ( D_INIT , dev , " Resetting %s (status=%02Xh) \n " ,
2015-05-05 10:06:07 -07:00
dev - > name , arcnet_inb ( ioaddr , COM9026_REG_R_STATUS ) ) ;
2005-04-16 15:20:36 -07:00
if ( really_reset ) {
/* reset the card */
2015-05-05 10:06:07 -07:00
arcnet_inb ( ioaddr , COM9026_REG_R_RESET ) ;
2005-04-16 15:20:36 -07:00
mdelay ( RESETtime ) ;
}
/* Set the thing to IO-mapped, 8-bit mode */
lp - > config = ( 0x1C | IOMAPflag ) & ~ ENABLE16flag ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( lp - > config , ioaddr , COM9026_REG_RW_CONFIG ) ;
2005-04-16 15:20:36 -07:00
2015-05-05 10:06:07 -07:00
arcnet_outb ( CFLAGScmd | RESETclear , ioaddr , COM9026_REG_W_COMMAND ) ;
/* clear flags & end reset */
arcnet_outb ( CFLAGScmd | CONFIGclear , ioaddr , COM9026_REG_W_COMMAND ) ;
2005-04-16 15:20:36 -07:00
/* verify that the ARCnet signature byte is present */
if ( get_buffer_byte ( dev , 0 ) ! = TESTvalue ) {
2015-05-05 10:05:55 -07:00
arc_printk ( D_NORMAL , dev , " reset failed: TESTvalue not present. \n " ) ;
2005-04-16 15:20:36 -07:00
return 1 ;
}
/* enable extended (512-byte) packets */
2015-05-05 10:06:07 -07:00
arcnet_outb ( CONFIGcmd | EXTconf , ioaddr , COM9026_REG_W_COMMAND ) ;
2005-04-16 15:20:36 -07:00
/* done! return success. */
return 0 ;
}
static void com90io_command ( struct net_device * dev , int cmd )
{
short ioaddr = dev - > base_addr ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( cmd , ioaddr , COM9026_REG_W_COMMAND ) ;
2005-04-16 15:20:36 -07:00
}
static int com90io_status ( struct net_device * dev )
{
short ioaddr = dev - > base_addr ;
2015-05-05 10:06:07 -07:00
return arcnet_inb ( ioaddr , COM9026_REG_R_STATUS ) ;
2005-04-16 15:20:36 -07:00
}
static void com90io_setmask ( struct net_device * dev , int mask )
{
short ioaddr = dev - > base_addr ;
2015-05-05 10:06:07 -07:00
arcnet_outb ( mask , ioaddr , COM9026_REG_W_INTMASK ) ;
2005-04-16 15:20:36 -07:00
}
2015-05-05 10:06:02 -07:00
static void com90io_copy_to_card ( struct net_device * dev , int bufnum ,
int offset , void * buf , int count )
2005-04-16 15:20:36 -07:00
{
2015-05-05 10:05:55 -07:00
TIME ( dev , " put_whole_buffer " , count ,
put_whole_buffer ( dev , bufnum * 512 + offset , count , buf ) ) ;
2005-04-16 15:20:36 -07:00
}
2015-05-05 10:06:02 -07:00
static void com90io_copy_from_card ( struct net_device * dev , int bufnum ,
int offset , void * buf , int count )
2005-04-16 15:20:36 -07:00
{
2015-05-05 10:05:55 -07:00
TIME ( dev , " get_whole_buffer " , count ,
get_whole_buffer ( dev , bufnum * 512 + offset , count , buf ) ) ;
2005-04-16 15:20:36 -07:00
}
static int io ; /* use the insmod io= irq= shmem= options */
static int irq ;
static char device [ 9 ] ; /* use eg. device=arc1 to change name */
2017-04-04 16:54:25 +01:00
module_param_hw ( io , int , ioport , 0 ) ;
module_param_hw ( irq , int , irq , 0 ) ;
2005-04-16 15:20:36 -07:00
module_param_string ( device , device , sizeof ( device ) , 0 ) ;
MODULE_LICENSE ( " GPL " ) ;
# ifndef MODULE
static int __init com90io_setup ( char * s )
{
int ints [ 4 ] ;
2015-05-05 10:05:48 -07:00
2005-04-16 15:20:36 -07:00
s = get_options ( s , 4 , ints ) ;
if ( ! ints [ 0 ] )
return 0 ;
switch ( ints [ 0 ] ) {
default : /* ERROR */
2015-05-05 10:05:56 -07:00
pr_err ( " Too many arguments \n " ) ;
2005-04-16 15:20:36 -07:00
case 2 : /* IRQ */
irq = ints [ 2 ] ;
case 1 : /* IO address */
io = ints [ 1 ] ;
}
if ( * s )
snprintf ( device , sizeof ( device ) , " %s " , s ) ;
return 1 ;
}
__setup ( " com90io= " , com90io_setup ) ;
# endif
static struct net_device * my_dev ;
static int __init com90io_init ( void )
{
struct net_device * dev ;
int err ;
dev = alloc_arcdev ( device ) ;
if ( ! dev )
return - ENOMEM ;
dev - > base_addr = io ;
dev - > irq = irq ;
if ( dev - > irq = = 2 )
dev - > irq = 9 ;
err = com90io_probe ( dev ) ;
if ( err ) {
free_netdev ( dev ) ;
return err ;
}
my_dev = dev ;
return 0 ;
}
static void __exit com90io_exit ( void )
{
struct net_device * dev = my_dev ;
int ioaddr = dev - > base_addr ;
unregister_netdev ( dev ) ;
2015-05-05 10:06:02 -07:00
/* In case the old driver is loaded later,
* set the thing back to MMAP mode
*/
2015-05-05 10:06:07 -07:00
arcnet_outb ( arcnet_inb ( ioaddr , COM9026_REG_RW_CONFIG ) & ~ IOMAPflag ,
ioaddr , COM9026_REG_RW_CONFIG ) ;
2005-04-16 15:20:36 -07:00
free_irq ( dev - > irq , dev ) ;
release_region ( dev - > base_addr , ARCNET_TOTAL_SIZE ) ;
free_netdev ( dev ) ;
}
module_init ( com90io_init )
module_exit ( com90io_exit )