2005-04-16 15:20:36 -07:00
/*
* Moxa C101 synchronous serial card driver for Linux
*
* Copyright ( C ) 2000 - 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 21:36:52 +02:00
* For information see < http : //www.kernel.org/pub/linux/utils/net/hdlc/>
2005-04-16 15:20:36 -07:00
*
* Sources of information :
* Hitachi HD64570 SCA User ' s Manual
* Moxa C101 User ' s Manual
*/
# include <linux/module.h>
# include <linux/kernel.h>
2009-10-12 16:22:46 +02:00
# include <linux/capability.h>
2005-04-16 15:20:36 -07:00
# include <linux/slab.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/moduleparam.h>
# include <linux/netdevice.h>
# include <linux/hdlc.h>
# include <linux/delay.h>
# include <asm/io.h>
# include "hd64570.h"
static const char * version = " Moxa C101 driver version: 1.15 " ;
static const char * devname = " C101 " ;
# undef DEBUG_PKT
# define DEBUG_RINGS
# define C101_PAGE 0x1D00
# define C101_DTR 0x1E00
# define C101_SCA 0x1F00
# define C101_WINDOW_SIZE 0x2000
# define C101_MAPPED_RAM_SIZE 0x4000
# define RAM_SIZE (256 * 1024)
# define TX_RING_BUFFERS 10
# define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) / \
( sizeof ( pkt_desc ) + HDLC_MAX_MRU ) - TX_RING_BUFFERS )
# define CLOCK_BASE 9830400 /* 9.8304 MHz */
# define PAGE0_ALWAYS_MAPPED
static char * hw ; /* pointer to hw=xxx command line string */
typedef struct card_s {
struct net_device * dev ;
spinlock_t lock ; /* TX lock */
u8 __iomem * win0base ; /* ISA window base address */
u32 phy_winbase ; /* ISA physical base address */
sync_serial_settings settings ;
int rxpart ; /* partial frame received, next frame invalid*/
unsigned short encoding ;
unsigned short parity ;
u16 rx_ring_buffers ; /* number of buffers in a ring */
u16 tx_ring_buffers ;
u16 buff_offset ; /* offset of first buffer of first channel */
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 irq ; /* IRQ (3-15) */
u8 page ;
struct card_s * next_card ;
} card_t ;
typedef card_t port_t ;
static card_t * first_card ;
static card_t * * new_card = & first_card ;
# define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg))
# define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg))
# define sca_inw(reg, card) readw((card)->win0base + C101_SCA + (reg))
/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */
# define sca_outw(value, reg, card) do { \
writeb ( value & 0xFF , ( card ) - > win0base + C101_SCA + ( reg ) ) ; \
2008-03-24 19:12:23 +01:00
writeb ( ( value > > 8 ) & 0xFF , ( card ) - > win0base + C101_SCA + ( reg + 1 ) ) ; \
2005-04-16 15:20:36 -07:00
} while ( 0 )
# define port_to_card(port) (port)
# define log_node(port) (0)
# define phy_node(port) (0)
# define winsize(card) (C101_WINDOW_SIZE)
# define win0base(card) ((card)->win0base)
# define winbase(card) ((card)->win0base + 0x2000)
# define get_port(card, port) (card)
static void sca_msci_intr ( port_t * port ) ;
static inline u8 sca_get_page ( card_t * card )
{
return card - > page ;
}
static inline void openwin ( card_t * card , u8 page )
{
card - > page = page ;
writeb ( page , card - > win0base + C101_PAGE ) ;
}
2008-03-24 16:39:02 +01:00
# include "hd64570.c"
2005-04-16 15:20:36 -07:00
2006-07-12 13:46:12 -07:00
static inline void set_carrier ( port_t * port )
{
2006-08-16 01:52:23 +02:00
if ( ! ( sca_in ( MSCI1_OFFSET + ST3 , port ) & ST3_DCD ) )
2006-07-12 13:46:12 -07:00
netif_carrier_on ( port_to_dev ( port ) ) ;
else
netif_carrier_off ( port_to_dev ( port ) ) ;
}
2005-04-16 15:20:36 -07:00
static void sca_msci_intr ( port_t * port )
{
2006-08-16 01:52:23 +02:00
u8 stat = sca_in ( MSCI0_OFFSET + ST1 , port ) ; /* read MSCI ST1 status */
2005-04-16 15:20:36 -07:00
2006-08-16 01:52:23 +02:00
/* Reset MSCI TX underrun and CDCD (ignored) status bit */
sca_out ( stat & ( ST1_UDRN | ST1_CDCD ) , MSCI0_OFFSET + ST1 , port ) ;
2005-04-16 15:20:36 -07:00
if ( stat & ST1_UDRN ) {
2008-06-30 23:26:53 +02:00
/* TX Underrun error detected */
port_to_dev ( port ) - > stats . tx_errors + + ;
port_to_dev ( port ) - > stats . tx_fifo_errors + + ;
2005-04-16 15:20:36 -07:00
}
2006-08-16 01:52:23 +02:00
stat = sca_in ( MSCI1_OFFSET + ST1 , port ) ; /* read MSCI1 ST1 status */
2005-04-16 15:20:36 -07:00
/* Reset MSCI CDCD status bit - uses ch#2 DCD input */
2006-07-12 13:46:12 -07:00
sca_out ( stat & ST1_CDCD , MSCI1_OFFSET + ST1 , port ) ;
2005-04-16 15:20:36 -07:00
if ( stat & ST1_CDCD )
2006-07-12 13:46:12 -07:00
set_carrier ( port ) ;
2005-04-16 15:20:36 -07:00
}
static void c101_set_iface ( port_t * port )
{
u8 rxs = port - > rxs & CLK_BRG_MASK ;
u8 txs = port - > txs & CLK_BRG_MASK ;
switch ( port - > settings . clock_type ) {
case CLOCK_INT :
rxs | = CLK_BRG_RX ; /* TX clock */
txs | = CLK_RXCLK_TX ; /* BRG output */
break ;
case CLOCK_TXINT :
rxs | = CLK_LINE_RX ; /* RXC input */
txs | = CLK_BRG_TX ; /* BRG output */
break ;
case CLOCK_TXFROMRX :
rxs | = CLK_LINE_RX ; /* RXC input */
txs | = CLK_RXCLK_TX ; /* RX clock */
break ;
default : /* EXTernal clock */
rxs | = CLK_LINE_RX ; /* RXC input */
txs | = CLK_LINE_TX ; /* TXC input */
}
port - > rxs = rxs ;
port - > txs = txs ;
sca_out ( rxs , MSCI1_OFFSET + RXS , port ) ;
sca_out ( txs , MSCI1_OFFSET + TXS , port ) ;
sca_set_port ( port ) ;
}
static int c101_open ( struct net_device * dev )
{
port_t * port = dev_to_port ( dev ) ;
int result ;
result = hdlc_open ( dev ) ;
if ( result )
return result ;
writeb ( 1 , port - > win0base + C101_DTR ) ;
sca_out ( 0 , MSCI1_OFFSET + CTL , port ) ; /* RTS uses ch#2 output */
sca_open ( dev ) ;
/* DCD is connected to port 2 !@#$%^& - disable MSCI0 CDCD interrupt */
sca_out ( IE1_UDRN , MSCI0_OFFSET + IE1 , port ) ;
sca_out ( IE0_TXINT , MSCI0_OFFSET + IE0 , port ) ;
2006-07-12 13:46:12 -07:00
set_carrier ( port ) ;
2005-04-16 15:20:36 -07:00
/* enable MSCI1 CDCD interrupt */
sca_out ( IE1_CDCD , MSCI1_OFFSET + IE1 , port ) ;
sca_out ( IE0_RXINTA , MSCI1_OFFSET + IE0 , port ) ;
sca_out ( 0x48 , IER0 , port ) ; /* TXINT #0 and RXINT #1 */
c101_set_iface ( port ) ;
return 0 ;
}
static int c101_close ( struct net_device * dev )
{
port_t * port = dev_to_port ( dev ) ;
sca_close ( dev ) ;
writeb ( 0 , port - > win0base + C101_DTR ) ;
sca_out ( CTL_NORTS , MSCI1_OFFSET + CTL , port ) ;
hdlc_close ( dev ) ;
return 0 ;
}
static int c101_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 ) ;
printk ( KERN_DEBUG " MSCI1: ST: %02x %02x %02x %02x \n " ,
sca_in ( MSCI1_OFFSET + ST0 , port ) ,
sca_in ( MSCI1_OFFSET + ST1 , port ) ,
sca_in ( MSCI1_OFFSET + ST2 , port ) ,
sca_in ( MSCI1_OFFSET + ST3 , port ) ) ;
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 )
2010-08-05 10:17:00 +00:00
return - EINVAL ; /* No such clock setting */
2005-04-16 15:20:36 -07:00
if ( new_line . loopback ! = 0 & & new_line . loopback ! = 1 )
return - EINVAL ;
memcpy ( & port - > settings , & new_line , size ) ; /* Update settings */
c101_set_iface ( port ) ;
return 0 ;
default :
return hdlc_ioctl ( dev , ifr , cmd ) ;
}
}
static void c101_destroy_card ( card_t * card )
{
readb ( card - > win0base + C101_PAGE ) ; /* Resets SCA? */
if ( card - > irq )
free_irq ( card - > irq , card ) ;
if ( card - > win0base ) {
iounmap ( card - > win0base ) ;
release_mem_region ( card - > phy_winbase , C101_MAPPED_RAM_SIZE ) ;
}
free_netdev ( card - > dev ) ;
kfree ( card ) ;
}
2009-01-08 22:52:11 +01:00
static const struct net_device_ops c101_ops = {
. ndo_open = c101_open ,
. ndo_stop = c101_close ,
. ndo_change_mtu = hdlc_change_mtu ,
. ndo_start_xmit = hdlc_start_xmit ,
. ndo_do_ioctl = c101_ioctl ,
} ;
2005-04-16 15:20:36 -07:00
static int __init c101_run ( unsigned long irq , unsigned long winbase )
{
struct net_device * dev ;
hdlc_device * hdlc ;
card_t * card ;
int result ;
if ( irq < 3 | | irq > 15 | | irq = = 6 ) /* FIXME */ {
printk ( KERN_ERR " c101: invalid IRQ value \n " ) ;
return - ENODEV ;
}
if ( winbase < 0xC0000 | | winbase > 0xDFFFF | | ( winbase & 0x3FFF ) ! = 0 ) {
printk ( KERN_ERR " c101: invalid RAM value \n " ) ;
return - ENODEV ;
}
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 01:49:03 -07:00
card = kzalloc ( sizeof ( card_t ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( card = = NULL ) {
printk ( KERN_ERR " c101: unable to allocate memory \n " ) ;
return - ENOBUFS ;
}
card - > dev = alloc_hdlcdev ( card ) ;
if ( ! card - > dev ) {
printk ( KERN_ERR " c101: unable to allocate memory \n " ) ;
kfree ( card ) ;
return - ENOBUFS ;
}
if ( request_irq ( irq , sca_intr , 0 , devname , card ) ) {
printk ( KERN_ERR " c101: could not allocate IRQ \n " ) ;
c101_destroy_card ( card ) ;
2006-06-22 22:29:28 +02:00
return - EBUSY ;
2005-04-16 15:20:36 -07:00
}
card - > irq = irq ;
if ( ! request_mem_region ( winbase , C101_MAPPED_RAM_SIZE , devname ) ) {
printk ( KERN_ERR " c101: could not request RAM window \n " ) ;
c101_destroy_card ( card ) ;
2006-06-22 22:29:28 +02:00
return - EBUSY ;
2005-04-16 15:20:36 -07:00
}
card - > phy_winbase = winbase ;
card - > win0base = ioremap ( winbase , C101_MAPPED_RAM_SIZE ) ;
if ( ! card - > win0base ) {
printk ( KERN_ERR " c101: could not map I/O address \n " ) ;
c101_destroy_card ( card ) ;
2006-06-22 22:29:28 +02:00
return - EFAULT ;
2005-04-16 15:20:36 -07:00
}
card - > tx_ring_buffers = TX_RING_BUFFERS ;
card - > rx_ring_buffers = RX_RING_BUFFERS ;
card - > buff_offset = C101_WINDOW_SIZE ; /* Bytes 1D00-1FFF reserved */
readb ( card - > win0base + C101_PAGE ) ; /* Resets SCA? */
udelay ( 100 ) ;
writeb ( 0 , card - > win0base + C101_PAGE ) ;
writeb ( 0 , card - > win0base + C101_DTR ) ; /* Power-up for RAM? */
sca_init ( card , 0 ) ;
dev = port_to_dev ( card ) ;
hdlc = dev_to_hdlc ( dev ) ;
spin_lock_init ( & card - > lock ) ;
dev - > irq = irq ;
dev - > mem_start = winbase ;
dev - > mem_end = winbase + C101_MAPPED_RAM_SIZE - 1 ;
dev - > tx_queue_len = 50 ;
2009-01-08 22:52:11 +01:00
dev - > netdev_ops = & c101_ops ;
2005-04-16 15:20:36 -07:00
hdlc - > attach = sca_attach ;
hdlc - > xmit = sca_xmit ;
card - > settings . clock_type = CLOCK_EXT ;
result = register_hdlc_device ( dev ) ;
if ( result ) {
printk ( KERN_WARNING " c101: unable to register hdlc device \n " ) ;
c101_destroy_card ( card ) ;
return result ;
}
2008-03-24 19:12:23 +01:00
sca_init_port ( card ) ; /* Set up C101 memory */
2006-07-12 13:46:12 -07:00
set_carrier ( card ) ;
2005-04-16 15:20:36 -07:00
printk ( KERN_INFO " %s: Moxa C101 on IRQ%u, "
" using %u TX + %u RX packets rings \n " ,
dev - > name , card - > irq ,
card - > tx_ring_buffers , card - > rx_ring_buffers ) ;
* new_card = card ;
new_card = & card - > next_card ;
return 0 ;
}
static int __init c101_init ( void )
{
if ( hw = = NULL ) {
# ifdef MODULE
printk ( KERN_INFO " c101: no card initialized \n " ) ;
# endif
2008-04-20 19:10:56 +02:00
return - EINVAL ; /* no parameters specified, abort */
2005-04-16 15:20:36 -07:00
}
printk ( KERN_INFO " %s \n " , version ) ;
do {
unsigned long irq , ram ;
irq = simple_strtoul ( hw , & hw , 0 ) ;
if ( * hw + + ! = ' , ' )
break ;
ram = simple_strtoul ( hw , & hw , 0 ) ;
if ( * hw = = ' : ' | | * hw = = ' \x0 ' )
c101_run ( irq , ram ) ;
if ( * hw = = ' \x0 ' )
2008-04-20 19:10:56 +02:00
return first_card ? 0 : - EINVAL ;
2005-04-16 15:20:36 -07:00
} while ( * hw + + = = ' : ' ) ;
printk ( KERN_ERR " c101: invalid hardware parameters \n " ) ;
2008-04-20 19:10:56 +02:00
return first_card ? 0 : - EINVAL ;
2005-04-16 15:20:36 -07:00
}
static void __exit c101_cleanup ( void )
{
card_t * card = first_card ;
while ( card ) {
card_t * ptr = card ;
card = card - > next_card ;
unregister_hdlc_device ( port_to_dev ( ptr ) ) ;
c101_destroy_card ( ptr ) ;
}
}
module_init ( c101_init ) ;
module_exit ( c101_cleanup ) ;
MODULE_AUTHOR ( " Krzysztof Halasa <khc@pm.waw.pl> " ) ;
MODULE_DESCRIPTION ( " Moxa C101 serial port driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2006-07-21 14:41:36 -07:00
module_param ( hw , charp , 0444 ) ;
MODULE_PARM_DESC ( hw , " irq,ram:irq,... " ) ;