2005-04-16 15:20:36 -07:00
/*
* Sealevel Systems 4021 driver .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* ( c ) Copyright 1999 , 2001 Alan Cox
* ( c ) Copyright 2001 Red Hat Inc .
2008-07-02 17:47:52 +02:00
* Generic HDLC port Copyright ( C ) 2008 Krzysztof Halasa < khc @ pm . waw . pl >
2005-04-16 15:20:36 -07:00
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/net.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/if_arp.h>
# include <linux/delay.h>
2008-07-02 17:47:52 +02:00
# include <linux/hdlc.h>
2005-04-16 15:20:36 -07:00
# include <linux/ioport.h>
# include <linux/init.h>
# include <net/arp.h>
2005-12-06 05:53:04 -05:00
# include <asm/irq.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <asm/dma.h>
# include <asm/byteorder.h>
# include "z85230.h"
struct slvl_device
{
struct z8530_channel * chan ;
int channel ;
} ;
struct slvl_board
{
2008-07-02 17:47:52 +02:00
struct slvl_device dev [ 2 ] ;
2005-04-16 15:20:36 -07:00
struct z8530_dev board ;
int iobase ;
} ;
/*
* Network driver support routines
*/
2008-07-02 17:47:52 +02:00
static inline struct slvl_device * dev_to_chan ( struct net_device * dev )
{
return ( struct slvl_device * ) dev_to_hdlc ( dev ) - > priv ;
}
2005-04-16 15:20:36 -07:00
/*
2008-07-02 17:47:52 +02:00
* Frame receive . Simple for our card as we do HDLC and there
2005-04-16 15:20:36 -07:00
* is no funny garbage involved
*/
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
static void sealevel_input ( struct z8530_channel * c , struct sk_buff * skb )
{
/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
2008-07-02 17:47:52 +02:00
skb_trim ( skb , skb - > len - 2 ) ;
skb - > protocol = hdlc_type_trans ( skb , c - > netdevice ) ;
2007-03-19 15:33:04 -07:00
skb_reset_mac_header ( skb ) ;
2008-07-02 17:47:52 +02:00
skb - > dev = c - > netdevice ;
2005-04-16 15:20:36 -07:00
netif_rx ( skb ) ;
}
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
/*
* We ' ve been placed in the UP state
2008-07-02 17:47:52 +02:00
*/
2005-04-16 15:20:36 -07:00
static int sealevel_open ( struct net_device * d )
{
2008-07-02 17:47:52 +02:00
struct slvl_device * slvl = dev_to_chan ( d ) ;
2005-04-16 15:20:36 -07:00
int err = - 1 ;
int unit = slvl - > channel ;
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
/*
2008-07-02 17:47:52 +02:00
* Link layer up .
2005-04-16 15:20:36 -07:00
*/
2009-12-02 01:26:01 -08:00
switch ( unit ) {
2005-04-16 15:20:36 -07:00
case 0 :
2008-07-02 17:47:52 +02:00
err = z8530_sync_dma_open ( d , slvl - > chan ) ;
2005-04-16 15:20:36 -07:00
break ;
case 1 :
2008-07-02 17:47:52 +02:00
err = z8530_sync_open ( d , slvl - > chan ) ;
2005-04-16 15:20:36 -07:00
break ;
}
2008-07-02 17:47:52 +02:00
if ( err )
2005-04-16 15:20:36 -07:00
return err ;
2008-07-02 17:47:52 +02:00
err = hdlc_open ( d ) ;
if ( err ) {
switch ( unit ) {
2005-04-16 15:20:36 -07:00
case 0 :
z8530_sync_dma_close ( d , slvl - > chan ) ;
break ;
case 1 :
z8530_sync_close ( d , slvl - > chan ) ;
break ;
2008-07-02 17:47:52 +02:00
}
2005-04-16 15:20:36 -07:00
return err ;
}
2008-07-02 17:47:52 +02:00
slvl - > chan - > rx_function = sealevel_input ;
2005-04-16 15:20:36 -07:00
/*
* Go go go
*/
netif_start_queue ( d ) ;
return 0 ;
}
static int sealevel_close ( struct net_device * d )
{
2008-07-02 17:47:52 +02:00
struct slvl_device * slvl = dev_to_chan ( d ) ;
2005-04-16 15:20:36 -07:00
int unit = slvl - > channel ;
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
/*
* Discard new frames
*/
2008-07-02 17:47:52 +02:00
slvl - > chan - > rx_function = z8530_null_rx ;
hdlc_close ( d ) ;
2005-04-16 15:20:36 -07:00
netif_stop_queue ( d ) ;
2008-07-02 17:47:52 +02:00
2009-12-02 01:26:01 -08:00
switch ( unit ) {
2005-04-16 15:20:36 -07:00
case 0 :
z8530_sync_dma_close ( d , slvl - > chan ) ;
break ;
case 1 :
z8530_sync_close ( d , slvl - > chan ) ;
break ;
}
return 0 ;
}
static int sealevel_ioctl ( struct net_device * d , struct ifreq * ifr , int cmd )
{
2008-07-02 17:47:52 +02:00
/* struct slvl_device *slvl=dev_to_chan(d);
2005-04-16 15:20:36 -07:00
z8530_ioctl ( d , & slvl - > sync . chanA , ifr , cmd ) */
2008-07-02 17:47:52 +02:00
return hdlc_ioctl ( d , ifr , cmd ) ;
2005-04-16 15:20:36 -07:00
}
/*
2008-07-02 17:47:52 +02:00
* Passed network frames , fire them downwind .
2005-04-16 15:20:36 -07:00
*/
2008-07-02 17:47:52 +02:00
2009-08-31 19:50:47 +00:00
static netdev_tx_t sealevel_queue_xmit ( struct sk_buff * skb ,
struct net_device * d )
2005-04-16 15:20:36 -07:00
{
2008-07-02 17:47:52 +02:00
return z8530_queue_xmit ( dev_to_chan ( d ) - > chan , skb ) ;
2005-04-16 15:20:36 -07:00
}
2008-07-02 17:47:52 +02:00
static int sealevel_attach ( struct net_device * dev , unsigned short encoding ,
unsigned short parity )
2005-04-16 15:20:36 -07:00
{
2008-07-02 17:47:52 +02:00
if ( encoding = = ENCODING_NRZ & & parity = = PARITY_CRC16_PR1_CCITT )
return 0 ;
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2009-01-08 22:52:11 +01:00
static const struct net_device_ops sealevel_ops = {
. ndo_open = sealevel_open ,
. ndo_stop = sealevel_close ,
. ndo_change_mtu = hdlc_change_mtu ,
. ndo_start_xmit = hdlc_start_xmit ,
. ndo_do_ioctl = sealevel_ioctl ,
} ;
2008-07-02 17:47:52 +02:00
static int slvl_setup ( struct slvl_device * sv , int iobase , int irq )
2005-04-16 15:20:36 -07:00
{
2008-07-02 17:47:52 +02:00
struct net_device * dev = alloc_hdlcdev ( sv ) ;
if ( ! dev )
return - 1 ;
dev_to_hdlc ( dev ) - > attach = sealevel_attach ;
dev_to_hdlc ( dev ) - > xmit = sealevel_queue_xmit ;
2009-01-08 22:52:11 +01:00
dev - > netdev_ops = & sealevel_ops ;
2008-07-02 17:47:52 +02:00
dev - > base_addr = iobase ;
dev - > irq = irq ;
if ( register_hdlc_device ( dev ) ) {
printk ( KERN_ERR " sealevel: unable to register HDLC device \n " ) ;
free_netdev ( dev ) ;
return - 1 ;
2005-04-16 15:20:36 -07:00
}
2008-07-02 17:47:52 +02:00
sv - > chan - > netdevice = dev ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/*
* Allocate and setup Sealevel board .
*/
2008-07-02 17:47:52 +02:00
static __init struct slvl_board * slvl_init ( int iobase , int irq ,
2005-04-16 15:20:36 -07:00
int txdma , int rxdma , int slow )
{
struct z8530_dev * dev ;
struct slvl_board * b ;
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
/*
* Get the needed I / O space
*/
2008-07-02 17:47:52 +02:00
if ( ! request_region ( iobase , 8 , " Sealevel 4021 " ) ) {
printk ( KERN_WARNING " sealevel: I/O 0x%X already in use. \n " ,
iobase ) ;
2005-04-16 15:20:36 -07:00
return NULL ;
}
2008-07-02 17:47:52 +02:00
b = kzalloc ( sizeof ( struct slvl_board ) , GFP_KERNEL ) ;
if ( ! b )
goto err_kzalloc ;
2005-04-16 15:20:36 -07:00
2008-07-02 17:47:52 +02:00
b - > dev [ 0 ] . chan = & b - > board . chanA ;
b - > dev [ 0 ] . channel = 0 ;
2005-04-16 15:20:36 -07:00
2008-07-02 17:47:52 +02:00
b - > dev [ 1 ] . chan = & b - > board . chanB ;
b - > dev [ 1 ] . channel = 1 ;
2005-04-16 15:20:36 -07:00
dev = & b - > board ;
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
/*
* Stuff in the I / O addressing
*/
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
dev - > active = 0 ;
b - > iobase = iobase ;
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
/*
* Select 8530 delays for the old board
*/
2008-07-02 17:47:52 +02:00
if ( slow )
2005-04-16 15:20:36 -07:00
iobase | = Z8530_PORT_SLEEP ;
2008-07-02 17:47:52 +02:00
dev - > chanA . ctrlio = iobase + 1 ;
dev - > chanA . dataio = iobase ;
dev - > chanB . ctrlio = iobase + 3 ;
dev - > chanB . dataio = iobase + 2 ;
dev - > chanA . irqs = & z8530_nop ;
dev - > chanB . irqs = & z8530_nop ;
2005-04-16 15:20:36 -07:00
/*
* Assert DTR enable DMA
*/
2008-07-02 17:47:52 +02:00
outb ( 3 | ( 1 < < 7 ) , b - > iobase + 4 ) ;
2005-04-16 15:20:36 -07:00
/* We want a fast IRQ for this device. Actually we'd like an even faster
IRQ ; ) - This is one driver RtLinux is made for */
2008-07-02 17:47:52 +02:00
2009-11-18 23:29:17 -08:00
if ( request_irq ( irq , z8530_interrupt , IRQF_DISABLED ,
2008-07-02 17:47:52 +02:00
" SeaLevel " , dev ) < 0 ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_WARNING " sealevel: IRQ %d already in use. \n " , irq ) ;
2008-07-02 17:47:52 +02:00
goto err_request_irq ;
2005-04-16 15:20:36 -07:00
}
2008-07-02 17:47:52 +02:00
dev - > irq = irq ;
dev - > chanA . private = & b - > dev [ 0 ] ;
dev - > chanB . private = & b - > dev [ 1 ] ;
dev - > chanA . dev = dev ;
dev - > chanB . dev = dev ;
dev - > chanA . txdma = 3 ;
dev - > chanA . rxdma = 1 ;
if ( request_dma ( dev - > chanA . txdma , " SeaLevel (TX) " ) )
goto err_dma_tx ;
if ( request_dma ( dev - > chanA . rxdma , " SeaLevel (RX) " ) )
goto err_dma_rx ;
2005-04-16 15:20:36 -07:00
disable_irq ( irq ) ;
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
/*
* Begin normal initialise
*/
2008-07-02 17:47:52 +02:00
if ( z8530_init ( dev ) ! = 0 ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR " Z8530 series device not found. \n " ) ;
enable_irq ( irq ) ;
2008-07-02 17:47:52 +02:00
goto free_hw ;
2005-04-16 15:20:36 -07:00
}
2008-07-02 17:47:52 +02:00
if ( dev - > type = = Z85C30 ) {
2005-04-16 15:20:36 -07:00
z8530_channel_load ( & dev - > chanA , z8530_hdlc_kilostream ) ;
z8530_channel_load ( & dev - > chanB , z8530_hdlc_kilostream ) ;
2008-07-02 17:47:52 +02:00
} else {
2005-04-16 15:20:36 -07:00
z8530_channel_load ( & dev - > chanA , z8530_hdlc_kilostream_85230 ) ;
z8530_channel_load ( & dev - > chanB , z8530_hdlc_kilostream_85230 ) ;
}
/*
* Now we can take the IRQ
*/
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
enable_irq ( irq ) ;
2008-07-02 17:47:52 +02:00
if ( slvl_setup ( & b - > dev [ 0 ] , iobase , irq ) )
goto free_hw ;
if ( slvl_setup ( & b - > dev [ 1 ] , iobase , irq ) )
goto free_netdev0 ;
2005-04-16 15:20:36 -07:00
z8530_describe ( dev , " I/O " , iobase ) ;
2008-07-02 17:47:52 +02:00
dev - > active = 1 ;
2005-04-16 15:20:36 -07:00
return b ;
2008-07-02 17:47:52 +02:00
free_netdev0 :
unregister_hdlc_device ( b - > dev [ 0 ] . chan - > netdevice ) ;
free_netdev ( b - > dev [ 0 ] . chan - > netdevice ) ;
free_hw :
2005-04-16 15:20:36 -07:00
free_dma ( dev - > chanA . rxdma ) ;
2008-07-02 17:47:52 +02:00
err_dma_rx :
2005-04-16 15:20:36 -07:00
free_dma ( dev - > chanA . txdma ) ;
2008-07-02 17:47:52 +02:00
err_dma_tx :
2005-04-16 15:20:36 -07:00
free_irq ( irq , dev ) ;
2008-07-02 17:47:52 +02:00
err_request_irq :
2005-04-16 15:20:36 -07:00
kfree ( b ) ;
2008-07-02 17:47:52 +02:00
err_kzalloc :
release_region ( iobase , 8 ) ;
2005-04-16 15:20:36 -07:00
return NULL ;
}
static void __exit slvl_shutdown ( struct slvl_board * b )
{
int u ;
z8530_shutdown ( & b - > board ) ;
2008-07-02 17:47:52 +02:00
2009-12-02 01:26:01 -08:00
for ( u = 0 ; u < 2 ; u + + ) {
2008-07-02 17:47:52 +02:00
struct net_device * d = b - > dev [ u ] . chan - > netdevice ;
unregister_hdlc_device ( d ) ;
2005-04-16 15:20:36 -07:00
free_netdev ( d ) ;
}
2008-07-02 17:47:52 +02:00
2005-04-16 15:20:36 -07:00
free_irq ( b - > board . irq , & b - > board ) ;
free_dma ( b - > board . chanA . rxdma ) ;
free_dma ( b - > board . chanA . txdma ) ;
/* DMA off on the card, drop DTR */
outb ( 0 , b - > iobase ) ;
release_region ( b - > iobase , 8 ) ;
kfree ( b ) ;
}
static int io = 0x238 ;
static int txdma = 1 ;
static int rxdma = 3 ;
static int irq = 5 ;
static int slow = 0 ;
module_param ( io , int , 0 ) ;
MODULE_PARM_DESC ( io , " The I/O base of the Sealevel card " ) ;
module_param ( txdma , int , 0 ) ;
MODULE_PARM_DESC ( txdma , " Transmit DMA channel " ) ;
module_param ( rxdma , int , 0 ) ;
MODULE_PARM_DESC ( rxdma , " Receive DMA channel " ) ;
module_param ( irq , int , 0 ) ;
MODULE_PARM_DESC ( irq , " The interrupt line setting for the SeaLevel card " ) ;
module_param ( slow , bool , 0 ) ;
MODULE_PARM_DESC ( slow , " Set this for an older Sealevel card such as the 4012 " ) ;
MODULE_AUTHOR ( " Alan Cox " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Modular driver for the SeaLevel 4021 " ) ;
static struct slvl_board * slvl_unit ;
static int __init slvl_init_module ( void )
{
slvl_unit = slvl_init ( io , irq , txdma , rxdma , slow ) ;
return slvl_unit ? 0 : - ENODEV ;
}
static void __exit slvl_cleanup_module ( void )
{
2009-12-02 01:26:01 -08:00
if ( slvl_unit )
2005-04-16 15:20:36 -07:00
slvl_shutdown ( slvl_unit ) ;
}
module_init ( slvl_init_module ) ;
module_exit ( slvl_cleanup_module ) ;