2008-02-07 12:57:12 +03:00
/*
* IPWireless 3 G PCMCIA Network Driver
*
* Original code
* by Stephen Blackheath < stephen @ blacksapphire . com > ,
* Ben Martel < benm @ symmetric . co . nz >
*
* Copyrighted as follows :
* Copyright ( C ) 2004 by Symmetric Systems Ltd ( NZ )
*
* Various driver changes and rewrites , port to new kernels
* Copyright ( C ) 2006 - 2007 Jiri Kosina
*
* Misc code cleanups and updates
* Copyright ( C ) 2007 David Sterba
*/
# include "hardware.h"
# include "network.h"
# include "main.h"
# include "tty.h"
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <pcmcia/cisreg.h>
# include <pcmcia/device_id.h>
# include <pcmcia/ss.h>
# include <pcmcia/ds.h>
2011-05-04 06:29:01 +04:00
static const struct pcmcia_device_id ipw_ids [ ] = {
2008-02-07 12:57:12 +03:00
PCMCIA_DEVICE_MANF_CARD ( 0x02f2 , 0x0100 ) ,
PCMCIA_DEVICE_MANF_CARD ( 0x02f2 , 0x0200 ) ,
PCMCIA_DEVICE_NULL
} ;
MODULE_DEVICE_TABLE ( pcmcia , ipw_ids ) ;
static void ipwireless_detach ( struct pcmcia_device * link ) ;
/*
* Module params
*/
/* Debug mode: more verbose, print sent/recv bytes */
int ipwireless_debug ;
int ipwireless_loopback ;
2008-07-28 18:53:21 +04:00
int ipwireless_out_queue = 10 ;
2008-02-07 12:57:12 +03:00
module_param_named ( debug , ipwireless_debug , int , 0 ) ;
module_param_named ( loopback , ipwireless_loopback , int , 0 ) ;
module_param_named ( out_queue , ipwireless_out_queue , int , 0 ) ;
MODULE_PARM_DESC ( debug , " switch on debug messages [0] " ) ;
MODULE_PARM_DESC ( loopback ,
" debug: enable ras_raw channel [0] " ) ;
2008-07-28 18:53:21 +04:00
MODULE_PARM_DESC ( out_queue , " debug: set size of outgoing PPP queue [10] " ) ;
2008-02-07 12:57:12 +03:00
/* Executes in process context. */
static void signalled_reboot_work ( struct work_struct * work_reboot )
{
struct ipw_dev * ipw = container_of ( work_reboot , struct ipw_dev ,
work_reboot ) ;
struct pcmcia_device * link = ipw - > link ;
2009-10-24 17:47:29 +04:00
pcmcia_reset_card ( link - > socket ) ;
2008-02-07 12:57:12 +03:00
}
static void signalled_reboot_callback ( void * callback_data )
{
struct ipw_dev * ipw = ( struct ipw_dev * ) callback_data ;
/* Delegate to process context. */
schedule_work ( & ipw - > work_reboot ) ;
}
2010-07-30 15:13:46 +04:00
static int ipwireless_probe ( struct pcmcia_device * p_dev , void * priv_data )
2009-10-18 20:28:39 +04:00
{
2009-10-18 21:48:39 +04:00
struct ipw_dev * ipw = priv_data ;
int ret ;
2010-07-30 15:13:46 +04:00
p_dev - > resource [ 0 ] - > flags & = ~ IO_DATA_PATH_WIDTH ;
2010-07-24 19:23:51 +04:00
p_dev - > resource [ 0 ] - > flags | = IO_DATA_PATH_WIDTH_AUTO ;
2009-10-18 20:28:39 +04:00
/* 0x40 causes it to generate level mode interrupts. */
/* 0x04 enables IREQ pin. */
2010-07-30 15:13:46 +04:00
p_dev - > config_index | = 0x44 ;
2010-07-24 19:23:51 +04:00
p_dev - > io_lines = 16 ;
ret = pcmcia_request_io ( p_dev ) ;
2009-10-18 21:48:39 +04:00
if ( ret )
return ret ;
2009-10-18 20:28:39 +04:00
2011-02-13 15:12:10 +03:00
if ( ! request_region ( p_dev - > resource [ 0 ] - > start ,
resource_size ( p_dev - > resource [ 0 ] ) ,
IPWIRELESS_PCCARD_NAME ) ) {
ret = - EBUSY ;
goto exit ;
}
2008-02-07 12:57:12 +03:00
2010-07-28 12:59:06 +04:00
p_dev - > resource [ 2 ] - > flags | =
2009-10-18 21:48:39 +04:00
WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE ;
2008-02-07 12:57:12 +03:00
2010-07-28 12:59:06 +04:00
ret = pcmcia_request_window ( p_dev , p_dev - > resource [ 2 ] , 0 ) ;
2009-10-24 17:47:29 +04:00
if ( ret ! = 0 )
2009-10-18 21:48:39 +04:00
goto exit1 ;
2008-02-07 12:57:12 +03:00
2010-07-30 15:13:46 +04:00
ret = pcmcia_map_mem_page ( p_dev , p_dev - > resource [ 2 ] , p_dev - > card_addr ) ;
2009-10-24 17:47:29 +04:00
if ( ret ! = 0 )
2011-02-13 15:12:10 +03:00
goto exit1 ;
2008-02-07 12:57:12 +03:00
2010-07-30 15:13:46 +04:00
ipw - > is_v2_card = resource_size ( p_dev - > resource [ 2 ] ) = = 0x100 ;
2008-02-07 12:57:12 +03:00
2011-02-13 15:12:10 +03:00
ipw - > common_memory = ioremap ( p_dev - > resource [ 2 ] - > start ,
2010-07-28 12:59:06 +04:00
resource_size ( p_dev - > resource [ 2 ] ) ) ;
2011-02-13 15:12:10 +03:00
if ( ! request_mem_region ( p_dev - > resource [ 2 ] - > start ,
resource_size ( p_dev - > resource [ 2 ] ) ,
IPWIRELESS_PCCARD_NAME ) ) {
ret = - EBUSY ;
goto exit2 ;
}
2009-10-18 21:48:39 +04:00
2010-07-28 12:59:06 +04:00
p_dev - > resource [ 3 ] - > flags | = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM |
WIN_ENABLE ;
p_dev - > resource [ 3 ] - > end = 0 ; /* this used to be 0x1000 */
ret = pcmcia_request_window ( p_dev , p_dev - > resource [ 3 ] , 0 ) ;
2009-10-24 17:47:29 +04:00
if ( ret ! = 0 )
2011-02-13 15:12:10 +03:00
goto exit3 ;
2008-02-07 12:57:12 +03:00
2010-07-28 12:59:06 +04:00
ret = pcmcia_map_mem_page ( p_dev , p_dev - > resource [ 3 ] , 0 ) ;
2009-10-24 17:47:29 +04:00
if ( ret ! = 0 )
2009-10-18 21:48:39 +04:00
goto exit3 ;
2008-02-07 12:57:12 +03:00
2010-07-28 12:59:06 +04:00
ipw - > attr_memory = ioremap ( p_dev - > resource [ 3 ] - > start ,
resource_size ( p_dev - > resource [ 3 ] ) ) ;
2011-02-13 15:12:10 +03:00
if ( ! request_mem_region ( p_dev - > resource [ 3 ] - > start ,
resource_size ( p_dev - > resource [ 3 ] ) ,
IPWIRELESS_PCCARD_NAME ) ) {
ret = - EBUSY ;
goto exit4 ;
}
2008-02-07 12:57:12 +03:00
2009-10-18 21:48:39 +04:00
return 0 ;
2008-02-07 12:57:12 +03:00
2011-02-13 15:12:10 +03:00
exit4 :
iounmap ( ipw - > attr_memory ) ;
2009-10-18 21:48:39 +04:00
exit3 :
2011-02-13 15:12:10 +03:00
release_mem_region ( p_dev - > resource [ 2 ] - > start ,
resource_size ( p_dev - > resource [ 2 ] ) ) ;
2009-10-18 21:48:39 +04:00
exit2 :
2011-02-13 15:12:10 +03:00
iounmap ( ipw - > common_memory ) ;
2009-10-18 21:48:39 +04:00
exit1 :
2011-02-13 15:12:10 +03:00
release_region ( p_dev - > resource [ 0 ] - > start ,
resource_size ( p_dev - > resource [ 0 ] ) ) ;
exit :
2009-10-18 21:48:39 +04:00
pcmcia_disable_device ( p_dev ) ;
2011-02-13 15:12:10 +03:00
return ret ;
2009-10-18 21:48:39 +04:00
}
2008-02-07 12:57:12 +03:00
2009-10-18 21:48:39 +04:00
static int config_ipwireless ( struct ipw_dev * ipw )
{
struct pcmcia_device * link = ipw - > link ;
int ret = 0 ;
2008-02-07 12:57:12 +03:00
2009-10-18 21:48:39 +04:00
ipw - > is_v2_card = 0 ;
2010-07-30 15:13:46 +04:00
link - > config_flags | = CONF_AUTO_SET_IO | CONF_AUTO_SET_IOMEM |
CONF_ENABLE_IRQ ;
2008-02-07 12:57:12 +03:00
2009-10-18 21:48:39 +04:00
ret = pcmcia_loop_config ( link , ipwireless_probe , ipw ) ;
2009-10-24 17:47:29 +04:00
if ( ret ! = 0 )
2009-10-18 21:48:39 +04:00
return ret ;
2008-02-07 12:57:12 +03:00
INIT_WORK ( & ipw - > work_reboot , signalled_reboot_work ) ;
2010-07-24 17:58:54 +04:00
ipwireless_init_hardware_v1 ( ipw - > hardware , link - > resource [ 0 ] - > start ,
2008-02-07 12:57:12 +03:00
ipw - > attr_memory , ipw - > common_memory ,
ipw - > is_v2_card , signalled_reboot_callback ,
ipw ) ;
2010-03-07 14:21:16 +03:00
ret = pcmcia_request_irq ( link , ipwireless_interrupt ) ;
2009-10-24 17:47:29 +04:00
if ( ret ! = 0 )
2009-10-18 21:48:39 +04:00
goto exit ;
2008-02-07 12:57:12 +03:00
printk ( KERN_INFO IPWIRELESS_PCCARD_NAME " : Card type %s \n " ,
ipw - > is_v2_card ? " V2/V3 " : " V1 " ) ;
printk ( KERN_INFO IPWIRELESS_PCCARD_NAME
2010-07-24 17:58:54 +04:00
" : I/O ports %pR, irq %d \n " , link - > resource [ 0 ] ,
2010-03-07 14:21:16 +03:00
( unsigned int ) link - > irq ) ;
2008-02-07 12:57:12 +03:00
if ( ipw - > attr_memory & & ipw - > common_memory )
printk ( KERN_INFO IPWIRELESS_PCCARD_NAME
2010-07-28 12:59:06 +04:00
" : attr memory %pR, common memory %pR \n " ,
link - > resource [ 3 ] ,
link - > resource [ 2 ] ) ;
2008-02-07 12:57:12 +03:00
ipw - > network = ipwireless_network_create ( ipw - > hardware ) ;
if ( ! ipw - > network )
2009-10-18 21:48:39 +04:00
goto exit ;
2008-02-07 12:57:12 +03:00
2010-03-20 21:43:26 +03:00
ipw - > tty = ipwireless_tty_create ( ipw - > hardware , ipw - > network ) ;
2008-02-07 12:57:12 +03:00
if ( ! ipw - > tty )
2009-10-18 21:48:39 +04:00
goto exit ;
2008-02-07 12:57:12 +03:00
ipwireless_init_hardware_v2_v3 ( ipw - > hardware ) ;
/*
* Do the RequestConfiguration last , because it enables interrupts .
* Then we don ' t get any interrupts before we ' re ready for them .
*/
2010-07-29 21:27:09 +04:00
ret = pcmcia_enable_device ( link ) ;
2009-10-24 17:47:29 +04:00
if ( ret ! = 0 )
2009-10-18 21:48:39 +04:00
goto exit ;
2008-02-07 12:57:12 +03:00
return 0 ;
2009-10-18 21:48:39 +04:00
exit :
2008-02-07 12:57:12 +03:00
if ( ipw - > common_memory ) {
2010-07-28 12:59:06 +04:00
release_mem_region ( link - > resource [ 2 ] - > start ,
resource_size ( link - > resource [ 2 ] ) ) ;
2008-02-07 12:57:12 +03:00
iounmap ( ipw - > common_memory ) ;
}
2010-07-28 12:59:06 +04:00
if ( ipw - > attr_memory ) {
release_mem_region ( link - > resource [ 3 ] - > start ,
resource_size ( link - > resource [ 3 ] ) ) ;
iounmap ( ipw - > attr_memory ) ;
}
2008-02-07 12:57:12 +03:00
pcmcia_disable_device ( link ) ;
return - 1 ;
}
static void release_ipwireless ( struct ipw_dev * ipw )
{
2011-02-13 15:12:10 +03:00
release_region ( ipw - > link - > resource [ 0 ] - > start ,
resource_size ( ipw - > link - > resource [ 0 ] ) ) ;
2008-07-28 18:53:16 +04:00
if ( ipw - > common_memory ) {
2010-07-28 12:59:06 +04:00
release_mem_region ( ipw - > link - > resource [ 2 ] - > start ,
resource_size ( ipw - > link - > resource [ 2 ] ) ) ;
2008-02-07 12:57:12 +03:00
iounmap ( ipw - > common_memory ) ;
2008-07-28 18:53:16 +04:00
}
if ( ipw - > attr_memory ) {
2010-07-28 12:59:06 +04:00
release_mem_region ( ipw - > link - > resource [ 3 ] - > start ,
resource_size ( ipw - > link - > resource [ 3 ] ) ) ;
2008-02-07 12:57:12 +03:00
iounmap ( ipw - > attr_memory ) ;
2008-07-28 18:53:16 +04:00
}
pcmcia_disable_device ( ipw - > link ) ;
2008-02-07 12:57:12 +03:00
}
/*
* ipwireless_attach ( ) creates an " instance " of the driver , allocating
* local data structures for one device ( one interface ) . The device
* is registered with Card Services .
*
* The pcmcia_device structure is initialized , but we don ' t actually
* configure the card at this point - - we wait until we receive a
* card insertion event .
*/
static int ipwireless_attach ( struct pcmcia_device * link )
{
struct ipw_dev * ipw ;
int ret ;
ipw = kzalloc ( sizeof ( struct ipw_dev ) , GFP_KERNEL ) ;
if ( ! ipw )
return - ENOMEM ;
ipw - > link = link ;
link - > priv = ipw ;
ipw - > hardware = ipwireless_hardware_create ( ) ;
if ( ! ipw - > hardware ) {
kfree ( ipw ) ;
return - ENOMEM ;
}
/* RegisterClient will call config_ipwireless */
ret = config_ipwireless ( ipw ) ;
if ( ret ! = 0 ) {
ipwireless_detach ( link ) ;
return ret ;
}
return 0 ;
}
/*
* This deletes a driver " instance " . The device is de - registered with
* Card Services . If it has been released , all local data structures
* are freed . Otherwise , the structures will be freed when the device
* is released .
*/
static void ipwireless_detach ( struct pcmcia_device * link )
{
struct ipw_dev * ipw = link - > priv ;
release_ipwireless ( ipw ) ;
if ( ipw - > tty ! = NULL )
ipwireless_tty_free ( ipw - > tty ) ;
if ( ipw - > network ! = NULL )
ipwireless_network_free ( ipw - > network ) ;
if ( ipw - > hardware ! = NULL )
ipwireless_hardware_free ( ipw - > hardware ) ;
kfree ( ipw ) ;
}
static struct pcmcia_driver me = {
. owner = THIS_MODULE ,
. probe = ipwireless_attach ,
. remove = ipwireless_detach ,
2010-08-08 13:36:26 +04:00
. name = IPWIRELESS_PCCARD_NAME ,
2008-02-07 12:57:12 +03:00
. id_table = ipw_ids
} ;
/*
* Module insertion : initialisation of the module .
* Register the card with cardmgr . . .
*/
static int __init init_ipwireless ( void )
{
int ret ;
ret = ipwireless_tty_init ( ) ;
if ( ret ! = 0 )
return ret ;
ret = pcmcia_register_driver ( & me ) ;
if ( ret ! = 0 )
ipwireless_tty_release ( ) ;
return ret ;
}
/*
* Module removal
*/
static void __exit exit_ipwireless ( void )
{
pcmcia_unregister_driver ( & me ) ;
ipwireless_tty_release ( ) ;
}
module_init ( init_ipwireless ) ;
module_exit ( exit_ipwireless ) ;
MODULE_AUTHOR ( IPWIRELESS_PCMCIA_AUTHOR ) ;
MODULE_DESCRIPTION ( IPWIRELESS_PCCARD_NAME " " IPWIRELESS_PCMCIA_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;