2005-04-17 02:20:36 +04:00
/* airport.c
*
* A driver for " Hermes " chipset based Apple Airport wireless
* card .
*
* Copyright notice & release notes in file orinoco . c
*
* Note specific to airport stub :
*
* 0.05 : first version of the new split driver
* 0.06 : fix possible hang on powerup , add sleep support
*/
# define DRIVER_NAME "airport"
# define PFX DRIVER_NAME ": "
# include <linux/config.h>
# 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-04-17 02:20:36 +04:00
# include <asm/pmac_feature.h>
# include "orinoco.h"
# define AIRPORT_IO_LEN (0x1000) /* one page */
struct airport {
struct macio_dev * mdev ;
void __iomem * vaddr ;
int irq_requested ;
int ndev_registered ;
} ;
static int
2005-04-17 02:25:25 +04:00
airport_suspend ( struct macio_dev * mdev , pm_message_t state )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev = dev_get_drvdata ( & mdev - > ofdev . dev ) ;
struct orinoco_private * priv = netdev_priv ( dev ) ;
unsigned long flags ;
int err ;
printk ( KERN_DEBUG " %s: Airport entering sleep mode \n " , dev - > name ) ;
err = orinoco_lock ( priv , & flags ) ;
if ( err ) {
printk ( KERN_ERR " %s: hw_unavailable on PBOOK_SLEEP_NOW \n " ,
dev - > name ) ;
return 0 ;
}
err = __orinoco_down ( dev ) ;
if ( err )
printk ( KERN_WARNING " %s: PBOOK_SLEEP_NOW: Error %d downing interface \n " ,
dev - > name , err ) ;
netif_device_detach ( dev ) ;
priv - > hw_unavailable + + ;
orinoco_unlock ( priv , & flags ) ;
disable_irq ( dev - > irq ) ;
pmac_call_feature ( PMAC_FTR_AIRPORT_ENABLE , macio_get_of_node ( mdev ) , 0 , 0 ) ;
return 0 ;
}
static int
airport_resume ( struct macio_dev * mdev )
{
struct net_device * dev = dev_get_drvdata ( & mdev - > ofdev . dev ) ;
struct orinoco_private * priv = netdev_priv ( dev ) ;
unsigned long flags ;
int err ;
printk ( KERN_DEBUG " %s: Airport waking up \n " , dev - > name ) ;
pmac_call_feature ( PMAC_FTR_AIRPORT_ENABLE , macio_get_of_node ( mdev ) , 0 , 1 ) ;
msleep ( 200 ) ;
enable_irq ( dev - > irq ) ;
err = orinoco_reinit_firmware ( dev ) ;
if ( err ) {
printk ( KERN_ERR " %s: Error %d re-initializing firmware on PBOOK_WAKE \n " ,
dev - > name , err ) ;
return 0 ;
}
spin_lock_irqsave ( & priv - > lock , flags ) ;
netif_device_attach ( dev ) ;
priv - > hw_unavailable - - ;
if ( priv - > open & & ( ! priv - > hw_unavailable ) ) {
err = __orinoco_up ( dev ) ;
if ( err )
printk ( KERN_ERR " %s: Error %d restarting card on PBOOK_WAKE \n " ,
dev - > name , err ) ;
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return 0 ;
}
static int
airport_detach ( struct macio_dev * mdev )
{
struct net_device * dev = dev_get_drvdata ( & mdev - > ofdev . dev ) ;
struct orinoco_private * priv = netdev_priv ( dev ) ;
struct airport * card = priv - > card ;
if ( card - > ndev_registered )
unregister_netdev ( dev ) ;
card - > ndev_registered = 0 ;
if ( card - > irq_requested )
free_irq ( dev - > irq , dev ) ;
card - > irq_requested = 0 ;
if ( card - > vaddr )
iounmap ( card - > vaddr ) ;
card - > vaddr = NULL ;
macio_release_resource ( mdev , 0 ) ;
pmac_call_feature ( PMAC_FTR_AIRPORT_ENABLE , macio_get_of_node ( mdev ) , 0 , 0 ) ;
ssleep ( 1 ) ;
macio_set_drvdata ( mdev , NULL ) ;
free_orinocodev ( dev ) ;
return 0 ;
}
static int airport_hard_reset ( struct orinoco_private * priv )
{
/* It would be nice to power cycle the Airport for a real hard
* reset , but for some reason although it appears to
* re - initialize properly , it falls in a screaming heap
* shortly afterwards . */
#if 0
struct net_device * dev = priv - > ndev ;
struct airport * card = priv - > card ;
/* Vitally important. If we don't do this it seems we get an
* interrupt somewhere during the power cycle , since
* hw_unavailable is already set it doesn ' t get ACKed , we get
* into an interrupt loop and the the PMU decides to turn us
* off . */
disable_irq ( dev - > irq ) ;
pmac_call_feature ( PMAC_FTR_AIRPORT_ENABLE , macio_get_of_node ( card - > mdev ) , 0 , 0 ) ;
ssleep ( 1 ) ;
pmac_call_feature ( PMAC_FTR_AIRPORT_ENABLE , macio_get_of_node ( card - > mdev ) , 0 , 1 ) ;
ssleep ( 1 ) ;
enable_irq ( dev - > irq ) ;
ssleep ( 1 ) ;
# endif
return 0 ;
}
static int
2005-07-06 23:44:41 +04:00
airport_attach ( struct macio_dev * mdev , const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
struct orinoco_private * priv ;
struct net_device * dev ;
struct airport * card ;
unsigned long phys_addr ;
hermes_t * hw ;
if ( macio_resource_count ( mdev ) < 1 | | macio_irq_count ( mdev ) < 1 ) {
printk ( KERN_ERR PFX " Wrong interrupt/addresses in OF tree \n " ) ;
return - ENODEV ;
}
/* Allocate space for private device-specific data */
dev = alloc_orinocodev ( sizeof ( * card ) , airport_hard_reset ) ;
if ( ! dev ) {
printk ( KERN_ERR PFX " Cannot allocate network device \n " ) ;
return - ENODEV ;
}
priv = netdev_priv ( dev ) ;
card = priv - > card ;
hw = & priv - > hw ;
card - > mdev = mdev ;
if ( macio_request_resource ( mdev , 0 , " airport " ) ) {
printk ( KERN_ERR PFX " can't request IO resource ! \n " ) ;
free_orinocodev ( dev ) ;
return - EBUSY ;
}
SET_MODULE_OWNER ( dev ) ;
SET_NETDEV_DEV ( dev , & mdev - > ofdev . dev ) ;
macio_set_drvdata ( mdev , dev ) ;
/* Setup interrupts & base address */
dev - > irq = macio_irq ( mdev , 0 ) ;
phys_addr = macio_resource_start ( mdev , 0 ) ; /* Physical address */
printk ( KERN_DEBUG PFX " Physical address %lx \n " , phys_addr ) ;
dev - > base_addr = phys_addr ;
card - > vaddr = ioremap ( phys_addr , AIRPORT_IO_LEN ) ;
if ( ! card - > vaddr ) {
printk ( KERN_ERR PFX " ioremap() failed \n " ) ;
goto failed ;
}
hermes_struct_init ( hw , card - > vaddr , HERMES_16BIT_REGSPACING ) ;
/* Power up card */
pmac_call_feature ( PMAC_FTR_AIRPORT_ENABLE , macio_get_of_node ( mdev ) , 0 , 1 ) ;
ssleep ( 1 ) ;
/* Reset it before we get the interrupt */
hermes_init ( hw ) ;
if ( request_irq ( dev - > irq , orinoco_interrupt , 0 , dev - > name , dev ) ) {
printk ( KERN_ERR PFX " Couldn't get IRQ %d \n " , dev - > irq ) ;
goto failed ;
}
card - > irq_requested = 1 ;
/* Tell the stack we exist */
if ( register_netdev ( dev ) ! = 0 ) {
printk ( KERN_ERR PFX " register_netdev() failed \n " ) ;
goto failed ;
}
printk ( KERN_DEBUG PFX " Card registered for interface %s \n " , dev - > name ) ;
card - > ndev_registered = 1 ;
return 0 ;
failed :
airport_detach ( mdev ) ;
return - ENODEV ;
} /* airport_attach */
static char version [ ] __initdata = DRIVER_NAME " " DRIVER_VERSION
" (Benjamin Herrenschmidt <benh@kernel.crashing.org>) " ;
MODULE_AUTHOR ( " Benjamin Herrenschmidt <benh@kernel.crashing.org> " ) ;
MODULE_DESCRIPTION ( " Driver for the Apple Airport wireless card. " ) ;
MODULE_LICENSE ( " Dual MPL/GPL " ) ;
2005-07-06 23:44:41 +04:00
static struct of_device_id airport_match [ ] =
2005-04-17 02:20:36 +04:00
{
{
. name = " radio " ,
} ,
{ } ,
} ;
2005-07-06 23:44:41 +04:00
MODULE_DEVICE_TABLE ( of , airport_match ) ;
2005-04-17 02:20:36 +04:00
static struct macio_driver airport_driver =
{
. name = DRIVER_NAME ,
. match_table = airport_match ,
. probe = airport_attach ,
. remove = airport_detach ,
. suspend = airport_suspend ,
. resume = airport_resume ,
} ;
static int __init
init_airport ( void )
{
printk ( KERN_DEBUG " %s \n " , version ) ;
return macio_register_driver ( & airport_driver ) ;
}
static void __exit
exit_airport ( void )
{
return macio_unregister_driver ( & airport_driver ) ;
}
module_init ( init_airport ) ;
module_exit ( exit_airport ) ;