2005-04-17 02:20:36 +04:00
/*
* Driver for the media bay on the PowerBook 3400 and 2400.
*
* Copyright ( C ) 1998 Paul Mackerras .
*
* Various evolutions by Benjamin Herrenschmidt & Henry Worth
*
* 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 .
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/sched.h>
# include <linux/timer.h>
# include <linux/stddef.h>
# include <linux/init.h>
2007-12-13 06:12:58 +03:00
# include <linux/kthread.h>
2008-06-10 03:26:09 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <asm/prom.h>
# include <asm/pgtable.h>
# include <asm/io.h>
# include <asm/machdep.h>
# include <asm/pmac_feature.h>
# include <asm/mediabay.h>
# include <asm/sections.h>
# include <asm/ohare.h>
# include <asm/heathrow.h>
# include <asm/keylargo.h>
# include <linux/adb.h>
# include <linux/pmu.h>
# define MB_FCR32(bay, r) ((bay)->base + ((r) >> 2))
# define MB_FCR8(bay, r) (((volatile u8 __iomem *)((bay)->base)) + (r))
# define MB_IN32(bay,r) (in_le32(MB_FCR32(bay,r)))
# define MB_OUT32(bay,r,v) (out_le32(MB_FCR32(bay,r), (v)))
# define MB_BIS(bay,r,v) (MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
# define MB_BIC(bay,r,v) (MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
# define MB_IN8(bay,r) (in_8(MB_FCR8(bay,r)))
# define MB_OUT8(bay,r,v) (out_8(MB_FCR8(bay,r), (v)))
struct media_bay_info ;
struct mb_ops {
char * name ;
void ( * init ) ( struct media_bay_info * bay ) ;
u8 ( * content ) ( struct media_bay_info * bay ) ;
void ( * power ) ( struct media_bay_info * bay , int on_off ) ;
int ( * setup_bus ) ( struct media_bay_info * bay , u8 device_id ) ;
void ( * un_reset ) ( struct media_bay_info * bay ) ;
void ( * un_reset_ide ) ( struct media_bay_info * bay ) ;
} ;
struct media_bay_info {
u32 __iomem * base ;
int content_id ;
int state ;
int last_value ;
int value_count ;
int timer ;
struct macio_dev * mdev ;
2012-05-21 23:57:39 +04:00
const struct mb_ops * ops ;
2005-04-17 02:20:36 +04:00
int index ;
int cached_gpio ;
int sleeping ;
2009-12-01 17:36:28 +03:00
int user_lock ;
2008-06-10 03:26:09 +04:00
struct mutex lock ;
2005-04-17 02:20:36 +04:00
} ;
# define MAX_BAYS 2
static struct media_bay_info media_bays [ MAX_BAYS ] ;
2009-12-01 17:36:28 +03:00
static int media_bay_count = 0 ;
2005-04-17 02:20:36 +04:00
/*
* Wait that number of ms between each step in normal polling mode
*/
# define MB_POLL_DELAY 25
/*
* Consider the media - bay ID value stable if it is the same for
* this number of milliseconds
*/
# define MB_STABLE_DELAY 100
/* Wait after powering up the media bay this delay in ms
* timeout bumped for some powerbooks
*/
# define MB_POWER_DELAY 200
/*
* Hold the media - bay reset signal true for this many ticks
* after a device is inserted before releasing it .
*/
# define MB_RESET_DELAY 50
/*
* Wait this long after the reset signal is released and before doing
* further operations . After this delay , the IDE reset signal is released
* too for an IDE device
*/
# define MB_SETUP_DELAY 100
/*
* Wait this many ticks after an IDE device ( e . g . CD - ROM ) is inserted
2009-12-01 17:36:28 +03:00
* ( or until the device is ready ) before calling into the driver
2005-04-17 02:20:36 +04:00
*/
# define MB_IDE_WAIT 1000
/*
* States of a media bay
*/
enum {
mb_empty = 0 , /* Idle */
mb_powering_up , /* power bit set, waiting MB_POWER_DELAY */
mb_enabling_bay , /* enable bits set, waiting MB_RESET_DELAY */
mb_resetting , /* reset bit unset, waiting MB_SETUP_DELAY */
mb_ide_resetting , /* IDE reset bit unser, waiting MB_IDE_WAIT */
mb_up , /* Media bay full */
mb_powering_down /* Powering down (avoid too fast down/up) */
} ;
# define MB_POWER_SOUND 0x08
# define MB_POWER_FLOPPY 0x04
# define MB_POWER_ATA 0x02
# define MB_POWER_PCI 0x01
# define MB_POWER_OFF 0x00
/*
* Functions for polling content of media bay
*/
2005-09-17 19:36:54 +04:00
static u8
2005-04-17 02:20:36 +04:00
ohare_mb_content ( struct media_bay_info * bay )
{
return ( MB_IN32 ( bay , OHARE_MBCR ) > > 12 ) & 7 ;
}
2005-09-17 19:36:54 +04:00
static u8
2005-04-17 02:20:36 +04:00
heathrow_mb_content ( struct media_bay_info * bay )
{
return ( MB_IN32 ( bay , HEATHROW_MBCR ) > > 12 ) & 7 ;
}
2005-09-17 19:36:54 +04:00
static u8
2005-04-17 02:20:36 +04:00
keylargo_mb_content ( struct media_bay_info * bay )
{
int new_gpio ;
new_gpio = MB_IN8 ( bay , KL_GPIO_MEDIABAY_IRQ ) & KEYLARGO_GPIO_INPUT_DATA ;
if ( new_gpio ) {
bay - > cached_gpio = new_gpio ;
return MB_NO ;
} else if ( bay - > cached_gpio ! = new_gpio ) {
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_ENABLE ) ;
( void ) MB_IN32 ( bay , KEYLARGO_MBCR ) ;
udelay ( 5 ) ;
MB_BIC ( bay , KEYLARGO_MBCR , 0x0000000F ) ;
( void ) MB_IN32 ( bay , KEYLARGO_MBCR ) ;
udelay ( 5 ) ;
bay - > cached_gpio = new_gpio ;
}
return ( MB_IN32 ( bay , KEYLARGO_MBCR ) > > 4 ) & 7 ;
}
/*
* Functions for powering up / down the bay , puts the bay device
* into reset state as well
*/
2005-09-17 19:36:54 +04:00
static void
2005-04-17 02:20:36 +04:00
ohare_mb_power ( struct media_bay_info * bay , int on_off )
{
if ( on_off ) {
/* Power up device, assert it's reset line */
MB_BIC ( bay , OHARE_FCR , OH_BAY_RESET_N ) ;
MB_BIC ( bay , OHARE_FCR , OH_BAY_POWER_N ) ;
} else {
/* Disable all devices */
MB_BIC ( bay , OHARE_FCR , OH_BAY_DEV_MASK ) ;
MB_BIC ( bay , OHARE_FCR , OH_FLOPPY_ENABLE ) ;
/* Cut power from bay, release reset line */
MB_BIS ( bay , OHARE_FCR , OH_BAY_POWER_N ) ;
MB_BIS ( bay , OHARE_FCR , OH_BAY_RESET_N ) ;
MB_BIS ( bay , OHARE_FCR , OH_IDE1_RESET_N ) ;
}
MB_BIC ( bay , OHARE_MBCR , 0x00000F00 ) ;
}
2005-09-17 19:36:54 +04:00
static void
2005-04-17 02:20:36 +04:00
heathrow_mb_power ( struct media_bay_info * bay , int on_off )
{
if ( on_off ) {
/* Power up device, assert it's reset line */
MB_BIC ( bay , HEATHROW_FCR , HRW_BAY_RESET_N ) ;
MB_BIC ( bay , HEATHROW_FCR , HRW_BAY_POWER_N ) ;
} else {
/* Disable all devices */
MB_BIC ( bay , HEATHROW_FCR , HRW_BAY_DEV_MASK ) ;
MB_BIC ( bay , HEATHROW_FCR , HRW_SWIM_ENABLE ) ;
/* Cut power from bay, release reset line */
MB_BIS ( bay , HEATHROW_FCR , HRW_BAY_POWER_N ) ;
MB_BIS ( bay , HEATHROW_FCR , HRW_BAY_RESET_N ) ;
MB_BIS ( bay , HEATHROW_FCR , HRW_IDE1_RESET_N ) ;
}
MB_BIC ( bay , HEATHROW_MBCR , 0x00000F00 ) ;
}
2005-09-17 19:36:54 +04:00
static void
2005-04-17 02:20:36 +04:00
keylargo_mb_power ( struct media_bay_info * bay , int on_off )
{
if ( on_off ) {
/* Power up device, assert it's reset line */
MB_BIC ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_DEV_RESET ) ;
MB_BIC ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_DEV_POWER ) ;
} else {
/* Disable all devices */
MB_BIC ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_DEV_MASK ) ;
MB_BIC ( bay , KEYLARGO_FCR1 , KL1_EIDE0_ENABLE ) ;
/* Cut power from bay, release reset line */
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_DEV_POWER ) ;
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_DEV_RESET ) ;
MB_BIS ( bay , KEYLARGO_FCR1 , KL1_EIDE0_RESET_N ) ;
}
MB_BIC ( bay , KEYLARGO_MBCR , 0x0000000F ) ;
}
/*
* Functions for configuring the media bay for a given type of device ,
* enable the related busses
*/
2005-09-17 19:36:54 +04:00
static int
2005-04-17 02:20:36 +04:00
ohare_mb_setup_bus ( struct media_bay_info * bay , u8 device_id )
{
switch ( device_id ) {
case MB_FD :
case MB_FD1 :
MB_BIS ( bay , OHARE_FCR , OH_BAY_FLOPPY_ENABLE ) ;
MB_BIS ( bay , OHARE_FCR , OH_FLOPPY_ENABLE ) ;
return 0 ;
case MB_CD :
MB_BIC ( bay , OHARE_FCR , OH_IDE1_RESET_N ) ;
MB_BIS ( bay , OHARE_FCR , OH_BAY_IDE_ENABLE ) ;
return 0 ;
case MB_PCI :
MB_BIS ( bay , OHARE_FCR , OH_BAY_PCI_ENABLE ) ;
return 0 ;
}
return - ENODEV ;
}
2005-09-17 19:36:54 +04:00
static int
2005-04-17 02:20:36 +04:00
heathrow_mb_setup_bus ( struct media_bay_info * bay , u8 device_id )
{
switch ( device_id ) {
case MB_FD :
case MB_FD1 :
MB_BIS ( bay , HEATHROW_FCR , HRW_BAY_FLOPPY_ENABLE ) ;
MB_BIS ( bay , HEATHROW_FCR , HRW_SWIM_ENABLE ) ;
return 0 ;
case MB_CD :
MB_BIC ( bay , HEATHROW_FCR , HRW_IDE1_RESET_N ) ;
MB_BIS ( bay , HEATHROW_FCR , HRW_BAY_IDE_ENABLE ) ;
return 0 ;
case MB_PCI :
MB_BIS ( bay , HEATHROW_FCR , HRW_BAY_PCI_ENABLE ) ;
return 0 ;
}
return - ENODEV ;
}
2005-09-17 19:36:54 +04:00
static int
2005-04-17 02:20:36 +04:00
keylargo_mb_setup_bus ( struct media_bay_info * bay , u8 device_id )
{
switch ( device_id ) {
case MB_CD :
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_IDE_ENABLE ) ;
MB_BIC ( bay , KEYLARGO_FCR1 , KL1_EIDE0_RESET_N ) ;
MB_BIS ( bay , KEYLARGO_FCR1 , KL1_EIDE0_ENABLE ) ;
return 0 ;
case MB_PCI :
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_PCI_ENABLE ) ;
return 0 ;
case MB_SOUND :
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_SOUND_ENABLE ) ;
return 0 ;
}
return - ENODEV ;
}
/*
* Functions for tweaking resets
*/
2005-09-17 19:36:54 +04:00
static void
2005-04-17 02:20:36 +04:00
ohare_mb_un_reset ( struct media_bay_info * bay )
{
MB_BIS ( bay , OHARE_FCR , OH_BAY_RESET_N ) ;
}
2005-09-17 19:36:54 +04:00
static void keylargo_mb_init ( struct media_bay_info * bay )
2005-04-17 02:20:36 +04:00
{
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_ENABLE ) ;
}
2005-09-17 19:36:54 +04:00
static void heathrow_mb_un_reset ( struct media_bay_info * bay )
2005-04-17 02:20:36 +04:00
{
MB_BIS ( bay , HEATHROW_FCR , HRW_BAY_RESET_N ) ;
}
2005-09-17 19:36:54 +04:00
static void keylargo_mb_un_reset ( struct media_bay_info * bay )
2005-04-17 02:20:36 +04:00
{
MB_BIS ( bay , KEYLARGO_MBCR , KL_MBCR_MB0_DEV_RESET ) ;
}
2005-09-17 19:36:54 +04:00
static void ohare_mb_un_reset_ide ( struct media_bay_info * bay )
2005-04-17 02:20:36 +04:00
{
MB_BIS ( bay , OHARE_FCR , OH_IDE1_RESET_N ) ;
}
2005-09-17 19:36:54 +04:00
static void heathrow_mb_un_reset_ide ( struct media_bay_info * bay )
2005-04-17 02:20:36 +04:00
{
MB_BIS ( bay , HEATHROW_FCR , HRW_IDE1_RESET_N ) ;
}
2005-09-17 19:36:54 +04:00
static void keylargo_mb_un_reset_ide ( struct media_bay_info * bay )
2005-04-17 02:20:36 +04:00
{
MB_BIS ( bay , KEYLARGO_FCR1 , KL1_EIDE0_RESET_N ) ;
}
2005-09-17 19:36:54 +04:00
static inline void set_mb_power ( struct media_bay_info * bay , int onoff )
2005-04-17 02:20:36 +04:00
{
/* Power up up and assert the bay reset line */
if ( onoff ) {
bay - > ops - > power ( bay , 1 ) ;
bay - > state = mb_powering_up ;
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: powering up \n " , bay - > index ) ;
2005-04-17 02:20:36 +04:00
} else {
/* Make sure everything is powered down & disabled */
bay - > ops - > power ( bay , 0 ) ;
bay - > state = mb_powering_down ;
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: powering down \n " , bay - > index ) ;
2005-04-17 02:20:36 +04:00
}
bay - > timer = msecs_to_jiffies ( MB_POWER_DELAY ) ;
}
2005-09-17 19:36:54 +04:00
static void poll_media_bay ( struct media_bay_info * bay )
2005-04-17 02:20:36 +04:00
{
int id = bay - > ops - > content ( bay ) ;
2009-12-01 17:36:28 +03:00
static char * mb_content_types [ ] = {
" a floppy drive " ,
" a floppy drive " ,
2012-01-23 21:26:36 +04:00
" an unsupported audio device " ,
2009-12-01 17:36:28 +03:00
" an ATA device " ,
" an unsupported PCI device " ,
" an unknown device " ,
} ;
if ( id ! = bay - > last_value ) {
2005-04-17 02:20:36 +04:00
bay - > last_value = id ;
bay - > value_count = 0 ;
2009-12-01 17:36:28 +03:00
return ;
}
if ( id = = bay - > content_id )
return ;
bay - > value_count + = msecs_to_jiffies ( MB_POLL_DELAY ) ;
if ( bay - > value_count > = msecs_to_jiffies ( MB_STABLE_DELAY ) ) {
/* If the device type changes without going thru
* " MB_NO " , we force a pass by " MB_NO " to make sure
* things are properly reset
*/
if ( ( id ! = MB_NO ) & & ( bay - > content_id ! = MB_NO ) ) {
id = MB_NO ;
pr_debug ( " mediabay%d: forcing MB_NO \n " , bay - > index ) ;
}
pr_debug ( " mediabay%d: switching to %d \n " , bay - > index , id ) ;
set_mb_power ( bay , id ! = MB_NO ) ;
bay - > content_id = id ;
if ( id > = MB_NO | | id < 0 )
printk ( KERN_INFO " mediabay%d: Bay is now empty \n " , bay - > index ) ;
else
printk ( KERN_INFO " mediabay%d: Bay contains %s \n " ,
bay - > index , mb_content_types [ id ] ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-12-01 17:36:28 +03:00
int check_media_bay ( struct macio_dev * baydev )
2005-04-17 02:20:36 +04:00
{
2009-12-01 17:36:28 +03:00
struct media_bay_info * bay ;
int id ;
2005-04-17 02:20:36 +04:00
2009-12-01 17:36:28 +03:00
if ( baydev = = NULL )
return MB_NO ;
/* This returns an instant snapshot, not locking, sine
* we may be called with the bay lock held . The resulting
* fuzzyness of the result if called at the wrong time is
* not actually a huge deal
*/
bay = macio_get_drvdata ( baydev ) ;
if ( bay = = NULL )
return MB_NO ;
id = bay - > content_id ;
if ( bay - > state ! = mb_up )
return MB_NO ;
if ( id = = MB_FD1 )
return MB_FD ;
return id ;
2005-04-17 02:20:36 +04:00
}
2009-12-01 17:36:28 +03:00
EXPORT_SYMBOL_GPL ( check_media_bay ) ;
2005-04-17 02:20:36 +04:00
2009-12-01 17:36:28 +03:00
void lock_media_bay ( struct macio_dev * baydev )
2005-04-17 02:20:36 +04:00
{
2009-12-01 17:36:28 +03:00
struct media_bay_info * bay ;
2008-02-06 04:57:50 +03:00
2009-12-01 17:36:28 +03:00
if ( baydev = = NULL )
return ;
bay = macio_get_drvdata ( baydev ) ;
if ( bay = = NULL )
return ;
mutex_lock ( & bay - > lock ) ;
bay - > user_lock = 1 ;
2005-04-17 02:20:36 +04:00
}
2009-12-01 17:36:28 +03:00
EXPORT_SYMBOL_GPL ( lock_media_bay ) ;
2005-04-17 02:20:36 +04:00
2009-12-01 17:36:28 +03:00
void unlock_media_bay ( struct macio_dev * baydev )
2005-04-17 02:20:36 +04:00
{
2009-12-01 17:36:28 +03:00
struct media_bay_info * bay ;
2005-04-17 02:20:36 +04:00
2009-12-01 17:36:28 +03:00
if ( baydev = = NULL )
return ;
bay = macio_get_drvdata ( baydev ) ;
if ( bay = = NULL )
return ;
if ( bay - > user_lock ) {
bay - > user_lock = 0 ;
mutex_unlock ( & bay - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2009-12-01 17:36:28 +03:00
}
EXPORT_SYMBOL_GPL ( unlock_media_bay ) ;
2008-02-06 04:57:50 +03:00
2009-12-01 17:36:28 +03:00
static int mb_broadcast_hotplug ( struct device * dev , void * data )
{
struct media_bay_info * bay = data ;
struct macio_dev * mdev ;
struct macio_driver * drv ;
int state ;
if ( dev - > bus ! = & macio_bus_type )
return 0 ;
state = bay - > state = = mb_up ? bay - > content_id : MB_NO ;
if ( state = = MB_FD1 )
state = MB_FD ;
mdev = to_macio_device ( dev ) ;
drv = to_macio_driver ( dev - > driver ) ;
if ( dev - > driver & & drv - > mediabay_event )
drv - > mediabay_event ( mdev , state ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-09-17 19:36:54 +04:00
static void media_bay_step ( int i )
2005-04-17 02:20:36 +04:00
{
struct media_bay_info * bay = & media_bays [ i ] ;
/* We don't poll when powering down */
if ( bay - > state ! = mb_powering_down )
poll_media_bay ( bay ) ;
2009-12-01 17:36:28 +03:00
/* If timer expired run state machine */
if ( bay - > timer ! = 0 ) {
2005-04-17 02:20:36 +04:00
bay - > timer - = msecs_to_jiffies ( MB_POLL_DELAY ) ;
if ( bay - > timer > 0 )
return ;
bay - > timer = 0 ;
}
switch ( bay - > state ) {
case mb_powering_up :
if ( bay - > ops - > setup_bus ( bay , bay - > last_value ) < 0 ) {
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: device not supported (kind:%d) \n " ,
i , bay - > content_id ) ;
2005-04-17 02:20:36 +04:00
set_mb_power ( bay , 0 ) ;
break ;
}
bay - > timer = msecs_to_jiffies ( MB_RESET_DELAY ) ;
bay - > state = mb_enabling_bay ;
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: enabling (kind:%d) \n " , i , bay - > content_id ) ;
2005-04-17 02:20:36 +04:00
break ;
case mb_enabling_bay :
bay - > ops - > un_reset ( bay ) ;
bay - > timer = msecs_to_jiffies ( MB_SETUP_DELAY ) ;
bay - > state = mb_resetting ;
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: releasing bay reset (kind:%d) \n " ,
i , bay - > content_id ) ;
2005-04-17 02:20:36 +04:00
break ;
case mb_resetting :
if ( bay - > content_id ! = MB_CD ) {
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: bay is up (kind:%d) \n " , i ,
bay - > content_id ) ;
2005-04-17 02:20:36 +04:00
bay - > state = mb_up ;
2009-12-01 17:36:28 +03:00
device_for_each_child ( & bay - > mdev - > ofdev . dev ,
bay , mb_broadcast_hotplug ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: releasing ATA reset (kind:%d) \n " ,
i , bay - > content_id ) ;
2005-04-17 02:20:36 +04:00
bay - > ops - > un_reset_ide ( bay ) ;
bay - > timer = msecs_to_jiffies ( MB_IDE_WAIT ) ;
bay - > state = mb_ide_resetting ;
break ;
2009-12-01 17:36:28 +03:00
2005-04-17 02:20:36 +04:00
case mb_ide_resetting :
2009-12-01 17:36:28 +03:00
pr_debug ( " mediabay%d: bay is up (kind:%d) \n " , i , bay - > content_id ) ;
bay - > state = mb_up ;
device_for_each_child ( & bay - > mdev - > ofdev . dev ,
bay , mb_broadcast_hotplug ) ;
2005-04-17 02:20:36 +04:00
break ;
2009-12-01 17:36:28 +03:00
2005-04-17 02:20:36 +04:00
case mb_powering_down :
bay - > state = mb_empty ;
2009-12-01 17:36:28 +03:00
device_for_each_child ( & bay - > mdev - > ofdev . dev ,
bay , mb_broadcast_hotplug ) ;
pr_debug ( " mediabay%d: end of power down \n " , i ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
/*
* This procedure runs as a kernel thread to poll the media bay
* once each tick and register and unregister the IDE interface
* with the IDE driver . It needs to be a thread because
* ide_register can ' t be called from interrupt context .
*/
2005-09-17 19:36:54 +04:00
static int media_bay_task ( void * x )
2005-04-17 02:20:36 +04:00
{
int i ;
2007-12-13 06:12:58 +03:00
while ( ! kthread_should_stop ( ) ) {
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < media_bay_count ; + + i ) {
2008-06-10 03:26:09 +04:00
mutex_lock ( & media_bays [ i ] . lock ) ;
2005-04-17 02:20:36 +04:00
if ( ! media_bays [ i ] . sleeping )
media_bay_step ( i ) ;
2008-06-10 03:26:09 +04:00
mutex_unlock ( & media_bays [ i ] . lock ) ;
2005-04-17 02:20:36 +04:00
}
msleep_interruptible ( MB_POLL_DELAY ) ;
}
2007-12-13 06:12:58 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2012-12-22 03:03:50 +04:00
static int media_bay_attach ( struct macio_dev * mdev ,
const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
struct media_bay_info * bay ;
u32 __iomem * regbase ;
struct device_node * ofnode ;
2005-12-13 10:01:21 +03:00
unsigned long base ;
2005-04-17 02:20:36 +04:00
int i ;
2010-04-14 03:12:29 +04:00
ofnode = mdev - > ofdev . dev . of_node ;
2005-04-17 02:20:36 +04:00
if ( macio_resource_count ( mdev ) < 1 )
return - ENODEV ;
if ( macio_request_resources ( mdev , " media-bay " ) )
return - EBUSY ;
/* Media bay registers are located at the beginning of the
2005-12-13 10:01:21 +03:00
* mac - io chip , for now , we trick and align down the first
* resource passed in
2005-04-17 02:20:36 +04:00
*/
2005-12-13 10:01:21 +03:00
base = macio_resource_start ( mdev , 0 ) & 0xffff0000u ;
regbase = ( u32 __iomem * ) ioremap ( base , 0x100 ) ;
2005-04-17 02:20:36 +04:00
if ( regbase = = NULL ) {
macio_release_resources ( mdev ) ;
return - ENOMEM ;
}
i = media_bay_count + + ;
bay = & media_bays [ i ] ;
bay - > mdev = mdev ;
bay - > base = regbase ;
bay - > index = i ;
bay - > ops = match - > data ;
bay - > sleeping = 0 ;
2008-06-10 03:26:09 +04:00
mutex_init ( & bay - > lock ) ;
2005-04-17 02:20:36 +04:00
/* Init HW probing */
if ( bay - > ops - > init )
bay - > ops - > init ( bay ) ;
printk ( KERN_INFO " mediabay%d: Registered %s media-bay \n " , i , bay - > ops - > name ) ;
/* Force an immediate detect */
set_mb_power ( bay , 0 ) ;
msleep ( MB_POWER_DELAY ) ;
bay - > content_id = MB_NO ;
bay - > last_value = bay - > ops - > content ( bay ) ;
bay - > value_count = msecs_to_jiffies ( MB_STABLE_DELAY ) ;
bay - > state = mb_empty ;
/* Mark us ready by filling our mdev data */
macio_set_drvdata ( mdev , bay ) ;
/* Startup kernel thread */
if ( i = = 0 )
2007-12-13 06:12:58 +03:00
kthread_run ( media_bay_task , NULL , " media-bay " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-17 19:36:54 +04:00
static int media_bay_suspend ( struct macio_dev * mdev , pm_message_t state )
2005-04-17 02:20:36 +04:00
{
struct media_bay_info * bay = macio_get_drvdata ( mdev ) ;
2008-02-23 21:13:25 +03:00
if ( state . event ! = mdev - > ofdev . dev . power . power_state . event
& & ( state . event & PM_EVENT_SLEEP ) ) {
2008-06-10 03:26:09 +04:00
mutex_lock ( & bay - > lock ) ;
2005-04-17 02:20:36 +04:00
bay - > sleeping = 1 ;
set_mb_power ( bay , 0 ) ;
2008-06-10 03:26:09 +04:00
mutex_unlock ( & bay - > lock ) ;
2005-04-17 02:20:36 +04:00
msleep ( MB_POLL_DELAY ) ;
mdev - > ofdev . dev . power . power_state = state ;
}
return 0 ;
}
2005-09-17 19:36:54 +04:00
static int media_bay_resume ( struct macio_dev * mdev )
2005-04-17 02:20:36 +04:00
{
struct media_bay_info * bay = macio_get_drvdata ( mdev ) ;
2005-09-04 02:56:57 +04:00
if ( mdev - > ofdev . dev . power . power_state . event ! = PM_EVENT_ON ) {
2005-09-04 02:56:56 +04:00
mdev - > ofdev . dev . power . power_state = PMSG_ON ;
2005-04-17 02:20:36 +04:00
/* We re-enable the bay using it's previous content
only if it did not change . Note those bozo timings ,
they seem to help the 3400 get it right .
*/
/* Force MB power to 0 */
2008-06-10 03:26:09 +04:00
mutex_lock ( & bay - > lock ) ;
2005-04-17 02:20:36 +04:00
set_mb_power ( bay , 0 ) ;
msleep ( MB_POWER_DELAY ) ;
if ( bay - > ops - > content ( bay ) ! = bay - > content_id ) {
2009-12-01 17:36:28 +03:00
printk ( " mediabay%d: Content changed during sleep... \n " , bay - > index ) ;
2008-06-10 03:26:09 +04:00
mutex_unlock ( & bay - > lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
set_mb_power ( bay , 1 ) ;
bay - > last_value = bay - > content_id ;
bay - > value_count = msecs_to_jiffies ( MB_STABLE_DELAY ) ;
bay - > timer = msecs_to_jiffies ( MB_POWER_DELAY ) ;
do {
msleep ( MB_POLL_DELAY ) ;
media_bay_step ( bay - > index ) ;
} while ( ( bay - > state ! = mb_empty ) & &
( bay - > state ! = mb_up ) ) ;
bay - > sleeping = 0 ;
2008-06-10 03:26:09 +04:00
mutex_unlock ( & bay - > lock ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/* Definitions of "ops" structures.
*/
2012-07-13 20:24:26 +04:00
static const struct mb_ops ohare_mb_ops = {
2005-04-17 02:20:36 +04:00
. name = " Ohare " ,
. content = ohare_mb_content ,
. power = ohare_mb_power ,
. setup_bus = ohare_mb_setup_bus ,
. un_reset = ohare_mb_un_reset ,
. un_reset_ide = ohare_mb_un_reset_ide ,
} ;
2012-07-13 20:24:26 +04:00
static const struct mb_ops heathrow_mb_ops = {
2005-04-17 02:20:36 +04:00
. name = " Heathrow " ,
. content = heathrow_mb_content ,
. power = heathrow_mb_power ,
. setup_bus = heathrow_mb_setup_bus ,
. un_reset = heathrow_mb_un_reset ,
. un_reset_ide = heathrow_mb_un_reset_ide ,
} ;
2012-07-13 20:24:26 +04:00
static const struct mb_ops keylargo_mb_ops = {
2005-04-17 02:20:36 +04:00
. name = " KeyLargo " ,
. init = keylargo_mb_init ,
. content = keylargo_mb_content ,
. power = keylargo_mb_power ,
. setup_bus = keylargo_mb_setup_bus ,
. un_reset = keylargo_mb_un_reset ,
. un_reset_ide = keylargo_mb_un_reset_ide ,
} ;
/*
* It seems that the bit for the media - bay interrupt in the IRQ_LEVEL
* register is always set when there is something in the media bay .
* This causes problems for the interrupt code if we attach an interrupt
* handler to the media - bay interrupt , because it tends to go into
* an infinite loop calling the media bay interrupt handler .
* Therefore we do it all by polling the media bay once each tick .
*/
2005-07-06 23:44:41 +04:00
static struct of_device_id media_bay_match [ ] =
2005-04-17 02:20:36 +04:00
{
{
. name = " media-bay " ,
. compatible = " keylargo-media-bay " ,
. data = & keylargo_mb_ops ,
} ,
{
. name = " media-bay " ,
. compatible = " heathrow-media-bay " ,
. data = & heathrow_mb_ops ,
} ,
{
. name = " media-bay " ,
. compatible = " ohare-media-bay " ,
. data = & ohare_mb_ops ,
} ,
{ } ,
} ;
static struct macio_driver media_bay_driver =
{
2010-06-02 11:09:18 +04:00
. driver = {
. name = " media-bay " ,
. of_match_table = media_bay_match ,
} ,
2005-04-17 02:20:36 +04:00
. probe = media_bay_attach ,
. suspend = media_bay_suspend ,
. resume = media_bay_resume
} ;
static int __init media_bay_init ( void )
{
int i ;
for ( i = 0 ; i < MAX_BAYS ; i + + ) {
memset ( ( char * ) & media_bays [ i ] , 0 , sizeof ( struct media_bay_info ) ) ;
media_bays [ i ] . content_id = - 1 ;
}
2006-03-28 16:15:54 +04:00
if ( ! machine_is ( powermac ) )
return 0 ;
2005-04-17 02:20:36 +04:00
macio_register_driver ( & media_bay_driver ) ;
return 0 ;
}
device_initcall ( media_bay_init ) ;