2005-09-02 04:08:56 +04:00
/* orinoco_nortel.c
2006-04-07 12:10:57 +04:00
*
2005-09-02 04:08:56 +04:00
* Driver for Prism II devices which would usually be driven by orinoco_cs ,
2005-12-17 02:57:10 +03:00
* but are connected to the PCI bus by a PCI - to - PCMCIA adapter used in
* Nortel emobility , Symbol LA - 4113 and Symbol LA - 4123.
2005-09-02 04:08:56 +04:00
*
* Copyright ( C ) 2002 Tobias Hoffmann
* ( C ) 2003 Christoph Jungegger < disdos @ traum404 . de >
*
* Some of this code is borrowed from orinoco_plx . c
* Copyright ( C ) 2001 Daniel Barlow
* Some of this code is borrowed from orinoco_pci . c
* Copyright ( C ) 2001 Jean Tourrilhes
* Some of this code is " inspired " by linux - wlan - ng - 0.1 .10 , but nothing
* has been copied from it . linux - wlan - ng - 0.1 .10 is originally :
* Copyright ( C ) 1999 AbsoluteValue Systems , Inc . All Rights Reserved .
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 ( the " License " ) ; you may not use this file except in
* compliance with the License . You may obtain a copy of the License
* at http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS "
* basis , WITHOUT WARRANTY OF ANY KIND , either express or implied . See
* the License for the specific language governing rights and
* limitations under the License .
*
* Alternatively , the contents of this file may be used under the
* terms of the GNU General Public License version 2 ( the " GPL " ) , in
* which case the provisions of the GPL are applicable instead of the
* above . If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the MPL , indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL . If you do not delete the
* provisions above , a recipient may use your version of this file
* under either the MPL or the GPL .
*/
# define DRIVER_NAME "orinoco_nortel"
# define PFX DRIVER_NAME ": "
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
2005-09-23 12:18:07 +04:00
# include <linux/delay.h>
2005-09-02 04:08:56 +04:00
# include <linux/pci.h>
# include <pcmcia/cisreg.h>
# include "orinoco.h"
2006-04-07 12:10:57 +04:00
# include "orinoco_pci.h"
2005-09-02 04:08:56 +04:00
# define COR_OFFSET (0xe0) /* COR attribute offset of Prism2 PC card */
# define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
/*
2006-04-07 12:11:00 +04:00
* Do a soft reset of the card using the Configuration Option Register
2005-09-02 04:08:56 +04:00
* We need this to get going . . .
* This is the part of the code that is strongly inspired from wlan - ng
*
* Note bis : Don ' t try to access HERMES_CMD during the reset phase .
* It just won ' t work !
*/
2006-04-07 12:10:57 +04:00
static int orinoco_nortel_cor_reset ( struct orinoco_private * priv )
2005-09-02 04:08:56 +04:00
{
2006-04-07 12:10:57 +04:00
struct orinoco_pci_card * card = priv - > card ;
2005-09-02 04:08:56 +04:00
2006-04-07 12:11:00 +04:00
/* Assert the reset until the card notices */
2006-04-07 12:10:57 +04:00
iowrite16 ( 8 , card - > bridge_io + 2 ) ;
ioread16 ( card - > attr_io + COR_OFFSET ) ;
iowrite16 ( 0x80 , card - > attr_io + COR_OFFSET ) ;
2005-09-02 04:08:56 +04:00
mdelay ( 1 ) ;
/* Give time for the card to recover from this hard effort */
2006-04-07 12:10:57 +04:00
iowrite16 ( 0 , card - > attr_io + COR_OFFSET ) ;
iowrite16 ( 0 , card - > attr_io + COR_OFFSET ) ;
2005-09-02 04:08:56 +04:00
mdelay ( 1 ) ;
2006-04-07 12:10:57 +04:00
/* Set COR as usual */
iowrite16 ( COR_VALUE , card - > attr_io + COR_OFFSET ) ;
iowrite16 ( COR_VALUE , card - > attr_io + COR_OFFSET ) ;
2005-09-02 04:08:56 +04:00
mdelay ( 1 ) ;
2006-04-07 12:10:57 +04:00
iowrite16 ( 0x228 , card - > bridge_io + 2 ) ;
2005-09-02 04:08:56 +04:00
return 0 ;
}
2006-04-07 12:10:57 +04:00
static int orinoco_nortel_hw_init ( struct orinoco_pci_card * card )
2005-09-02 04:08:56 +04:00
{
int i ;
u32 reg ;
2006-04-07 12:10:57 +04:00
/* Setup bridge */
if ( ioread16 ( card - > bridge_io ) & 1 ) {
2005-09-02 04:08:56 +04:00
printk ( KERN_ERR PFX " brg1 answer1 wrong \n " ) ;
return - EBUSY ;
}
2006-04-07 12:10:57 +04:00
iowrite16 ( 0x118 , card - > bridge_io + 2 ) ;
iowrite16 ( 0x108 , card - > bridge_io + 2 ) ;
2005-09-02 04:08:56 +04:00
mdelay ( 30 ) ;
2006-04-07 12:10:57 +04:00
iowrite16 ( 0x8 , card - > bridge_io + 2 ) ;
2005-09-02 04:08:56 +04:00
for ( i = 0 ; i < 30 ; i + + ) {
mdelay ( 30 ) ;
2006-04-07 12:10:57 +04:00
if ( ioread16 ( card - > bridge_io ) & 0x10 ) {
2005-09-02 04:08:56 +04:00
break ;
}
}
if ( i = = 30 ) {
printk ( KERN_ERR PFX " brg1 timed out \n " ) ;
return - EBUSY ;
}
2006-04-07 12:10:57 +04:00
if ( ioread16 ( card - > attr_io + COR_OFFSET ) & 1 ) {
2005-09-02 04:08:56 +04:00
printk ( KERN_ERR PFX " brg2 answer1 wrong \n " ) ;
return - EBUSY ;
}
2006-04-07 12:10:57 +04:00
if ( ioread16 ( card - > attr_io + COR_OFFSET + 2 ) & 1 ) {
2005-09-02 04:08:56 +04:00
printk ( KERN_ERR PFX " brg2 answer2 wrong \n " ) ;
return - EBUSY ;
}
2006-04-07 12:10:57 +04:00
if ( ioread16 ( card - > attr_io + COR_OFFSET + 4 ) & 1 ) {
2005-09-02 04:08:56 +04:00
printk ( KERN_ERR PFX " brg2 answer3 wrong \n " ) ;
return - EBUSY ;
}
2006-04-07 12:11:00 +04:00
/* Set the PCMCIA COR register */
2006-04-07 12:10:57 +04:00
iowrite16 ( COR_VALUE , card - > attr_io + COR_OFFSET ) ;
2005-09-02 04:08:56 +04:00
mdelay ( 1 ) ;
2006-04-07 12:10:57 +04:00
reg = ioread16 ( card - > attr_io + COR_OFFSET ) ;
2005-09-02 04:08:56 +04:00
if ( reg ! = COR_VALUE ) {
printk ( KERN_ERR PFX " Error setting COR value (reg=%x) \n " ,
reg ) ;
return - EBUSY ;
}
2006-04-07 12:10:57 +04:00
/* Set LEDs */
iowrite16 ( 1 , card - > bridge_io + 10 ) ;
2005-09-02 04:08:56 +04:00
return 0 ;
}
2006-04-07 12:10:57 +04:00
static int orinoco_nortel_init_one ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2005-09-02 04:08:56 +04:00
{
int err ;
struct orinoco_private * priv ;
2006-04-07 12:10:57 +04:00
struct orinoco_pci_card * card ;
2005-09-02 04:08:56 +04:00
struct net_device * dev ;
2006-04-07 12:10:57 +04:00
void __iomem * hermes_io , * bridge_io , * attr_io ;
2005-09-02 04:08:56 +04:00
err = pci_enable_device ( pdev ) ;
if ( err ) {
printk ( KERN_ERR PFX " Cannot enable PCI device \n " ) ;
return err ;
}
err = pci_request_regions ( pdev , DRIVER_NAME ) ;
2006-04-07 12:10:57 +04:00
if ( err ) {
2005-09-02 04:08:56 +04:00
printk ( KERN_ERR PFX " Cannot obtain PCI resources \n " ) ;
goto fail_resources ;
}
2006-04-07 12:10:57 +04:00
bridge_io = pci_iomap ( pdev , 0 , 0 ) ;
if ( ! bridge_io ) {
printk ( KERN_ERR PFX " Cannot map bridge registers \n " ) ;
err = - EIO ;
goto fail_map_bridge ;
}
attr_io = pci_iomap ( pdev , 1 , 0 ) ;
if ( ! attr_io ) {
printk ( KERN_ERR PFX " Cannot map PCMCIA attributes \n " ) ;
err = - EIO ;
goto fail_map_attr ;
}
hermes_io = pci_iomap ( pdev , 2 , 0 ) ;
if ( ! hermes_io ) {
printk ( KERN_ERR PFX " Cannot map chipset registers \n " ) ;
err = - EIO ;
goto fail_map_hermes ;
2005-09-02 04:08:56 +04:00
}
/* Allocate network device */
2006-04-07 12:10:57 +04:00
dev = alloc_orinocodev ( sizeof ( * card ) , orinoco_nortel_cor_reset ) ;
2005-09-02 04:08:56 +04:00
if ( ! dev ) {
printk ( KERN_ERR PFX " Cannot allocate network device \n " ) ;
err = - ENOMEM ;
goto fail_alloc ;
}
priv = netdev_priv ( dev ) ;
card = priv - > card ;
2006-04-07 12:10:57 +04:00
card - > bridge_io = bridge_io ;
card - > attr_io = attr_io ;
2005-09-02 04:08:56 +04:00
SET_MODULE_OWNER ( dev ) ;
SET_NETDEV_DEV ( dev , & pdev - > dev ) ;
2006-04-07 12:10:57 +04:00
hermes_struct_init ( & priv - > hw , hermes_io , HERMES_16BIT_REGSPACING ) ;
2005-09-02 04:08:56 +04:00
2006-07-02 06:29:39 +04:00
err = request_irq ( pdev - > irq , orinoco_interrupt , IRQF_SHARED ,
2005-09-02 04:08:56 +04:00
dev - > name , dev ) ;
if ( err ) {
printk ( KERN_ERR PFX " Cannot allocate IRQ %d \n " , pdev - > irq ) ;
err = - EBUSY ;
goto fail_irq ;
}
2006-04-07 12:10:57 +04:00
err = orinoco_nortel_hw_init ( card ) ;
2005-09-02 04:08:56 +04:00
if ( err ) {
printk ( KERN_ERR PFX " Hardware initialization failed \n " ) ;
goto fail ;
}
2006-04-07 12:10:57 +04:00
err = orinoco_nortel_cor_reset ( priv ) ;
2005-09-02 04:08:56 +04:00
if ( err ) {
printk ( KERN_ERR PFX " Initial reset failed \n " ) ;
goto fail ;
}
err = register_netdev ( dev ) ;
if ( err ) {
printk ( KERN_ERR PFX " Cannot register network device \n " ) ;
goto fail ;
}
pci_set_drvdata ( pdev , dev ) ;
2006-05-01 10:13:33 +04:00
printk ( KERN_DEBUG " %s: " DRIVER_NAME " at %s \n " , dev - > name ,
pci_name ( pdev ) ) ;
2005-09-02 04:08:56 +04:00
return 0 ;
fail :
free_irq ( pdev - > irq , dev ) ;
fail_irq :
pci_set_drvdata ( pdev , NULL ) ;
free_orinocodev ( dev ) ;
fail_alloc :
2006-04-07 12:10:57 +04:00
pci_iounmap ( pdev , hermes_io ) ;
fail_map_hermes :
pci_iounmap ( pdev , attr_io ) ;
fail_map_attr :
pci_iounmap ( pdev , bridge_io ) ;
2005-09-02 04:08:56 +04:00
2006-04-07 12:10:57 +04:00
fail_map_bridge :
2005-09-02 04:08:56 +04:00
pci_release_regions ( pdev ) ;
fail_resources :
pci_disable_device ( pdev ) ;
return err ;
}
2006-04-07 12:10:57 +04:00
static void __devexit orinoco_nortel_remove_one ( struct pci_dev * pdev )
2005-09-02 04:08:56 +04:00
{
struct net_device * dev = pci_get_drvdata ( pdev ) ;
struct orinoco_private * priv = netdev_priv ( dev ) ;
2006-04-07 12:10:57 +04:00
struct orinoco_pci_card * card = priv - > card ;
2005-09-02 04:08:56 +04:00
2006-04-07 12:10:57 +04:00
/* Clear LEDs */
iowrite16 ( 0 , card - > bridge_io + 10 ) ;
2005-09-02 04:08:56 +04:00
unregister_netdev ( dev ) ;
2006-05-01 10:13:33 +04:00
free_irq ( pdev - > irq , dev ) ;
2005-09-02 04:08:56 +04:00
pci_set_drvdata ( pdev , NULL ) ;
free_orinocodev ( dev ) ;
pci_iounmap ( pdev , priv - > hw . iobase ) ;
2006-04-07 12:10:57 +04:00
pci_iounmap ( pdev , card - > attr_io ) ;
pci_iounmap ( pdev , card - > bridge_io ) ;
2005-09-02 04:08:56 +04:00
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
}
2006-04-07 12:10:57 +04:00
static struct pci_device_id orinoco_nortel_id_table [ ] = {
2005-09-02 04:08:56 +04:00
/* Nortel emobility PCI */
{ 0x126c , 0x8030 , PCI_ANY_ID , PCI_ANY_ID , } ,
2005-12-17 02:57:10 +03:00
/* Symbol LA-4123 PCI */
{ 0x1562 , 0x0001 , PCI_ANY_ID , PCI_ANY_ID , } ,
2005-09-02 04:08:56 +04:00
{ 0 , } ,
} ;
2006-04-07 12:10:57 +04:00
MODULE_DEVICE_TABLE ( pci , orinoco_nortel_id_table ) ;
2005-09-02 04:08:56 +04:00
2006-04-07 12:10:57 +04:00
static struct pci_driver orinoco_nortel_driver = {
2006-04-07 12:10:55 +04:00
. name = DRIVER_NAME ,
2006-04-07 12:10:57 +04:00
. id_table = orinoco_nortel_id_table ,
. probe = orinoco_nortel_init_one ,
. remove = __devexit_p ( orinoco_nortel_remove_one ) ,
. suspend = orinoco_pci_suspend ,
. resume = orinoco_pci_resume ,
2005-09-02 04:08:56 +04:00
} ;
static char version [ ] __initdata = DRIVER_NAME " " DRIVER_VERSION
" (Tobias Hoffmann & Christoph Jungegger <disdos@traum404.de>) " ;
MODULE_AUTHOR ( " Christoph Jungegger <disdos@traum404.de> " ) ;
MODULE_DESCRIPTION
( " Driver for wireless LAN cards using the Nortel PCI bridge " ) ;
MODULE_LICENSE ( " Dual MPL/GPL " ) ;
2006-04-07 12:10:57 +04:00
static int __init orinoco_nortel_init ( void )
2005-09-02 04:08:56 +04:00
{
printk ( KERN_DEBUG " %s \n " , version ) ;
2006-08-20 01:48:59 +04:00
return pci_register_driver ( & orinoco_nortel_driver ) ;
2005-09-02 04:08:56 +04:00
}
2006-04-07 12:10:57 +04:00
static void __exit orinoco_nortel_exit ( void )
2005-09-02 04:08:56 +04:00
{
2006-04-07 12:10:57 +04:00
pci_unregister_driver ( & orinoco_nortel_driver ) ;
2005-09-02 04:08:56 +04:00
}
2006-04-07 12:10:57 +04:00
module_init ( orinoco_nortel_init ) ;
module_exit ( orinoco_nortel_exit ) ;
2005-09-02 04:08:56 +04:00
/*
* Local variables :
* c - indent - level : 8
* c - basic - offset : 8
* tab - width : 8
* End :
*/