2007-09-21 15:55:55 +09:00
/*
* Core maple bus functionality
*
2009-02-27 16:07:32 +09:00
* Copyright ( C ) 2007 - 2009 Adrian McMenamin
2008-08-04 10:39:46 +09:00
* Copyright ( C ) 2001 - 2008 Paul Mundt
2009-02-27 16:07:32 +09:00
* Copyright ( C ) 2000 - 2001 YAEGASHI Takeshi
2007-09-21 15:55:55 +09:00
* Copyright ( C ) 2001 M . R . Brown
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/list.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/maple.h>
# include <linux/dma-mapping.h>
2008-07-29 22:10:56 +09:00
# include <linux/delay.h>
2011-07-31 19:18:02 -04:00
# include <linux/module.h>
2007-09-21 15:55:55 +09:00
# include <asm/cacheflush.h>
# include <asm/dma.h>
# include <asm/io.h>
2008-07-29 22:10:56 +09:00
# include <mach/dma.h>
# include <mach/sysasic.h>
2007-09-21 15:55:55 +09:00
2009-02-27 16:07:32 +09:00
MODULE_AUTHOR ( " Adrian McMenamin <adrian@mcmen.demon.co.uk> " ) ;
2007-09-21 15:55:55 +09:00
MODULE_DESCRIPTION ( " Maple bus driver for Dreamcast " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_SUPPORTED_DEVICE ( " {{SEGA, Dreamcast/Maple}} " ) ;
static void maple_dma_handler ( struct work_struct * work ) ;
static void maple_vblank_handler ( struct work_struct * work ) ;
static DECLARE_WORK ( maple_dma_process , maple_dma_handler ) ;
static DECLARE_WORK ( maple_vblank_process , maple_vblank_handler ) ;
static LIST_HEAD ( maple_waitq ) ;
static LIST_HEAD ( maple_sentq ) ;
2008-07-29 22:10:56 +09:00
/* mutex to protect queue of waiting packets */
static DEFINE_MUTEX ( maple_wlist_lock ) ;
2007-09-21 15:55:55 +09:00
2009-02-27 16:07:32 +09:00
static struct maple_driver maple_unsupported_device ;
2007-09-21 15:55:55 +09:00
static struct device maple_bus ;
static int subdevice_map [ MAPLE_PORTS ] ;
static unsigned long * maple_sendbuf , * maple_sendptr , * maple_lastptr ;
static unsigned long maple_pnp_time ;
2008-07-29 22:10:56 +09:00
static int started , scanning , fullscan ;
2007-09-21 15:55:55 +09:00
static struct kmem_cache * maple_queue_cache ;
struct maple_device_specify {
2008-02-06 22:46:21 +00:00
int port ;
int unit ;
2007-09-21 15:55:55 +09:00
} ;
2009-02-27 16:07:32 +09:00
static bool checked [ MAPLE_PORTS ] ;
static bool empty [ MAPLE_PORTS ] ;
static struct maple_device * baseunits [ MAPLE_PORTS ] ;
2008-02-24 14:30:23 +00:00
2007-09-21 15:55:55 +09:00
/**
2008-08-04 10:39:46 +09:00
* maple_driver_register - register a maple driver
* @ drv : maple driver to be registered .
*
* Registers the passed in @ drv , while updating the bus type .
* Devices with matching function IDs will be automatically probed .
2007-09-21 15:55:55 +09:00
*/
2008-08-04 10:39:46 +09:00
int maple_driver_register ( struct maple_driver * drv )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
if ( ! drv )
return - EINVAL ;
2008-08-04 10:39:46 +09:00
drv - > drv . bus = & maple_bus_type ;
return driver_register ( & drv - > drv ) ;
2007-09-21 15:55:55 +09:00
}
EXPORT_SYMBOL_GPL ( maple_driver_register ) ;
2008-08-04 10:39:46 +09:00
/**
* maple_driver_unregister - unregister a maple driver .
* @ drv : maple driver to unregister .
*
* Cleans up after maple_driver_register ( ) . To be invoked in the exit
* path of any module drivers .
*/
void maple_driver_unregister ( struct maple_driver * drv )
{
driver_unregister ( & drv - > drv ) ;
}
2008-08-04 10:58:24 +09:00
EXPORT_SYMBOL_GPL ( maple_driver_unregister ) ;
2008-08-04 10:39:46 +09:00
2007-09-21 15:55:55 +09:00
/* set hardware registers to enable next round of dma */
2009-02-27 16:07:32 +09:00
static void maple_dma_reset ( void )
2007-09-21 15:55:55 +09:00
{
2010-10-27 14:34:41 +09:00
__raw_writel ( MAPLE_MAGIC , MAPLE_RESET ) ;
2008-02-06 22:46:21 +00:00
/* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */
2010-10-27 14:34:41 +09:00
__raw_writel ( 1 , MAPLE_TRIGTYPE ) ;
2009-02-27 16:07:32 +09:00
/*
* Maple system register
* bits 31 - 16 timeout in units of 20 nsec
* bit 12 hard trigger - set 0 to keep responding to VBLANK
* bits 9 - 8 set 00 for 2 Mbps , 01 for 1 Mbps
* bits 3 - 0 delay ( in 1.3 ms ) between VBLANK and start of DMA
* max delay is 11
*/
2010-10-27 14:34:41 +09:00
__raw_writel ( MAPLE_2MBPS | MAPLE_TIMEOUT ( 0xFFFF ) , MAPLE_SPEED ) ;
__raw_writel ( virt_to_phys ( maple_sendbuf ) , MAPLE_DMAADDR ) ;
__raw_writel ( 1 , MAPLE_ENABLE ) ;
2007-09-21 15:55:55 +09:00
}
/**
* maple_getcond_callback - setup handling MAPLE_COMMAND_GETCOND
* @ dev : device responding
* @ callback : handler callback
* @ interval : interval in jiffies between callbacks
* @ function : the function code for the device
*/
void maple_getcond_callback ( struct maple_device * dev ,
2008-02-06 23:51:21 +00:00
void ( * callback ) ( struct mapleq * mq ) ,
unsigned long interval , unsigned long function )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
dev - > callback = callback ;
dev - > interval = interval ;
dev - > function = cpu_to_be32 ( function ) ;
dev - > when = jiffies ;
2007-09-21 15:55:55 +09:00
}
EXPORT_SYMBOL_GPL ( maple_getcond_callback ) ;
static int maple_dma_done ( void )
{
2010-10-27 14:34:41 +09:00
return ( __raw_readl ( MAPLE_STATE ) & 1 ) = = 0 ;
2007-09-21 15:55:55 +09:00
}
static void maple_release_device ( struct device * dev )
{
2008-02-06 23:51:21 +00:00
struct maple_device * mdev ;
struct mapleq * mq ;
2009-02-27 16:07:32 +09:00
2008-02-06 23:51:21 +00:00
mdev = to_maple_dev ( dev ) ;
mq = mdev - > mq ;
2009-02-27 16:07:32 +09:00
kmem_cache_free ( maple_queue_cache , mq - > recvbuf ) ;
kfree ( mq ) ;
2008-02-06 23:51:21 +00:00
kfree ( mdev ) ;
2007-09-21 15:55:55 +09:00
}
2008-08-04 12:51:06 +09:00
/**
2009-02-27 16:07:32 +09:00
* maple_add_packet - add a single instruction to the maple bus queue
2008-08-04 12:51:06 +09:00
* @ mdev : maple device
* @ function : function on device being queried
* @ command : maple command to add
* @ length : length of command string ( in 32 bit words )
* @ data : remainder of command string
2007-09-21 15:55:55 +09:00
*/
2008-07-29 22:10:56 +09:00
int maple_add_packet ( struct maple_device * mdev , u32 function , u32 command ,
size_t length , void * data )
2007-09-21 15:55:55 +09:00
{
2009-02-27 16:07:32 +09:00
int ret = 0 ;
2008-07-29 22:10:56 +09:00
void * sendbuf = NULL ;
if ( length ) {
2009-02-27 16:07:32 +09:00
sendbuf = kzalloc ( length * 4 , GFP_KERNEL ) ;
2008-07-29 22:10:56 +09:00
if ( ! sendbuf ) {
ret = - ENOMEM ;
goto out ;
}
( ( __be32 * ) sendbuf ) [ 0 ] = cpu_to_be32 ( function ) ;
}
mdev - > mq - > command = command ;
mdev - > mq - > length = length ;
if ( length > 1 )
memcpy ( sendbuf + 4 , data , ( length - 1 ) * 4 ) ;
mdev - > mq - > sendbuf = sendbuf ;
mutex_lock ( & maple_wlist_lock ) ;
2009-02-27 16:07:32 +09:00
list_add_tail ( & mdev - > mq - > list , & maple_waitq ) ;
2008-07-29 22:10:56 +09:00
mutex_unlock ( & maple_wlist_lock ) ;
out :
return ret ;
}
2009-02-27 16:07:32 +09:00
EXPORT_SYMBOL_GPL ( maple_add_packet ) ;
2008-07-29 22:10:56 +09:00
2008-02-06 23:51:21 +00:00
static struct mapleq * maple_allocq ( struct maple_device * mdev )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
struct mapleq * mq ;
2007-09-21 15:55:55 +09:00
2009-02-27 16:07:32 +09:00
mq = kzalloc ( sizeof ( * mq ) , GFP_KERNEL ) ;
2008-02-06 22:46:21 +00:00
if ( ! mq )
2008-07-29 22:10:56 +09:00
goto failed_nomem ;
2007-09-21 15:55:55 +09:00
2009-02-27 16:07:32 +09:00
INIT_LIST_HEAD ( & mq - > list ) ;
2008-02-06 23:51:21 +00:00
mq - > dev = mdev ;
2009-02-27 16:07:32 +09:00
mq - > recvbuf = kmem_cache_zalloc ( maple_queue_cache , GFP_KERNEL ) ;
2008-07-29 22:10:56 +09:00
if ( ! mq - > recvbuf )
goto failed_p2 ;
2009-02-27 16:07:32 +09:00
mq - > recvbuf - > buf = & ( ( mq - > recvbuf - > bufx ) [ 0 ] ) ;
2007-09-21 15:55:55 +09:00
2008-02-06 22:46:21 +00:00
return mq ;
2008-07-29 22:10:56 +09:00
failed_p2 :
kfree ( mq ) ;
failed_nomem :
2009-02-27 16:07:32 +09:00
dev_err ( & mdev - > dev , " could not allocate memory for device (%d, %d) \n " ,
mdev - > port , mdev - > unit ) ;
2008-07-29 22:10:56 +09:00
return NULL ;
2007-09-21 15:55:55 +09:00
}
static struct maple_device * maple_alloc_dev ( int port , int unit )
{
2008-02-06 23:51:21 +00:00
struct maple_device * mdev ;
2007-09-21 15:55:55 +09:00
2009-02-27 16:07:32 +09:00
/* zero this out to avoid kobj subsystem
* thinking it has already been registered */
2008-02-06 23:51:21 +00:00
mdev = kzalloc ( sizeof ( * mdev ) , GFP_KERNEL ) ;
if ( ! mdev )
2008-02-06 22:46:21 +00:00
return NULL ;
2007-09-21 15:55:55 +09:00
2008-02-06 23:51:21 +00:00
mdev - > port = port ;
mdev - > unit = unit ;
2009-02-27 16:07:32 +09:00
2008-02-06 23:51:21 +00:00
mdev - > mq = maple_allocq ( mdev ) ;
2007-09-21 15:55:55 +09:00
2008-02-06 23:51:21 +00:00
if ( ! mdev - > mq ) {
kfree ( mdev ) ;
2008-02-06 22:46:21 +00:00
return NULL ;
}
2008-02-06 23:51:21 +00:00
mdev - > dev . bus = & maple_bus_type ;
mdev - > dev . parent = & maple_bus ;
2009-02-27 16:07:32 +09:00
init_waitqueue_head ( & mdev - > maple_wait ) ;
2008-02-06 23:51:21 +00:00
return mdev ;
2007-09-21 15:55:55 +09:00
}
static void maple_free_dev ( struct maple_device * mdev )
{
2009-02-27 16:07:32 +09:00
kmem_cache_free ( maple_queue_cache , mdev - > mq - > recvbuf ) ;
kfree ( mdev - > mq ) ;
2008-02-06 22:46:21 +00:00
kfree ( mdev ) ;
2007-09-21 15:55:55 +09:00
}
/* process the command queue into a maple command block
* terminating command has bit 32 of first long set to 0
*/
static void maple_build_block ( struct mapleq * mq )
{
2008-02-06 22:46:21 +00:00
int port , unit , from , to , len ;
unsigned long * lsendbuf = mq - > sendbuf ;
2007-09-21 15:55:55 +09:00
2008-02-06 22:46:21 +00:00
port = mq - > dev - > port & 3 ;
unit = mq - > dev - > unit ;
len = mq - > length ;
from = port < < 6 ;
to = ( port < < 6 ) | ( unit > 0 ? ( 1 < < ( unit - 1 ) ) & 0x1f : 0x20 ) ;
2007-09-21 15:55:55 +09:00
2008-02-06 22:46:21 +00:00
* maple_lastptr & = 0x7fffffff ;
maple_lastptr = maple_sendptr ;
2007-09-21 15:55:55 +09:00
2008-02-06 22:46:21 +00:00
* maple_sendptr + + = ( port < < 16 ) | len | 0x80000000 ;
2009-10-13 12:35:30 +09:00
* maple_sendptr + + = virt_to_phys ( mq - > recvbuf - > buf ) ;
2008-02-06 22:46:21 +00:00
* maple_sendptr + + =
mq - > command | ( to < < 8 ) | ( from < < 16 ) | ( len < < 24 ) ;
while ( len - - > 0 )
* maple_sendptr + + = * lsendbuf + + ;
2007-09-21 15:55:55 +09:00
}
/* build up command queue */
static void maple_send ( void )
{
2008-07-29 22:10:56 +09:00
int i , maple_packets = 0 ;
2008-02-06 22:46:21 +00:00
struct mapleq * mq , * nmq ;
2009-02-27 16:07:32 +09:00
if ( ! maple_dma_done ( ) )
2008-02-06 22:46:21 +00:00
return ;
2009-02-27 16:07:32 +09:00
/* disable DMA */
2010-10-27 14:34:41 +09:00
__raw_writel ( 0 , MAPLE_ENABLE ) ;
2009-02-27 16:07:32 +09:00
if ( ! list_empty ( & maple_sentq ) )
goto finish ;
2008-07-29 22:10:56 +09:00
mutex_lock ( & maple_wlist_lock ) ;
2009-02-27 16:07:32 +09:00
if ( list_empty ( & maple_waitq ) ) {
2008-07-29 22:10:56 +09:00
mutex_unlock ( & maple_wlist_lock ) ;
2009-02-27 16:07:32 +09:00
goto finish ;
2008-07-29 22:10:56 +09:00
}
2009-02-27 16:07:32 +09:00
2008-07-29 22:10:56 +09:00
maple_lastptr = maple_sendbuf ;
maple_sendptr = maple_sendbuf ;
2009-02-27 16:07:32 +09:00
2008-02-06 22:46:21 +00:00
list_for_each_entry_safe ( mq , nmq , & maple_waitq , list ) {
maple_build_block ( mq ) ;
2009-02-27 16:07:32 +09:00
list_del_init ( & mq - > list ) ;
list_add_tail ( & mq - > list , & maple_sentq ) ;
2008-02-06 22:46:21 +00:00
if ( maple_packets + + > MAPLE_MAXPACKETS )
break ;
}
2008-07-29 22:10:56 +09:00
mutex_unlock ( & maple_wlist_lock ) ;
2008-02-06 22:46:21 +00:00
if ( maple_packets > 0 ) {
for ( i = 0 ; i < ( 1 < < MAPLE_DMA_PAGES ) ; i + + )
dma_cache_sync ( 0 , maple_sendbuf + i * PAGE_SIZE ,
PAGE_SIZE , DMA_BIDIRECTIONAL ) ;
}
2009-02-27 16:07:32 +09:00
finish :
maple_dma_reset ( ) ;
2007-09-21 15:55:55 +09:00
}
2008-07-29 22:10:56 +09:00
/* check if there is a driver registered likely to match this device */
2009-02-27 16:07:32 +09:00
static int maple_check_matching_driver ( struct device_driver * driver ,
2008-02-06 22:46:21 +00:00
void * devptr )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
struct maple_driver * maple_drv ;
struct maple_device * mdev ;
mdev = devptr ;
maple_drv = to_maple_driver ( driver ) ;
2008-07-29 22:10:56 +09:00
if ( mdev - > devinfo . function & cpu_to_be32 ( maple_drv - > function ) )
return 1 ;
2008-02-06 22:46:21 +00:00
return 0 ;
2007-09-21 15:55:55 +09:00
}
static void maple_detach_driver ( struct maple_device * mdev )
{
2008-02-06 23:51:21 +00:00
device_unregister ( & mdev - > dev ) ;
2007-09-21 15:55:55 +09:00
}
/* process initial MAPLE_COMMAND_DEVINFO for each device or port */
2008-02-06 23:51:21 +00:00
static void maple_attach_driver ( struct maple_device * mdev )
2007-09-21 15:55:55 +09:00
{
2008-02-06 23:51:21 +00:00
char * p , * recvbuf ;
2008-02-06 22:46:21 +00:00
unsigned long function ;
2009-02-27 16:07:32 +09:00
int matched , error ;
2008-02-06 22:46:21 +00:00
2009-02-27 16:07:32 +09:00
recvbuf = mdev - > mq - > recvbuf - > buf ;
2008-02-06 23:51:21 +00:00
/* copy the data as individual elements in
* case of memory optimisation */
memcpy ( & mdev - > devinfo . function , recvbuf + 4 , 4 ) ;
memcpy ( & mdev - > devinfo . function_data [ 0 ] , recvbuf + 8 , 12 ) ;
memcpy ( & mdev - > devinfo . area_code , recvbuf + 20 , 1 ) ;
memcpy ( & mdev - > devinfo . connector_direction , recvbuf + 21 , 1 ) ;
memcpy ( & mdev - > devinfo . product_name [ 0 ] , recvbuf + 22 , 30 ) ;
memcpy ( & mdev - > devinfo . standby_power , recvbuf + 112 , 2 ) ;
memcpy ( & mdev - > devinfo . max_power , recvbuf + 114 , 2 ) ;
memcpy ( mdev - > product_name , mdev - > devinfo . product_name , 30 ) ;
mdev - > product_name [ 30 ] = ' \0 ' ;
memcpy ( mdev - > product_licence , mdev - > devinfo . product_licence , 60 ) ;
mdev - > product_licence [ 60 ] = ' \0 ' ;
for ( p = mdev - > product_name + 29 ; mdev - > product_name < = p ; p - - )
2008-02-06 22:46:21 +00:00
if ( * p = = ' ' )
* p = ' \0 ' ;
else
break ;
2008-02-06 23:51:21 +00:00
for ( p = mdev - > product_licence + 59 ; mdev - > product_licence < = p ; p - - )
2008-02-06 22:46:21 +00:00
if ( * p = = ' ' )
* p = ' \0 ' ;
else
break ;
2008-02-06 23:51:21 +00:00
function = be32_to_cpu ( mdev - > devinfo . function ) ;
2008-02-06 22:46:21 +00:00
2009-02-27 16:07:32 +09:00
dev_info ( & mdev - > dev , " detected %s: function 0x%lX: at (%d, %d) \n " ,
mdev - > product_name , function , mdev - > port , mdev - > unit ) ;
2008-02-06 22:46:21 +00:00
if ( function > 0x200 ) {
/* Do this silently - as not a real device */
function = 0 ;
2009-02-27 16:07:32 +09:00
mdev - > driver = & maple_unsupported_device ;
2009-03-03 12:16:12 +09:00
dev_set_name ( & mdev - > dev , " %d:0.port " , mdev - > port ) ;
2008-02-06 22:46:21 +00:00
} else {
matched =
2008-07-29 22:10:56 +09:00
bus_for_each_drv ( & maple_bus_type , NULL , mdev ,
2009-02-27 16:07:32 +09:00
maple_check_matching_driver ) ;
2008-02-06 22:46:21 +00:00
if ( matched = = 0 ) {
/* Driver does not exist yet */
2009-02-27 16:07:32 +09:00
dev_info ( & mdev - > dev , " no driver found \n " ) ;
mdev - > driver = & maple_unsupported_device ;
2008-02-06 22:46:21 +00:00
}
2009-03-03 12:16:12 +09:00
dev_set_name ( & mdev - > dev , " %d:0%d.%lX " , mdev - > port ,
mdev - > unit , function ) ;
2008-02-06 22:46:21 +00:00
}
2009-02-27 16:07:32 +09:00
2008-02-06 23:51:21 +00:00
mdev - > function = function ;
mdev - > dev . release = & maple_release_device ;
2009-02-27 16:07:32 +09:00
atomic_set ( & mdev - > busy , 0 ) ;
error = device_register ( & mdev - > dev ) ;
if ( error ) {
dev_warn ( & mdev - > dev , " could not register device at "
" (%d, %d), with error 0x%X \n " , mdev - > unit ,
mdev - > port , error ) ;
2008-02-06 23:51:21 +00:00
maple_free_dev ( mdev ) ;
mdev = NULL ;
return ;
2008-02-06 22:46:21 +00:00
}
2007-09-21 15:55:55 +09:00
}
/*
* if device has been registered for the given
* port and unit then return 1 - allows identification
* of which devices need to be attached or detached
*/
2009-02-27 16:07:32 +09:00
static int check_maple_device ( struct device * device , void * portptr )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
struct maple_device_specify * ds ;
struct maple_device * mdev ;
ds = portptr ;
mdev = to_maple_dev ( device ) ;
if ( mdev - > port = = ds - > port & & mdev - > unit = = ds - > unit )
return 1 ;
return 0 ;
2007-09-21 15:55:55 +09:00
}
static int setup_maple_commands ( struct device * device , void * ignored )
{
2008-07-29 22:10:56 +09:00
int add ;
2009-02-27 16:07:32 +09:00
struct maple_device * mdev = to_maple_dev ( device ) ;
if ( mdev - > interval > 0 & & atomic_read ( & mdev - > busy ) = = 0 & &
time_after ( jiffies , mdev - > when ) ) {
/* bounce if we cannot add */
add = maple_add_packet ( mdev ,
be32_to_cpu ( mdev - > devinfo . function ) ,
2008-07-29 22:10:56 +09:00
MAPLE_COMMAND_GETCOND , 1 , NULL ) ;
if ( ! add )
2009-02-27 16:07:32 +09:00
mdev - > when = jiffies + mdev - > interval ;
2008-02-06 22:46:21 +00:00
} else {
2008-07-29 22:10:56 +09:00
if ( time_after ( jiffies , maple_pnp_time ) )
2009-02-27 16:07:32 +09:00
/* Ensure we don't have block reads and devinfo
* calls interfering with one another - so flag the
* device as busy */
if ( atomic_read ( & mdev - > busy ) = = 0 ) {
atomic_set ( & mdev - > busy , 1 ) ;
maple_add_packet ( mdev , 0 ,
MAPLE_COMMAND_DEVINFO , 0 , NULL ) ;
}
2008-02-06 22:46:21 +00:00
}
return 0 ;
2007-09-21 15:55:55 +09:00
}
/* VBLANK bottom half - implemented via workqueue */
static void maple_vblank_handler ( struct work_struct * work )
{
2009-02-27 16:07:32 +09:00
int x , locking ;
struct maple_device * mdev ;
if ( ! maple_dma_done ( ) )
2008-02-06 22:46:21 +00:00
return ;
2008-07-29 22:10:56 +09:00
2010-10-27 14:34:41 +09:00
__raw_writel ( 0 , MAPLE_ENABLE ) ;
2008-07-29 22:10:56 +09:00
2009-02-27 16:07:32 +09:00
if ( ! list_empty ( & maple_sentq ) )
goto finish ;
/*
* Set up essential commands - to fetch data and
* check devices are still present
*/
2008-02-06 22:46:21 +00:00
bus_for_each_dev ( & maple_bus_type , NULL , NULL ,
2009-02-27 16:07:32 +09:00
setup_maple_commands ) ;
if ( time_after ( jiffies , maple_pnp_time ) ) {
/*
* Scan the empty ports - bus is flakey and may have
* mis - reported emptyness
*/
for ( x = 0 ; x < MAPLE_PORTS ; x + + ) {
if ( checked [ x ] & & empty [ x ] ) {
mdev = baseunits [ x ] ;
if ( ! mdev )
break ;
atomic_set ( & mdev - > busy , 1 ) ;
locking = maple_add_packet ( mdev , 0 ,
MAPLE_COMMAND_DEVINFO , 0 , NULL ) ;
if ( ! locking )
break ;
}
}
2008-07-29 22:10:56 +09:00
2008-02-06 22:46:21 +00:00
maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL ;
}
2008-07-29 22:10:56 +09:00
2009-02-27 16:07:32 +09:00
finish :
maple_send ( ) ;
2007-09-21 15:55:55 +09:00
}
2009-02-27 16:07:32 +09:00
/* handle devices added via hotplugs - placing them on queue for DEVINFO */
2007-09-21 15:55:55 +09:00
static void maple_map_subunits ( struct maple_device * mdev , int submask )
{
2008-02-06 22:46:21 +00:00
int retval , k , devcheck ;
struct maple_device * mdev_add ;
struct maple_device_specify ds ;
2008-07-29 22:10:56 +09:00
ds . port = mdev - > port ;
2008-02-06 22:46:21 +00:00
for ( k = 0 ; k < 5 ; k + + ) {
ds . unit = k + 1 ;
retval =
bus_for_each_dev ( & maple_bus_type , NULL , & ds ,
2009-02-27 16:07:32 +09:00
check_maple_device ) ;
2008-02-06 22:46:21 +00:00
if ( retval ) {
submask = submask > > 1 ;
continue ;
}
devcheck = submask & 0x01 ;
if ( devcheck ) {
mdev_add = maple_alloc_dev ( mdev - > port , k + 1 ) ;
if ( ! mdev_add )
return ;
2009-02-27 16:07:32 +09:00
atomic_set ( & mdev_add - > busy , 1 ) ;
2008-07-29 22:10:56 +09:00
maple_add_packet ( mdev_add , 0 , MAPLE_COMMAND_DEVINFO ,
0 , NULL ) ;
/* mark that we are checking sub devices */
2008-02-06 22:46:21 +00:00
scanning = 1 ;
}
submask = submask > > 1 ;
}
2007-09-21 15:55:55 +09:00
}
/* mark a device as removed */
static void maple_clean_submap ( struct maple_device * mdev )
{
2008-02-06 22:46:21 +00:00
int killbit ;
2007-09-21 15:55:55 +09:00
2008-02-06 22:46:21 +00:00
killbit = ( mdev - > unit > 0 ? ( 1 < < ( mdev - > unit - 1 ) ) & 0x1f : 0x20 ) ;
killbit = ~ killbit ;
killbit & = 0xFF ;
subdevice_map [ mdev - > port ] = subdevice_map [ mdev - > port ] & killbit ;
2007-09-21 15:55:55 +09:00
}
/* handle empty port or hotplug removal */
2009-02-27 16:07:32 +09:00
static void maple_response_none ( struct maple_device * mdev )
{
maple_clean_submap ( mdev ) ;
if ( likely ( mdev - > unit ! = 0 ) ) {
/*
* Block devices play up
* and give the impression they have
* been removed even when still in place or
* trip the mtd layer when they have
* really gone - this code traps that eventuality
* and ensures we aren ' t overloaded with useless
* error messages
*/
if ( mdev - > can_unload ) {
if ( ! mdev - > can_unload ( mdev ) ) {
atomic_set ( & mdev - > busy , 2 ) ;
wake_up ( & mdev - > maple_wait ) ;
return ;
}
}
dev_info ( & mdev - > dev , " detaching device at (%d, %d) \n " ,
mdev - > port , mdev - > unit ) ;
2008-02-06 22:46:21 +00:00
maple_detach_driver ( mdev ) ;
return ;
2009-02-27 16:07:32 +09:00
} else {
if ( ! started | | ! fullscan ) {
if ( checked [ mdev - > port ] = = false ) {
checked [ mdev - > port ] = true ;
empty [ mdev - > port ] = true ;
dev_info ( & mdev - > dev , " no devices "
" to port %d \n " , mdev - > port ) ;
}
return ;
2008-02-24 14:30:23 +00:00
}
2008-02-06 22:46:21 +00:00
}
2009-02-27 16:07:32 +09:00
/* Some hardware devices generate false detach messages on unit 0 */
atomic_set ( & mdev - > busy , 0 ) ;
2007-09-21 15:55:55 +09:00
}
/* preprocess hotplugs or scans */
static void maple_response_devinfo ( struct maple_device * mdev ,
2008-02-06 22:46:21 +00:00
char * recvbuf )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
char submask ;
2008-02-24 14:30:23 +00:00
if ( ! started | | ( scanning = = 2 ) | | ! fullscan ) {
if ( ( mdev - > unit = = 0 ) & & ( checked [ mdev - > port ] = = false ) ) {
checked [ mdev - > port ] = true ;
maple_attach_driver ( mdev ) ;
} else {
if ( mdev - > unit ! = 0 )
maple_attach_driver ( mdev ) ;
2009-02-27 16:07:32 +09:00
if ( mdev - > unit = = 0 ) {
empty [ mdev - > port ] = false ;
maple_attach_driver ( mdev ) ;
}
2008-02-24 14:30:23 +00:00
}
2008-02-06 22:46:21 +00:00
}
if ( mdev - > unit = = 0 ) {
submask = recvbuf [ 2 ] & 0x1F ;
if ( submask ^ subdevice_map [ mdev - > port ] ) {
maple_map_subunits ( mdev , submask ) ;
subdevice_map [ mdev - > port ] = submask ;
}
}
2007-09-21 15:55:55 +09:00
}
2009-02-27 16:07:32 +09:00
static void maple_response_fileerr ( struct maple_device * mdev , void * recvbuf )
{
if ( mdev - > fileerr_handler ) {
mdev - > fileerr_handler ( mdev , recvbuf ) ;
return ;
} else
dev_warn ( & mdev - > dev , " device at (%d, %d) reports "
" file error 0x%X \n " , mdev - > port , mdev - > unit ,
( ( int * ) recvbuf ) [ 1 ] ) ;
}
2008-07-29 22:10:56 +09:00
static void maple_port_rescan ( void )
{
int i ;
struct maple_device * mdev ;
fullscan = 1 ;
for ( i = 0 ; i < MAPLE_PORTS ; i + + ) {
if ( checked [ i ] = = false ) {
fullscan = 0 ;
mdev = baseunits [ i ] ;
maple_add_packet ( mdev , 0 , MAPLE_COMMAND_DEVINFO ,
0 , NULL ) ;
}
}
}
2007-09-21 15:55:55 +09:00
/* maple dma end bottom half - implemented via workqueue */
static void maple_dma_handler ( struct work_struct * work )
{
2008-02-06 22:46:21 +00:00
struct mapleq * mq , * nmq ;
2009-02-27 16:07:32 +09:00
struct maple_device * mdev ;
2008-02-06 22:46:21 +00:00
char * recvbuf ;
enum maple_code code ;
if ( ! maple_dma_done ( ) )
return ;
2010-10-27 14:34:41 +09:00
__raw_writel ( 0 , MAPLE_ENABLE ) ;
2008-02-06 22:46:21 +00:00
if ( ! list_empty ( & maple_sentq ) ) {
list_for_each_entry_safe ( mq , nmq , & maple_sentq , list ) {
2009-02-27 16:07:32 +09:00
mdev = mq - > dev ;
recvbuf = mq - > recvbuf - > buf ;
dma_cache_sync ( & mdev - > dev , recvbuf , 0x400 ,
DMA_FROM_DEVICE ) ;
2008-02-06 22:46:21 +00:00
code = recvbuf [ 0 ] ;
2008-07-29 22:10:56 +09:00
kfree ( mq - > sendbuf ) ;
list_del_init ( & mq - > list ) ;
2008-02-06 22:46:21 +00:00
switch ( code ) {
case MAPLE_RESPONSE_NONE :
2009-02-27 16:07:32 +09:00
maple_response_none ( mdev ) ;
2008-02-06 22:46:21 +00:00
break ;
case MAPLE_RESPONSE_DEVINFO :
2009-02-27 16:07:32 +09:00
maple_response_devinfo ( mdev , recvbuf ) ;
atomic_set ( & mdev - > busy , 0 ) ;
2008-02-06 22:46:21 +00:00
break ;
case MAPLE_RESPONSE_DATATRF :
2009-02-27 16:07:32 +09:00
if ( mdev - > callback )
mdev - > callback ( mq ) ;
atomic_set ( & mdev - > busy , 0 ) ;
wake_up ( & mdev - > maple_wait ) ;
2008-02-06 22:46:21 +00:00
break ;
case MAPLE_RESPONSE_FILEERR :
2009-02-27 16:07:32 +09:00
maple_response_fileerr ( mdev , recvbuf ) ;
atomic_set ( & mdev - > busy , 0 ) ;
wake_up ( & mdev - > maple_wait ) ;
break ;
2008-02-06 22:46:21 +00:00
case MAPLE_RESPONSE_AGAIN :
case MAPLE_RESPONSE_BADCMD :
case MAPLE_RESPONSE_BADFUNC :
2009-02-27 16:07:32 +09:00
dev_warn ( & mdev - > dev , " non-fatal error "
" 0x%X at (%d, %d) \n " , code ,
mdev - > port , mdev - > unit ) ;
atomic_set ( & mdev - > busy , 0 ) ;
2008-02-06 22:46:21 +00:00
break ;
case MAPLE_RESPONSE_ALLINFO :
2009-02-27 16:07:32 +09:00
dev_notice ( & mdev - > dev , " extended "
" device information request for (%d, %d) "
" but call is not supported \n " , mdev - > port ,
mdev - > unit ) ;
atomic_set ( & mdev - > busy , 0 ) ;
2008-02-06 22:46:21 +00:00
break ;
case MAPLE_RESPONSE_OK :
2009-02-27 16:07:32 +09:00
atomic_set ( & mdev - > busy , 0 ) ;
wake_up ( & mdev - > maple_wait ) ;
2008-02-06 22:46:21 +00:00
break ;
default :
break ;
}
}
2008-07-29 22:10:56 +09:00
/* if scanning is 1 then we have subdevices to check */
2008-02-06 22:46:21 +00:00
if ( scanning = = 1 ) {
maple_send ( ) ;
scanning = 2 ;
} else
scanning = 0 ;
2008-07-29 22:10:56 +09:00
/*check if we have actually tested all ports yet */
if ( ! fullscan )
maple_port_rescan ( ) ;
/* mark that we have been through the first scan */
2009-02-27 16:07:32 +09:00
started = 1 ;
2008-02-06 22:46:21 +00:00
}
2009-02-27 16:07:32 +09:00
maple_send ( ) ;
2007-09-21 15:55:55 +09:00
}
2009-02-27 16:07:32 +09:00
static irqreturn_t maple_dma_interrupt ( int irq , void * dev_id )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
/* Load everything into the bottom half */
schedule_work ( & maple_dma_process ) ;
return IRQ_HANDLED ;
2007-09-21 15:55:55 +09:00
}
2009-02-27 16:07:32 +09:00
static irqreturn_t maple_vblank_interrupt ( int irq , void * dev_id )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
schedule_work ( & maple_vblank_process ) ;
return IRQ_HANDLED ;
2007-09-21 15:55:55 +09:00
}
static int maple_set_dma_interrupt_handler ( void )
{
2009-02-27 16:07:32 +09:00
return request_irq ( HW_EVENT_MAPLE_DMA , maple_dma_interrupt ,
IRQF_SHARED , " maple bus DMA " , & maple_unsupported_device ) ;
2007-09-21 15:55:55 +09:00
}
static int maple_set_vblank_interrupt_handler ( void )
{
2009-02-27 16:07:32 +09:00
return request_irq ( HW_EVENT_VSYNC , maple_vblank_interrupt ,
IRQF_SHARED , " maple bus VBLANK " , & maple_unsupported_device ) ;
2007-09-21 15:55:55 +09:00
}
static int maple_get_dma_buffer ( void )
{
2008-02-06 22:46:21 +00:00
maple_sendbuf =
( void * ) __get_free_pages ( GFP_KERNEL | __GFP_ZERO ,
MAPLE_DMA_PAGES ) ;
if ( ! maple_sendbuf )
return - ENOMEM ;
return 0 ;
2007-09-21 15:55:55 +09:00
}
2009-02-27 16:07:32 +09:00
static int maple_match_bus_driver ( struct device * devptr ,
2008-02-06 22:46:21 +00:00
struct device_driver * drvptr )
2007-09-21 15:55:55 +09:00
{
2008-08-04 10:39:46 +09:00
struct maple_driver * maple_drv = to_maple_driver ( drvptr ) ;
struct maple_device * maple_dev = to_maple_dev ( devptr ) ;
2008-02-06 22:46:21 +00:00
/* Trap empty port case */
if ( maple_dev - > devinfo . function = = 0xFFFFFFFF )
return 0 ;
else if ( maple_dev - > devinfo . function &
2008-07-29 22:10:56 +09:00
cpu_to_be32 ( maple_drv - > function ) )
2008-02-06 22:46:21 +00:00
return 1 ;
return 0 ;
2007-09-21 15:55:55 +09:00
}
2008-02-06 22:46:21 +00:00
static int maple_bus_uevent ( struct device * dev ,
struct kobj_uevent_env * env )
2007-09-21 15:55:55 +09:00
{
2008-02-06 22:46:21 +00:00
return 0 ;
2007-09-21 15:55:55 +09:00
}
static void maple_bus_release ( struct device * dev )
{
}
2009-02-27 16:07:32 +09:00
static struct maple_driver maple_unsupported_device = {
2008-02-06 22:46:21 +00:00
. drv = {
2009-02-27 16:07:32 +09:00
. name = " maple_unsupported_device " ,
2008-02-06 22:46:21 +00:00
. bus = & maple_bus_type ,
2008-02-06 23:51:21 +00:00
} ,
2007-09-21 15:55:55 +09:00
} ;
2009-03-28 21:50:17 -07:00
/*
2009-02-27 16:07:32 +09:00
* maple_bus_type - core maple bus structure
*/
2007-09-21 15:55:55 +09:00
struct bus_type maple_bus_type = {
2008-02-06 22:46:21 +00:00
. name = " maple " ,
2009-02-27 16:07:32 +09:00
. match = maple_match_bus_driver ,
2008-02-06 22:46:21 +00:00
. uevent = maple_bus_uevent ,
2007-09-21 15:55:55 +09:00
} ;
EXPORT_SYMBOL_GPL ( maple_bus_type ) ;
static struct device maple_bus = {
2009-03-03 12:16:12 +09:00
. init_name = " maple " ,
2008-02-06 22:46:21 +00:00
. release = maple_bus_release ,
2007-09-21 15:55:55 +09:00
} ;
static int __init maple_bus_init ( void )
{
2008-02-06 22:46:21 +00:00
int retval , i ;
struct maple_device * mdev [ MAPLE_PORTS ] ;
2009-02-27 16:07:32 +09:00
2010-10-27 14:34:41 +09:00
__raw_writel ( 0 , MAPLE_ENABLE ) ;
2008-02-06 22:46:21 +00:00
retval = device_register ( & maple_bus ) ;
if ( retval )
goto cleanup ;
retval = bus_register ( & maple_bus_type ) ;
if ( retval )
goto cleanup_device ;
2009-02-27 16:07:32 +09:00
retval = driver_register ( & maple_unsupported_device . drv ) ;
2008-02-06 22:46:21 +00:00
if ( retval )
goto cleanup_bus ;
/* allocate memory for maple bus dma */
retval = maple_get_dma_buffer ( ) ;
if ( retval ) {
2009-02-27 16:07:32 +09:00
dev_err ( & maple_bus , " failed to allocate DMA buffers \n " ) ;
2008-02-06 22:46:21 +00:00
goto cleanup_basic ;
}
/* set up DMA interrupt handler */
retval = maple_set_dma_interrupt_handler ( ) ;
if ( retval ) {
2009-02-27 16:07:32 +09:00
dev_err ( & maple_bus , " bus failed to grab maple "
" DMA IRQ \n " ) ;
2008-02-06 22:46:21 +00:00
goto cleanup_dma ;
}
/* set up VBLANK interrupt handler */
retval = maple_set_vblank_interrupt_handler ( ) ;
if ( retval ) {
2009-02-27 16:07:32 +09:00
dev_err ( & maple_bus , " bus failed to grab VBLANK IRQ \n " ) ;
2008-02-06 22:46:21 +00:00
goto cleanup_irq ;
}
2009-02-27 16:07:32 +09:00
maple_queue_cache = KMEM_CACHE ( maple_buffer , SLAB_HWCACHE_ALIGN ) ;
2008-02-06 22:46:21 +00:00
if ( ! maple_queue_cache )
goto cleanup_bothirqs ;
2008-07-29 22:10:56 +09:00
INIT_LIST_HEAD ( & maple_waitq ) ;
INIT_LIST_HEAD ( & maple_sentq ) ;
2008-02-06 22:46:21 +00:00
/* setup maple ports */
for ( i = 0 ; i < MAPLE_PORTS ; i + + ) {
2008-02-24 14:30:23 +00:00
checked [ i ] = false ;
2009-02-27 16:07:32 +09:00
empty [ i ] = false ;
2008-02-06 22:46:21 +00:00
mdev [ i ] = maple_alloc_dev ( i , 0 ) ;
if ( ! mdev [ i ] ) {
while ( i - - > 0 )
maple_free_dev ( mdev [ i ] ) ;
goto cleanup_cache ;
}
2009-02-27 16:07:32 +09:00
baseunits [ i ] = mdev [ i ] ;
atomic_set ( & mdev [ i ] - > busy , 1 ) ;
2008-07-29 22:10:56 +09:00
maple_add_packet ( mdev [ i ] , 0 , MAPLE_COMMAND_DEVINFO , 0 , NULL ) ;
2008-02-06 22:46:21 +00:00
subdevice_map [ i ] = 0 ;
}
2009-02-27 16:07:32 +09:00
maple_pnp_time = jiffies + HZ ;
/* prepare initial queue */
2008-02-06 22:46:21 +00:00
maple_send ( ) ;
2009-02-27 16:07:32 +09:00
dev_info ( & maple_bus , " bus core now registered \n " ) ;
2008-02-06 22:46:21 +00:00
return 0 ;
2008-02-06 23:51:21 +00:00
cleanup_cache :
2008-02-06 22:46:21 +00:00
kmem_cache_destroy ( maple_queue_cache ) ;
2008-02-06 23:51:21 +00:00
cleanup_bothirqs :
2008-02-06 22:46:21 +00:00
free_irq ( HW_EVENT_VSYNC , 0 ) ;
2008-02-06 23:51:21 +00:00
cleanup_irq :
2008-02-06 22:46:21 +00:00
free_irq ( HW_EVENT_MAPLE_DMA , 0 ) ;
2008-02-06 23:51:21 +00:00
cleanup_dma :
2008-02-06 22:46:21 +00:00
free_pages ( ( unsigned long ) maple_sendbuf , MAPLE_DMA_PAGES ) ;
2008-02-06 23:51:21 +00:00
cleanup_basic :
2009-02-27 16:07:32 +09:00
driver_unregister ( & maple_unsupported_device . drv ) ;
2008-02-06 22:46:21 +00:00
2008-02-06 23:51:21 +00:00
cleanup_bus :
2008-02-06 22:46:21 +00:00
bus_unregister ( & maple_bus_type ) ;
2008-02-06 23:51:21 +00:00
cleanup_device :
2008-02-06 22:46:21 +00:00
device_unregister ( & maple_bus ) ;
2008-02-06 23:51:21 +00:00
cleanup :
2009-02-27 16:07:32 +09:00
printk ( KERN_ERR " Maple bus registration failed \n " ) ;
2008-02-06 22:46:21 +00:00
return retval ;
2007-09-21 15:55:55 +09:00
}
2008-02-06 23:51:21 +00:00
/* Push init to later to ensure hardware gets detected */
fs_initcall ( maple_bus_init ) ;