2005-04-17 02:20:36 +04:00
/*
* Support for common PCI multi - I / O cards ( which is most of them )
*
* Copyright ( C ) 2001 Tim Waugh < twaugh @ redhat . com >
*
* 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 .
*
*
* Multi - function PCI cards are supposed to present separate logical
* devices on the bus . A common thing to do seems to be to just use
* one logical device with lots of base address registers for both
* parallel ports and serial ports . This driver is for dealing with
* that .
*
*/
# include <linux/types.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/parport.h>
# include <linux/parport_pc.h>
# include <linux/8250_pci.h>
enum parport_pc_pci_cards {
titan_110l = 0 ,
titan_210l ,
netmos_9xx5_combo ,
2005-06-23 11:09:55 +04:00
netmos_9855 ,
2005-04-17 02:20:36 +04:00
avlab_1s1p ,
avlab_1s2p ,
avlab_2s1p ,
siig_1s1p_10x ,
siig_2s1p_10x ,
siig_2p1s_20x ,
siig_1s1p_20x ,
siig_2s1p_20x ,
} ;
/* each element directly indexed from enum list, above */
struct parport_pc_pci {
int numports ;
struct { /* BAR (base address registers) numbers in the config
space header */
int lo ;
int hi ; /* -1 if not there, >6 for offset-method (max
BAR is 6 ) */
} addr [ 4 ] ;
/* If set, this is called immediately after pci_enable_device.
* If it returns non - zero , no probing will take place and the
* ports will not be used . */
int ( * preinit_hook ) ( struct pci_dev * pdev , struct parport_pc_pci * card ,
int autoirq , int autodma ) ;
/* If set, this is called after probing for ports. If 'failed'
* is non - zero we couldn ' t use any of the ports . */
void ( * postinit_hook ) ( struct pci_dev * pdev ,
struct parport_pc_pci * card , int failed ) ;
} ;
static int __devinit netmos_parallel_init ( struct pci_dev * dev , struct parport_pc_pci * card , int autoirq , int autodma )
{
/*
* Netmos uses the subdevice ID to indicate the number of parallel
* and serial ports . The form is 0x00 PS , where < P > is the number of
* parallel ports and < S > is the number of serial ports .
*/
card - > numports = ( dev - > subsystem_device & 0xf0 ) > > 4 ;
return 0 ;
}
static struct parport_pc_pci cards [ ] __devinitdata = {
/* titan_110l */ { 1 , { { 3 , - 1 } , } } ,
/* titan_210l */ { 1 , { { 3 , - 1 } , } } ,
/* netmos_9xx5_combo */ { 1 , { { 2 , - 1 } , } , netmos_parallel_init } ,
2008-02-06 12:37:44 +03:00
/* netmos_9855 */ { 1 , { { 2 , - 1 } , } , netmos_parallel_init } ,
2005-04-17 02:20:36 +04:00
/* avlab_1s1p */ { 1 , { { 1 , 2 } , } } ,
/* avlab_1s2p */ { 2 , { { 1 , 2 } , { 3 , 4 } , } } ,
/* avlab_2s1p */ { 1 , { { 2 , 3 } , } } ,
/* siig_1s1p_10x */ { 1 , { { 3 , 4 } , } } ,
/* siig_2s1p_10x */ { 1 , { { 4 , 5 } , } } ,
/* siig_2p1s_20x */ { 2 , { { 1 , 2 } , { 3 , 4 } , } } ,
/* siig_1s1p_20x */ { 1 , { { 1 , 2 } , } } ,
/* siig_2s1p_20x */ { 1 , { { 2 , 3 } , } } ,
} ;
static struct pci_device_id parport_serial_pci_tbl [ ] = {
/* PCI cards */
{ PCI_VENDOR_ID_TITAN , PCI_DEVICE_ID_TITAN_110L ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , titan_110l } ,
{ PCI_VENDOR_ID_TITAN , PCI_DEVICE_ID_TITAN_210L ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , titan_210l } ,
{ PCI_VENDOR_ID_NETMOS , PCI_DEVICE_ID_NETMOS_9735 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , netmos_9xx5_combo } ,
{ PCI_VENDOR_ID_NETMOS , PCI_DEVICE_ID_NETMOS_9745 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , netmos_9xx5_combo } ,
{ PCI_VENDOR_ID_NETMOS , PCI_DEVICE_ID_NETMOS_9835 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , netmos_9xx5_combo } ,
{ PCI_VENDOR_ID_NETMOS , PCI_DEVICE_ID_NETMOS_9845 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , netmos_9xx5_combo } ,
{ PCI_VENDOR_ID_NETMOS , PCI_DEVICE_ID_NETMOS_9855 ,
2005-06-23 11:09:55 +04:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , netmos_9855 } ,
2005-04-17 02:20:36 +04:00
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
2006-03-20 23:08:22 +03:00
{ PCI_VENDOR_ID_AFAVLAB , 0x2110 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_1s1p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2111 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_1s1p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2112 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_1s1p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2140 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_1s2p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2141 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_1s2p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2142 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_1s2p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2160 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_2s1p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2161 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_2s1p } ,
{ PCI_VENDOR_ID_AFAVLAB , 0x2162 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , avlab_2s1p } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_1S1P_10x_550 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_1s1p_10x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_1S1P_10x_650 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_1s1p_10x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_1S1P_10x_850 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_1s1p_10x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2S1P_10x_550 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2s1p_10x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2S1P_10x_650 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2s1p_10x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2S1P_10x_850 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2s1p_10x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2P1S_20x_550 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2p1s_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2P1S_20x_650 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2p1s_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2P1S_20x_850 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2p1s_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_1S1P_20x_550 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2s1p_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_1S1P_20x_650 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_1s1p_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_1S1P_20x_850 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_1s1p_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2S1P_20x_550 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2s1p_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2S1P_20x_650 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2s1p_20x } ,
{ PCI_VENDOR_ID_SIIG , PCI_DEVICE_ID_SIIG_2S1P_20x_850 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , siig_2s1p_20x } ,
{ 0 , } /* terminate list */
} ;
MODULE_DEVICE_TABLE ( pci , parport_serial_pci_tbl ) ;
2005-07-27 14:41:18 +04:00
/*
* This table describes the serial " geometry " of these boards . Any
* quirks for these can be found in drivers / serial / 8250 _pci . c
*
* Cards not tested are marked n / t
* If you have one of these cards and it works for you , please tell me . .
*/
static struct pciserial_board pci_parport_serial_boards [ ] __devinitdata = {
[ titan_110l ] = {
. flags = FL_BASE1 | FL_BASE_BARS ,
. num_ports = 1 ,
. base_baud = 921600 ,
. uart_offset = 8 ,
} ,
[ titan_210l ] = {
. flags = FL_BASE1 | FL_BASE_BARS ,
. num_ports = 2 ,
. base_baud = 921600 ,
. uart_offset = 8 ,
} ,
[ netmos_9xx5_combo ] = {
. flags = FL_BASE0 | FL_BASE_BARS ,
. num_ports = 1 ,
. base_baud = 115200 ,
. uart_offset = 8 ,
} ,
[ netmos_9855 ] = {
2008-02-06 12:37:44 +03:00
. flags = FL_BASE4 | FL_BASE_BARS ,
2005-07-27 14:41:18 +04:00
. num_ports = 1 ,
. base_baud = 115200 ,
. uart_offset = 8 ,
} ,
[ avlab_1s1p ] = { /* n/t */
. flags = FL_BASE0 | FL_BASE_BARS ,
. num_ports = 1 ,
. base_baud = 115200 ,
. uart_offset = 8 ,
} ,
[ avlab_1s2p ] = { /* n/t */
. flags = FL_BASE0 | FL_BASE_BARS ,
. num_ports = 1 ,
. base_baud = 115200 ,
. uart_offset = 8 ,
} ,
[ avlab_2s1p ] = { /* n/t */
. flags = FL_BASE0 | FL_BASE_BARS ,
. num_ports = 2 ,
. base_baud = 115200 ,
. uart_offset = 8 ,
} ,
[ siig_1s1p_10x ] = {
. flags = FL_BASE2 ,
. num_ports = 1 ,
. base_baud = 460800 ,
. uart_offset = 8 ,
} ,
[ siig_2s1p_10x ] = {
. flags = FL_BASE2 ,
. num_ports = 1 ,
. base_baud = 921600 ,
. uart_offset = 8 ,
} ,
[ siig_2p1s_20x ] = {
. flags = FL_BASE0 ,
. num_ports = 1 ,
. base_baud = 921600 ,
. uart_offset = 8 ,
} ,
[ siig_1s1p_20x ] = {
. flags = FL_BASE0 ,
. num_ports = 1 ,
. base_baud = 921600 ,
. uart_offset = 8 ,
} ,
[ siig_2s1p_20x ] = {
. flags = FL_BASE0 ,
. num_ports = 1 ,
. base_baud = 921600 ,
. uart_offset = 8 ,
} ,
2005-04-17 02:20:36 +04:00
} ;
struct parport_serial_private {
2005-07-27 14:41:18 +04:00
struct serial_private * serial ;
2005-04-17 02:20:36 +04:00
int num_par ;
struct parport * port [ PARPORT_MAX ] ;
struct parport_pc_pci par ;
} ;
/* Register the serial port(s) of a PCI card. */
static int __devinit serial_register ( struct pci_dev * dev ,
const struct pci_device_id * id )
{
struct parport_serial_private * priv = pci_get_drvdata ( dev ) ;
2005-07-27 14:41:18 +04:00
struct pciserial_board * board ;
struct serial_private * serial ;
2005-04-17 02:20:36 +04:00
2005-07-27 14:41:18 +04:00
board = & pci_parport_serial_boards [ id - > driver_data ] ;
serial = pciserial_init_ports ( dev , board ) ;
2005-04-17 02:20:36 +04:00
2005-07-27 14:41:18 +04:00
if ( IS_ERR ( serial ) )
return PTR_ERR ( serial ) ;
2005-04-17 02:20:36 +04:00
2005-07-27 14:41:18 +04:00
priv - > serial = serial ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* Register the parallel port(s) of a PCI card. */
static int __devinit parport_register ( struct pci_dev * dev ,
const struct pci_device_id * id )
{
struct parport_pc_pci * card ;
struct parport_serial_private * priv = pci_get_drvdata ( dev ) ;
2006-03-05 03:31:22 +03:00
int n , success = 0 ;
2005-04-17 02:20:36 +04:00
priv - > par = cards [ id - > driver_data ] ;
card = & priv - > par ;
if ( card - > preinit_hook & &
card - > preinit_hook ( dev , card , PARPORT_IRQ_NONE , PARPORT_DMA_NONE ) )
return - ENODEV ;
for ( n = 0 ; n < card - > numports ; n + + ) {
struct parport * port ;
int lo = card - > addr [ n ] . lo ;
int hi = card - > addr [ n ] . hi ;
unsigned long io_lo , io_hi ;
if ( priv - > num_par = = ARRAY_SIZE ( priv - > port ) ) {
printk ( KERN_WARNING
2006-02-03 14:04:02 +03:00
" parport_serial: %s: only %zu parallel ports "
2005-04-17 02:20:36 +04:00
" supported (%d reported) \n " , pci_name ( dev ) ,
2006-02-03 14:04:02 +03:00
ARRAY_SIZE ( priv - > port ) , card - > numports ) ;
2005-04-17 02:20:36 +04:00
break ;
}
io_lo = pci_resource_start ( dev , lo ) ;
io_hi = 0 ;
if ( ( hi > = 0 ) & & ( hi < = 6 ) )
io_hi = pci_resource_start ( dev , hi ) ;
else if ( hi > 6 )
io_lo + = hi ; /* Reinterpret the meaning of
" hi " as an offset ( see SYBA
def . ) */
/* TODO: test if sharing interrupts works */
2006-03-05 03:31:22 +03:00
dev_dbg ( & dev - > dev , " PCI parallel port detected: I/O at "
" %#lx(%#lx) \n " , io_lo , io_hi ) ;
2005-04-17 02:20:36 +04:00
port = parport_pc_probe_port ( io_lo , io_hi , PARPORT_IRQ_NONE ,
2007-05-08 11:27:35 +04:00
PARPORT_DMA_NONE , & dev - > dev ) ;
2005-04-17 02:20:36 +04:00
if ( port ) {
priv - > port [ priv - > num_par + + ] = port ;
success = 1 ;
}
}
if ( card - > postinit_hook )
card - > postinit_hook ( dev , card , ! success ) ;
2006-03-05 03:31:22 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int __devinit parport_serial_pci_probe ( struct pci_dev * dev ,
const struct pci_device_id * id )
{
struct parport_serial_private * priv ;
int err ;
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 12:49:03 +04:00
priv = kzalloc ( sizeof * priv , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! priv )
return - ENOMEM ;
pci_set_drvdata ( dev , priv ) ;
err = pci_enable_device ( dev ) ;
if ( err ) {
pci_set_drvdata ( dev , NULL ) ;
kfree ( priv ) ;
return err ;
}
if ( parport_register ( dev , id ) ) {
pci_set_drvdata ( dev , NULL ) ;
kfree ( priv ) ;
return - ENODEV ;
}
if ( serial_register ( dev , id ) ) {
int i ;
for ( i = 0 ; i < priv - > num_par ; i + + )
parport_pc_unregister_port ( priv - > port [ i ] ) ;
pci_set_drvdata ( dev , NULL ) ;
kfree ( priv ) ;
return - ENODEV ;
}
return 0 ;
}
static void __devexit parport_serial_pci_remove ( struct pci_dev * dev )
{
struct parport_serial_private * priv = pci_get_drvdata ( dev ) ;
int i ;
2005-07-27 14:41:18 +04:00
pci_set_drvdata ( dev , NULL ) ;
2005-04-17 02:20:36 +04:00
// Serial ports
2005-07-27 14:41:18 +04:00
if ( priv - > serial )
pciserial_remove_ports ( priv - > serial ) ;
2005-04-17 02:20:36 +04:00
// Parallel ports
for ( i = 0 ; i < priv - > num_par ; i + + )
parport_pc_unregister_port ( priv - > port [ i ] ) ;
kfree ( priv ) ;
return ;
}
2006-09-29 13:00:16 +04:00
# ifdef CONFIG_PM
2005-07-27 14:41:18 +04:00
static int parport_serial_pci_suspend ( struct pci_dev * dev , pm_message_t state )
{
struct parport_serial_private * priv = pci_get_drvdata ( dev ) ;
if ( priv - > serial )
pciserial_suspend_ports ( priv - > serial ) ;
/* FIXME: What about parport? */
pci_save_state ( dev ) ;
pci_set_power_state ( dev , pci_choose_state ( dev , state ) ) ;
return 0 ;
}
static int parport_serial_pci_resume ( struct pci_dev * dev )
{
struct parport_serial_private * priv = pci_get_drvdata ( dev ) ;
2007-05-08 11:29:01 +04:00
int err ;
2005-07-27 14:41:18 +04:00
pci_set_power_state ( dev , PCI_D0 ) ;
pci_restore_state ( dev ) ;
/*
* The device may have been disabled . Re - enable it .
*/
2007-05-08 11:29:01 +04:00
err = pci_enable_device ( dev ) ;
if ( err ) {
printk ( KERN_ERR " parport_serial: %s: error enabling "
" device for resume (%d) \n " , pci_name ( dev ) , err ) ;
return err ;
}
2005-07-27 14:41:18 +04:00
if ( priv - > serial )
pciserial_resume_ports ( priv - > serial ) ;
/* FIXME: What about parport? */
return 0 ;
}
2006-09-29 13:00:16 +04:00
# endif
2005-07-27 14:41:18 +04:00
2005-04-17 02:20:36 +04:00
static struct pci_driver parport_serial_pci_driver = {
. name = " parport_serial " ,
. id_table = parport_serial_pci_tbl ,
. probe = parport_serial_pci_probe ,
. remove = __devexit_p ( parport_serial_pci_remove ) ,
2006-09-29 13:00:16 +04:00
# ifdef CONFIG_PM
2005-07-27 14:41:18 +04:00
. suspend = parport_serial_pci_suspend ,
. resume = parport_serial_pci_resume ,
2006-09-29 13:00:16 +04:00
# endif
2005-04-17 02:20:36 +04:00
} ;
static int __init parport_serial_init ( void )
{
2005-11-30 03:00:35 +03:00
return pci_register_driver ( & parport_serial_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit parport_serial_exit ( void )
{
pci_unregister_driver ( & parport_serial_pci_driver ) ;
return ;
}
MODULE_AUTHOR ( " Tim Waugh <twaugh@redhat.com> " ) ;
MODULE_DESCRIPTION ( " Driver for common parallel+serial multi-I/O PCI cards " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( parport_serial_init ) ;
module_exit ( parport_serial_exit ) ;