2005-04-17 02:20:36 +04:00
/*
* SDL Inc . RISCom / N2 synchronous serial card driver for Linux
*
* Copyright ( C ) 1998 - 2003 Krzysztof Halasa < khc @ pm . waw . pl >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation .
*
2006-06-26 23:36:52 +04:00
* For information see < http : //www.kernel.org/pub/linux/utils/net/hdlc/>
2005-04-17 02:20:36 +04:00
*
* Note : integrated CSU / DSU / DDS are not supported by this driver
*
* Sources of information :
* Hitachi HD64570 SCA User ' s Manual
* SDL Inc . PPP / HDLC / CISCO driver
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/in.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/moduleparam.h>
# include <linux/netdevice.h>
# include <linux/hdlc.h>
# include <asm/io.h>
# include "hd64570.h"
static const char * version = " SDL RISCom/N2 driver version: 1.15 " ;
static const char * devname = " RISCom/N2 " ;
# undef DEBUG_PKT
# define DEBUG_RINGS
# define USE_WINDOWSIZE 16384
# define USE_BUS16BITS 1
# define CLOCK_BASE 9830400 /* 9.8304 MHz */
# define MAX_PAGES 16 /* 16 RAM pages at max */
# define MAX_RAM_SIZE 0x80000 /* 512 KB */
# if MAX_RAM_SIZE > MAX_PAGES * USE_WINDOWSIZE
# undef MAX_RAM_SIZE
# define MAX_RAM_SIZE (MAX_PAGES * USE_WINDOWSIZE)
# endif
# define N2_IOPORTS 0x10
# define NEED_DETECT_RAM
# define NEED_SCA_MSCI_INTR
# define MAX_TX_BUFFERS 10
static char * hw = NULL ; /* pointer to hw=xxx command line string */
/* RISCom/N2 Board Registers */
/* PC Control Register */
# define N2_PCR 0
# define PCR_RUNSCA 1 /* Run 64570 */
# define PCR_VPM 2 /* Enable VPM - needed if using RAM above 1 MB */
# define PCR_ENWIN 4 /* Open window */
# define PCR_BUS16 8 /* 16-bit bus */
/* Memory Base Address Register */
# define N2_BAR 2
/* Page Scan Register */
# define N2_PSR 4
# define WIN16K 0x00
# define WIN32K 0x20
# define WIN64K 0x40
# define PSR_WINBITS 0x60
# define PSR_DMAEN 0x80
# define PSR_PAGEBITS 0x0F
/* Modem Control Reg */
# define N2_MCR 6
# define CLOCK_OUT_PORT1 0x80
# define CLOCK_OUT_PORT0 0x40
# define TX422_PORT1 0x20
# define TX422_PORT0 0x10
# define DSR_PORT1 0x08
# define DSR_PORT0 0x04
# define DTR_PORT1 0x02
# define DTR_PORT0 0x01
typedef struct port_s {
struct net_device * dev ;
struct card_s * card ;
spinlock_t lock ; /* TX lock */
sync_serial_settings settings ;
int valid ; /* port enabled */
int rxpart ; /* partial frame received, next frame invalid*/
unsigned short encoding ;
unsigned short parity ;
u16 rxin ; /* rx ring buffer 'in' pointer */
u16 txin ; /* tx ring buffer 'in' and 'last' pointers */
u16 txlast ;
u8 rxs , txs , tmc ; /* SCA registers */
u8 phy_node ; /* physical port # - 0 or 1 */
u8 log_node ; /* logical port # */
} port_t ;
typedef struct card_s {
u8 __iomem * winbase ; /* ISA window base address */
u32 phy_winbase ; /* ISA physical base address */
u32 ram_size ; /* number of bytes */
u16 io ; /* IO Base address */
u16 buff_offset ; /* offset of first buffer of first channel */
u16 rx_ring_buffers ; /* number of buffers in a ring */
u16 tx_ring_buffers ;
u8 irq ; /* IRQ (3-15) */
port_t ports [ 2 ] ;
struct card_s * next_card ;
} card_t ;
static card_t * first_card ;
static card_t * * new_card = & first_card ;
# define sca_reg(reg, card) (0x8000 | (card)->io | \
( ( reg ) & 0x0F ) | ( ( ( reg ) & 0xF0 ) < < 6 ) )
# define sca_in(reg, card) inb(sca_reg(reg, card))
# define sca_out(value, reg, card) outb(value, sca_reg(reg, card))
# define sca_inw(reg, card) inw(sca_reg(reg, card))
# define sca_outw(value, reg, card) outw(value, sca_reg(reg, card))
# define port_to_card(port) ((port)->card)
# define log_node(port) ((port)->log_node)
# define phy_node(port) ((port)->phy_node)
# define winsize(card) (USE_WINDOWSIZE)
# define winbase(card) ((card)->winbase)
# define get_port(card, port) ((card)->ports[port].valid ? \
& ( card ) - > ports [ port ] : NULL )
static __inline__ u8 sca_get_page ( card_t * card )
{
return inb ( card - > io + N2_PSR ) & PSR_PAGEBITS ;
}
static __inline__ void openwin ( card_t * card , u8 page )
{
u8 psr = inb ( card - > io + N2_PSR ) ;
outb ( ( psr & ~ PSR_PAGEBITS ) | page , card - > io + N2_PSR ) ;
}
# include "hd6457x.c"
static void n2_set_iface ( port_t * port )
{
card_t * card = port - > card ;
int io = card - > io ;
u8 mcr = inb ( io + N2_MCR ) ;
u8 msci = get_msci ( port ) ;
u8 rxs = port - > rxs & CLK_BRG_MASK ;
u8 txs = port - > txs & CLK_BRG_MASK ;
switch ( port - > settings . clock_type ) {
case CLOCK_INT :
mcr | = port - > phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0 ;
rxs | = CLK_BRG_RX ; /* BRG output */
txs | = CLK_RXCLK_TX ; /* RX clock */
break ;
case CLOCK_TXINT :
mcr | = port - > phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0 ;
rxs | = CLK_LINE_RX ; /* RXC input */
txs | = CLK_BRG_TX ; /* BRG output */
break ;
case CLOCK_TXFROMRX :
mcr | = port - > phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0 ;
rxs | = CLK_LINE_RX ; /* RXC input */
txs | = CLK_RXCLK_TX ; /* RX clock */
break ;
default : /* Clock EXTernal */
mcr & = port - > phy_node ? ~ CLOCK_OUT_PORT1 : ~ CLOCK_OUT_PORT0 ;
rxs | = CLK_LINE_RX ; /* RXC input */
txs | = CLK_LINE_TX ; /* TXC input */
}
outb ( mcr , io + N2_MCR ) ;
port - > rxs = rxs ;
port - > txs = txs ;
sca_out ( rxs , msci + RXS , card ) ;
sca_out ( txs , msci + TXS , card ) ;
sca_set_port ( port ) ;
}
static int n2_open ( struct net_device * dev )
{
port_t * port = dev_to_port ( dev ) ;
int io = port - > card - > io ;
u8 mcr = inb ( io + N2_MCR ) | ( port - > phy_node ? TX422_PORT1 : TX422_PORT0 ) ;
int result ;
result = hdlc_open ( dev ) ;
if ( result )
return result ;
mcr & = port - > phy_node ? ~ DTR_PORT1 : ~ DTR_PORT0 ; /* set DTR ON */
outb ( mcr , io + N2_MCR ) ;
outb ( inb ( io + N2_PCR ) | PCR_ENWIN , io + N2_PCR ) ; /* open window */
outb ( inb ( io + N2_PSR ) | PSR_DMAEN , io + N2_PSR ) ; /* enable dma */
sca_open ( dev ) ;
n2_set_iface ( port ) ;
return 0 ;
}
static int n2_close ( struct net_device * dev )
{
port_t * port = dev_to_port ( dev ) ;
int io = port - > card - > io ;
u8 mcr = inb ( io + N2_MCR ) | ( port - > phy_node ? TX422_PORT1 : TX422_PORT0 ) ;
sca_close ( dev ) ;
mcr | = port - > phy_node ? DTR_PORT1 : DTR_PORT0 ; /* set DTR OFF */
outb ( mcr , io + N2_MCR ) ;
hdlc_close ( dev ) ;
return 0 ;
}
static int n2_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
const size_t size = sizeof ( sync_serial_settings ) ;
sync_serial_settings new_line ;
sync_serial_settings __user * line = ifr - > ifr_settings . ifs_ifsu . sync ;
port_t * port = dev_to_port ( dev ) ;
# ifdef DEBUG_RINGS
if ( cmd = = SIOCDEVPRIVATE ) {
sca_dump_rings ( dev ) ;
return 0 ;
}
# endif
if ( cmd ! = SIOCWANDEV )
return hdlc_ioctl ( dev , ifr , cmd ) ;
switch ( ifr - > ifr_settings . type ) {
case IF_GET_IFACE :
ifr - > ifr_settings . type = IF_IFACE_SYNC_SERIAL ;
if ( ifr - > ifr_settings . size < size ) {
ifr - > ifr_settings . size = size ; /* data size wanted */
return - ENOBUFS ;
}
if ( copy_to_user ( line , & port - > settings , size ) )
return - EFAULT ;
return 0 ;
case IF_IFACE_SYNC_SERIAL :
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
if ( copy_from_user ( & new_line , line , size ) )
return - EFAULT ;
if ( new_line . clock_type ! = CLOCK_EXT & &
new_line . clock_type ! = CLOCK_TXFROMRX & &
new_line . clock_type ! = CLOCK_INT & &
new_line . clock_type ! = CLOCK_TXINT )
return - EINVAL ; /* No such clock setting */
if ( new_line . loopback ! = 0 & & new_line . loopback ! = 1 )
return - EINVAL ;
memcpy ( & port - > settings , & new_line , size ) ; /* Update settings */
n2_set_iface ( port ) ;
return 0 ;
default :
return hdlc_ioctl ( dev , ifr , cmd ) ;
}
}
static void n2_destroy_card ( card_t * card )
{
int cnt ;
for ( cnt = 0 ; cnt < 2 ; cnt + + )
if ( card - > ports [ cnt ] . card ) {
struct net_device * dev = port_to_dev ( & card - > ports [ cnt ] ) ;
unregister_hdlc_device ( dev ) ;
}
if ( card - > irq )
free_irq ( card - > irq , card ) ;
if ( card - > winbase ) {
iounmap ( card - > winbase ) ;
release_mem_region ( card - > phy_winbase , USE_WINDOWSIZE ) ;
}
if ( card - > io )
release_region ( card - > io , N2_IOPORTS ) ;
if ( card - > ports [ 0 ] . dev )
free_netdev ( card - > ports [ 0 ] . dev ) ;
if ( card - > ports [ 1 ] . dev )
free_netdev ( card - > ports [ 1 ] . dev ) ;
kfree ( card ) ;
}
static int __init n2_run ( unsigned long io , unsigned long irq ,
unsigned long winbase , long valid0 , long valid1 )
{
card_t * card ;
u8 cnt , pcr ;
int i ;
if ( io < 0x200 | | io > 0x3FF | | ( io % N2_IOPORTS ) ! = 0 ) {
printk ( KERN_ERR " n2: invalid I/O port value \n " ) ;
return - ENODEV ;
}
if ( irq < 3 | | irq > 15 | | irq = = 6 ) /* FIXME */ {
printk ( KERN_ERR " n2: invalid IRQ value \n " ) ;
return - ENODEV ;
}
if ( winbase < 0xA0000 | | winbase > 0xFFFFF | | ( winbase & 0xFFF ) ! = 0 ) {
printk ( KERN_ERR " n2: invalid RAM value \n " ) ;
return - ENODEV ;
}
card = kmalloc ( sizeof ( card_t ) , GFP_KERNEL ) ;
if ( card = = NULL ) {
printk ( KERN_ERR " n2: unable to allocate memory \n " ) ;
return - ENOBUFS ;
}
memset ( card , 0 , sizeof ( card_t ) ) ;
card - > ports [ 0 ] . dev = alloc_hdlcdev ( & card - > ports [ 0 ] ) ;
card - > ports [ 1 ] . dev = alloc_hdlcdev ( & card - > ports [ 1 ] ) ;
if ( ! card - > ports [ 0 ] . dev | | ! card - > ports [ 1 ] . dev ) {
printk ( KERN_ERR " n2: unable to allocate memory \n " ) ;
n2_destroy_card ( card ) ;
return - ENOMEM ;
}
if ( ! request_region ( io , N2_IOPORTS , devname ) ) {
printk ( KERN_ERR " n2: I/O port region in use \n " ) ;
n2_destroy_card ( card ) ;
return - EBUSY ;
}
card - > io = io ;
if ( request_irq ( irq , & sca_intr , 0 , devname , card ) ) {
printk ( KERN_ERR " n2: could not allocate IRQ \n " ) ;
n2_destroy_card ( card ) ;
return ( - EBUSY ) ;
}
card - > irq = irq ;
if ( ! request_mem_region ( winbase , USE_WINDOWSIZE , devname ) ) {
printk ( KERN_ERR " n2: could not request RAM window \n " ) ;
n2_destroy_card ( card ) ;
return ( - EBUSY ) ;
}
card - > phy_winbase = winbase ;
card - > winbase = ioremap ( winbase , USE_WINDOWSIZE ) ;
2006-06-23 00:29:28 +04:00
if ( ! card - > winbase ) {
printk ( KERN_ERR " n2: ioremap() failed \n " ) ;
n2_destroy_card ( card ) ;
return - EFAULT ;
}
2005-04-17 02:20:36 +04:00
outb ( 0 , io + N2_PCR ) ;
outb ( winbase > > 12 , io + N2_BAR ) ;
switch ( USE_WINDOWSIZE ) {
case 16384 :
outb ( WIN16K , io + N2_PSR ) ;
break ;
case 32768 :
outb ( WIN32K , io + N2_PSR ) ;
break ;
case 65536 :
outb ( WIN64K , io + N2_PSR ) ;
break ;
default :
printk ( KERN_ERR " n2: invalid window size \n " ) ;
n2_destroy_card ( card ) ;
return - ENODEV ;
}
pcr = PCR_ENWIN | PCR_VPM | ( USE_BUS16BITS ? PCR_BUS16 : 0 ) ;
outb ( pcr , io + N2_PCR ) ;
card - > ram_size = sca_detect_ram ( card , card - > winbase , MAX_RAM_SIZE ) ;
/* number of TX + RX buffers for one port */
i = card - > ram_size / ( ( valid0 + valid1 ) * ( sizeof ( pkt_desc ) +
HDLC_MAX_MRU ) ) ;
card - > tx_ring_buffers = min ( i / 2 , MAX_TX_BUFFERS ) ;
card - > rx_ring_buffers = i - card - > tx_ring_buffers ;
card - > buff_offset = ( valid0 + valid1 ) * sizeof ( pkt_desc ) *
( card - > tx_ring_buffers + card - > rx_ring_buffers ) ;
printk ( KERN_INFO " n2: RISCom/N2 %u KB RAM, IRQ%u, "
" using %u TX + %u RX packets rings \n " , card - > ram_size / 1024 ,
card - > irq , card - > tx_ring_buffers , card - > rx_ring_buffers ) ;
if ( card - > tx_ring_buffers < 1 ) {
printk ( KERN_ERR " n2: RAM test failed \n " ) ;
n2_destroy_card ( card ) ;
return - EIO ;
}
pcr | = PCR_RUNSCA ; /* run SCA */
outb ( pcr , io + N2_PCR ) ;
outb ( 0 , io + N2_MCR ) ;
sca_init ( card , 0 ) ;
for ( cnt = 0 ; cnt < 2 ; cnt + + ) {
port_t * port = & card - > ports [ cnt ] ;
struct net_device * dev = port_to_dev ( port ) ;
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
if ( ( cnt = = 0 & & ! valid0 ) | | ( cnt = = 1 & & ! valid1 ) )
continue ;
port - > phy_node = cnt ;
port - > valid = 1 ;
if ( ( cnt = = 1 ) & & valid0 )
port - > log_node = 1 ;
spin_lock_init ( & port - > lock ) ;
SET_MODULE_OWNER ( dev ) ;
dev - > irq = irq ;
dev - > mem_start = winbase ;
dev - > mem_end = winbase + USE_WINDOWSIZE - 1 ;
dev - > tx_queue_len = 50 ;
dev - > do_ioctl = n2_ioctl ;
dev - > open = n2_open ;
dev - > stop = n2_close ;
hdlc - > attach = sca_attach ;
hdlc - > xmit = sca_xmit ;
port - > settings . clock_type = CLOCK_EXT ;
port - > card = card ;
if ( register_hdlc_device ( dev ) ) {
printk ( KERN_WARNING " n2: unable to register hdlc "
" device \n " ) ;
port - > card = NULL ;
n2_destroy_card ( card ) ;
return - ENOBUFS ;
}
sca_init_sync_port ( port ) ; /* Set up SCA memory */
printk ( KERN_INFO " %s: RISCom/N2 node %d \n " ,
dev - > name , port - > phy_node ) ;
}
* new_card = card ;
new_card = & card - > next_card ;
return 0 ;
}
static int __init n2_init ( void )
{
if ( hw = = NULL ) {
# ifdef MODULE
printk ( KERN_INFO " n2: no card initialized \n " ) ;
# endif
return - ENOSYS ; /* no parameters specified, abort */
}
printk ( KERN_INFO " %s \n " , version ) ;
do {
unsigned long io , irq , ram ;
long valid [ 2 ] = { 0 , 0 } ; /* Default = both ports disabled */
io = simple_strtoul ( hw , & hw , 0 ) ;
if ( * hw + + ! = ' , ' )
break ;
irq = simple_strtoul ( hw , & hw , 0 ) ;
if ( * hw + + ! = ' , ' )
break ;
ram = simple_strtoul ( hw , & hw , 0 ) ;
if ( * hw + + ! = ' , ' )
break ;
while ( 1 ) {
if ( * hw = = ' 0 ' & & ! valid [ 0 ] )
valid [ 0 ] = 1 ; /* Port 0 enabled */
else if ( * hw = = ' 1 ' & & ! valid [ 1 ] )
valid [ 1 ] = 1 ; /* Port 1 enabled */
else
break ;
hw + + ;
}
if ( ! valid [ 0 ] & & ! valid [ 1 ] )
break ; /* at least one port must be used */
if ( * hw = = ' : ' | | * hw = = ' \x0 ' )
n2_run ( io , irq , ram , valid [ 0 ] , valid [ 1 ] ) ;
if ( * hw = = ' \x0 ' )
return first_card ? 0 : - ENOSYS ;
} while ( * hw + + = = ' : ' ) ;
printk ( KERN_ERR " n2: invalid hardware parameters \n " ) ;
return first_card ? 0 : - ENOSYS ;
}
static void __exit n2_cleanup ( void )
{
card_t * card = first_card ;
while ( card ) {
card_t * ptr = card ;
card = card - > next_card ;
n2_destroy_card ( ptr ) ;
}
}
module_init ( n2_init ) ;
module_exit ( n2_cleanup ) ;
MODULE_AUTHOR ( " Krzysztof Halasa <khc@pm.waw.pl> " ) ;
MODULE_DESCRIPTION ( " RISCom/N2 serial port driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2006-07-22 01:41:36 +04:00
module_param ( hw , charp , 0444 ) ;
MODULE_PARM_DESC ( hw , " io,irq,ram,ports:io,irq,... " ) ;