2006-12-09 05:27:47 +03:00
/*
* PS3 virtual uart
*
* Copyright ( C ) 2006 Sony Computer Entertainment Inc .
* Copyright 2006 Sony Corp .
*
* 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 ; version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
2007-02-07 01:23:47 +03:00
# include <linux/workqueue.h>
2007-10-19 10:40:25 +04:00
# include <linux/bitops.h>
2006-12-09 05:27:47 +03:00
# include <asm/ps3.h>
2007-02-14 04:37:28 +03:00
# include <asm/firmware.h>
2006-12-09 05:27:47 +03:00
# include <asm/lv1call.h>
# include "vuart.h"
MODULE_AUTHOR ( " Sony Corporation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2007-02-14 04:37:28 +03:00
MODULE_DESCRIPTION ( " PS3 vuart " ) ;
2006-12-09 05:27:47 +03:00
/**
* vuart - An inter - partition data link service .
* port 0 : PS3 AV Settings .
* port 2 : PS3 System Manager .
*
* The vuart provides a bi - directional byte stream data link between logical
* partitions . Its primary role is as a communications link between the guest
* OS and the system policy module . The current HV does not support any
* connections other than those listed .
*/
enum { PORT_COUNT = 3 , } ;
enum vuart_param {
PARAM_TX_TRIGGER = 0 ,
PARAM_RX_TRIGGER = 1 ,
PARAM_INTERRUPT_MASK = 2 ,
PARAM_RX_BUF_SIZE = 3 , /* read only */
PARAM_RX_BYTES = 4 , /* read only */
PARAM_TX_BUF_SIZE = 5 , /* read only */
PARAM_TX_BYTES = 6 , /* read only */
PARAM_INTERRUPT_STATUS = 7 , /* read only */
} ;
enum vuart_interrupt_bit {
INTERRUPT_BIT_TX = 0 ,
INTERRUPT_BIT_RX = 1 ,
INTERRUPT_BIT_DISCONNECT = 2 ,
} ;
enum vuart_interrupt_mask {
INTERRUPT_MASK_TX = 1 ,
INTERRUPT_MASK_RX = 2 ,
INTERRUPT_MASK_DISCONNECT = 4 ,
} ;
2007-06-16 02:01:06 +04:00
/**
* struct ps3_vuart_port_priv - private vuart device data .
*/
struct ps3_vuart_port_priv {
u64 interrupt_mask ;
struct {
spinlock_t lock ;
struct list_head head ;
} tx_list ;
struct {
struct ps3_vuart_work work ;
unsigned long bytes_held ;
spinlock_t lock ;
struct list_head head ;
} rx_list ;
struct ps3_vuart_stats stats ;
} ;
static struct ps3_vuart_port_priv * to_port_priv (
struct ps3_system_bus_device * dev )
{
BUG_ON ( ! dev ) ;
BUG_ON ( ! dev - > driver_priv ) ;
return ( struct ps3_vuart_port_priv * ) dev - > driver_priv ;
}
2006-12-09 05:27:47 +03:00
/**
* struct ports_bmp - bitmap indicating ports needing service .
*
* A 256 bit read only bitmap indicating ports needing service . Do not write
* to these bits . Must not cross a page boundary .
*/
struct ports_bmp {
u64 status ;
u64 unused [ 3 ] ;
2008-01-18 23:32:19 +03:00
} __attribute__ ( ( aligned ( 32 ) ) ) ;
2006-12-09 05:27:47 +03:00
# define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__)
2007-06-16 01:18:14 +04:00
static void __maybe_unused _dump_ports_bmp (
2008-01-18 23:32:19 +03:00
const struct ports_bmp * bmp , const char * func , int line )
2006-12-09 05:27:47 +03:00
{
2009-01-13 23:10:06 +03:00
pr_debug ( " %s:%d: ports_bmp: %016llxh \n " , func , line , bmp - > status ) ;
2006-12-09 05:27:47 +03:00
}
# define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__)
2007-06-16 01:18:14 +04:00
static void __maybe_unused _dump_port_params ( unsigned int port_number ,
2008-01-18 23:32:19 +03:00
const char * func , int line )
2006-12-09 05:27:47 +03:00
{
# if defined(DEBUG)
static const char * strings [ ] = {
" tx_trigger " ,
" rx_trigger " ,
" interrupt_mask " ,
" rx_buf_size " ,
" rx_bytes " ,
" tx_buf_size " ,
" tx_bytes " ,
" interrupt_status " ,
} ;
int result ;
unsigned int i ;
u64 value ;
for ( i = 0 ; i < ARRAY_SIZE ( strings ) ; i + + ) {
result = lv1_get_virtual_uart_param ( port_number , i , & value ) ;
if ( result ) {
pr_debug ( " %s:%d: port_%u: %s failed: %s \n " , func , line ,
port_number , strings [ i ] , ps3_result ( result ) ) ;
continue ;
}
pr_debug ( " %s:%d: port_%u: %s = %lxh \n " ,
func , line , port_number , strings [ i ] , value ) ;
}
# endif
}
struct vuart_triggers {
unsigned long rx ;
unsigned long tx ;
} ;
2007-06-16 02:01:06 +04:00
int ps3_vuart_get_triggers ( struct ps3_system_bus_device * dev ,
2006-12-09 05:27:47 +03:00
struct vuart_triggers * trig )
{
int result ;
2009-01-13 22:59:41 +03:00
u64 size ;
u64 val ;
u64 tx ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
result = lv1_get_virtual_uart_param ( dev - > port_number ,
2009-01-13 22:59:41 +03:00
PARAM_TX_TRIGGER , & tx ) ;
trig - > tx = tx ;
2006-12-09 05:27:47 +03:00
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: tx_trigger failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
result = lv1_get_virtual_uart_param ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
PARAM_RX_BUF_SIZE , & size ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: tx_buf_size failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
result = lv1_get_virtual_uart_param ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
PARAM_RX_TRIGGER , & val ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: rx_trigger failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
trig - > rx = size - val ;
dev_dbg ( & dev - > core , " %s:%d: tx %lxh, rx %lxh \n " , __func__ , __LINE__ ,
trig - > tx , trig - > rx ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
int ps3_vuart_set_triggers ( struct ps3_system_bus_device * dev , unsigned int tx ,
2006-12-09 05:27:47 +03:00
unsigned int rx )
{
int result ;
2009-01-13 22:59:41 +03:00
u64 size ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
result = lv1_set_virtual_uart_param ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
PARAM_TX_TRIGGER , tx ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: tx_trigger failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
result = lv1_get_virtual_uart_param ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
PARAM_RX_BUF_SIZE , & size ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: tx_buf_size failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
result = lv1_set_virtual_uart_param ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
PARAM_RX_TRIGGER , size - rx ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: rx_trigger failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
dev_dbg ( & dev - > core , " %s:%d: tx %xh, rx %xh \n " , __func__ , __LINE__ ,
tx , rx ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
static int ps3_vuart_get_rx_bytes_waiting ( struct ps3_system_bus_device * dev ,
2007-02-14 04:37:28 +03:00
u64 * bytes_waiting )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
int result ;
result = lv1_get_virtual_uart_param ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
PARAM_RX_BYTES , bytes_waiting ) ;
if ( result )
dev_dbg ( & dev - > core , " %s:%d: rx_bytes failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
2009-01-13 23:10:06 +03:00
dev_dbg ( & dev - > core , " %s:%d: %llxh \n " , __func__ , __LINE__ ,
2006-12-09 05:27:47 +03:00
* bytes_waiting ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
/**
* ps3_vuart_set_interrupt_mask - Enable / disable the port interrupt sources .
* @ dev : The struct ps3_system_bus_device instance .
* @ bmp : Logical OR of enum vuart_interrupt_mask values . A zero bit disables .
*/
static int ps3_vuart_set_interrupt_mask ( struct ps3_system_bus_device * dev ,
2006-12-09 05:27:47 +03:00
unsigned long mask )
{
int result ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: %lxh \n " , __func__ , __LINE__ , mask ) ;
2007-06-16 02:01:06 +04:00
priv - > interrupt_mask = mask ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
result = lv1_set_virtual_uart_param ( dev - > port_number ,
PARAM_INTERRUPT_MASK , priv - > interrupt_mask ) ;
2006-12-09 05:27:47 +03:00
if ( result )
dev_dbg ( & dev - > core , " %s:%d: interrupt_mask failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
static int ps3_vuart_get_interrupt_status ( struct ps3_system_bus_device * dev ,
2006-12-09 05:27:47 +03:00
unsigned long * status )
{
2007-06-16 02:01:06 +04:00
int result ;
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2007-02-14 04:37:28 +03:00
u64 tmp ;
2007-06-16 02:01:06 +04:00
result = lv1_get_virtual_uart_param ( dev - > port_number ,
2007-02-14 04:37:28 +03:00
PARAM_INTERRUPT_STATUS , & tmp ) ;
2006-12-09 05:27:47 +03:00
if ( result )
dev_dbg ( & dev - > core , " %s:%d: interrupt_status failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
2007-06-16 02:01:06 +04:00
* status = tmp & priv - > interrupt_mask ;
2007-02-14 04:37:28 +03:00
2009-01-13 23:10:06 +03:00
dev_dbg ( & dev - > core , " %s:%d: m %llxh, s %llxh, m&s %lxh \n " ,
2007-06-16 02:01:06 +04:00
__func__ , __LINE__ , priv - > interrupt_mask , tmp , * status ) ;
2006-12-09 05:27:47 +03:00
return result ;
}
2007-06-16 02:01:06 +04:00
int ps3_vuart_enable_interrupt_tx ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
return ( priv - > interrupt_mask & INTERRUPT_MASK_TX ) ? 0
: ps3_vuart_set_interrupt_mask ( dev , priv - > interrupt_mask
2006-12-09 05:27:47 +03:00
| INTERRUPT_MASK_TX ) ;
}
2007-06-16 02:01:06 +04:00
int ps3_vuart_enable_interrupt_rx ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
return ( priv - > interrupt_mask & INTERRUPT_MASK_RX ) ? 0
: ps3_vuart_set_interrupt_mask ( dev , priv - > interrupt_mask
2006-12-09 05:27:47 +03:00
| INTERRUPT_MASK_RX ) ;
}
2007-06-16 02:01:06 +04:00
int ps3_vuart_enable_interrupt_disconnect ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
return ( priv - > interrupt_mask & INTERRUPT_MASK_DISCONNECT ) ? 0
: ps3_vuart_set_interrupt_mask ( dev , priv - > interrupt_mask
2006-12-09 05:27:47 +03:00
| INTERRUPT_MASK_DISCONNECT ) ;
}
2007-06-16 02:01:06 +04:00
int ps3_vuart_disable_interrupt_tx ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
return ( priv - > interrupt_mask & INTERRUPT_MASK_TX )
? ps3_vuart_set_interrupt_mask ( dev , priv - > interrupt_mask
2006-12-09 05:27:47 +03:00
& ~ INTERRUPT_MASK_TX ) : 0 ;
}
2007-06-16 02:01:06 +04:00
int ps3_vuart_disable_interrupt_rx ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
return ( priv - > interrupt_mask & INTERRUPT_MASK_RX )
? ps3_vuart_set_interrupt_mask ( dev , priv - > interrupt_mask
2006-12-09 05:27:47 +03:00
& ~ INTERRUPT_MASK_RX ) : 0 ;
}
2007-06-16 02:01:06 +04:00
int ps3_vuart_disable_interrupt_disconnect ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
return ( priv - > interrupt_mask & INTERRUPT_MASK_DISCONNECT )
? ps3_vuart_set_interrupt_mask ( dev , priv - > interrupt_mask
2006-12-09 05:27:47 +03:00
& ~ INTERRUPT_MASK_DISCONNECT ) : 0 ;
}
/**
* ps3_vuart_raw_write - Low level write helper .
2007-06-16 02:01:06 +04:00
* @ dev : The struct ps3_system_bus_device instance .
2006-12-09 05:27:47 +03:00
*
* Do not call ps3_vuart_raw_write directly , use ps3_vuart_write .
*/
2007-06-16 02:01:06 +04:00
static int ps3_vuart_raw_write ( struct ps3_system_bus_device * dev ,
2009-01-13 22:59:41 +03:00
const void * buf , unsigned int bytes , u64 * bytes_written )
2006-12-09 05:27:47 +03:00
{
int result ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
result = lv1_write_virtual_uart ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
ps3_mm_phys_to_lpar ( __pa ( buf ) ) , bytes , bytes_written ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: lv1_write_virtual_uart failed: "
" %s \n " , __func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
priv - > stats . bytes_written + = * bytes_written ;
2006-12-09 05:27:47 +03:00
2009-01-13 22:59:41 +03:00
dev_dbg ( & dev - > core , " %s:%d: wrote %llxh/%xh=>%lxh \n " , __func__ , __LINE__ ,
2007-06-16 02:01:06 +04:00
* bytes_written , bytes , priv - > stats . bytes_written ) ;
2006-12-09 05:27:47 +03:00
return result ;
}
/**
* ps3_vuart_raw_read - Low level read helper .
2007-06-16 02:01:06 +04:00
* @ dev : The struct ps3_system_bus_device instance .
2006-12-09 05:27:47 +03:00
*
* Do not call ps3_vuart_raw_read directly , use ps3_vuart_read .
*/
2007-06-16 02:01:06 +04:00
static int ps3_vuart_raw_read ( struct ps3_system_bus_device * dev , void * buf ,
2009-01-13 22:59:41 +03:00
unsigned int bytes , u64 * bytes_read )
2006-12-09 05:27:47 +03:00
{
int result ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: %xh \n " , __func__ , __LINE__ , bytes ) ;
2007-06-16 02:01:06 +04:00
result = lv1_read_virtual_uart ( dev - > port_number ,
2006-12-09 05:27:47 +03:00
ps3_mm_phys_to_lpar ( __pa ( buf ) ) , bytes , bytes_read ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: lv1_read_virtual_uart failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-06-16 02:01:06 +04:00
priv - > stats . bytes_read + = * bytes_read ;
2006-12-09 05:27:47 +03:00
2009-01-13 22:59:41 +03:00
dev_dbg ( & dev - > core , " %s:%d: read %llxh/%xh=>%lxh \n " , __func__ , __LINE__ ,
2007-06-16 02:01:06 +04:00
* bytes_read , bytes , priv - > stats . bytes_read ) ;
2006-12-09 05:27:47 +03:00
return result ;
}
2007-02-14 04:37:28 +03:00
/**
* ps3_vuart_clear_rx_bytes - Discard bytes received .
2007-06-16 02:01:06 +04:00
* @ dev : The struct ps3_system_bus_device instance .
2007-02-14 04:37:28 +03:00
* @ bytes : Max byte count to discard , zero = all pending .
*
* Used to clear pending rx interrupt source . Will not block .
*/
2007-06-16 02:01:06 +04:00
void ps3_vuart_clear_rx_bytes ( struct ps3_system_bus_device * dev ,
2007-02-14 04:37:28 +03:00
unsigned int bytes )
{
int result ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2007-02-14 04:37:28 +03:00
u64 bytes_waiting ;
2008-01-18 23:32:19 +03:00
void * tmp ;
2007-02-14 04:37:28 +03:00
result = ps3_vuart_get_rx_bytes_waiting ( dev , & bytes_waiting ) ;
BUG_ON ( result ) ;
bytes = bytes ? min ( bytes , ( unsigned int ) bytes_waiting ) : bytes_waiting ;
dev_dbg ( & dev - > core , " %s:%d: %u \n " , __func__ , __LINE__ , bytes ) ;
if ( ! bytes )
return ;
/* Add some extra space for recently arrived data. */
bytes + = 128 ;
tmp = kmalloc ( bytes , GFP_KERNEL ) ;
if ( ! tmp )
return ;
ps3_vuart_raw_read ( dev , tmp , bytes , & bytes_waiting ) ;
kfree ( tmp ) ;
/* Don't include these bytes in the stats. */
2007-06-16 02:01:06 +04:00
priv - > stats . bytes_read - = bytes_waiting ;
2007-02-14 04:37:28 +03:00
}
2007-06-16 02:01:06 +04:00
EXPORT_SYMBOL_GPL ( ps3_vuart_clear_rx_bytes ) ;
2007-02-14 04:37:28 +03:00
2006-12-09 05:27:47 +03:00
/**
* struct list_buffer - An element for a port device fifo buffer list .
*/
struct list_buffer {
struct list_head link ;
const unsigned char * head ;
const unsigned char * tail ;
unsigned long dbg_number ;
unsigned char data [ ] ;
} ;
/**
* ps3_vuart_write - the entry point for writing data to a port
2007-06-16 02:01:06 +04:00
* @ dev : The struct ps3_system_bus_device instance .
2006-12-09 05:27:47 +03:00
*
* If the port is idle on entry as much of the incoming data is written to
* the port as the port will accept . Otherwise a list buffer is created
* and any remaning incoming data is copied to that buffer . The buffer is
* then enqueued for transmision via the transmit interrupt .
*/
2007-06-16 02:01:06 +04:00
int ps3_vuart_write ( struct ps3_system_bus_device * dev , const void * buf ,
2006-12-09 05:27:47 +03:00
unsigned int bytes )
{
static unsigned long dbg_number ;
int result ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
unsigned long flags ;
struct list_buffer * lb ;
dev_dbg ( & dev - > core , " %s:%d: %u(%xh) bytes \n " , __func__ , __LINE__ ,
bytes , bytes ) ;
2007-06-16 02:01:06 +04:00
spin_lock_irqsave ( & priv - > tx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( list_empty ( & priv - > tx_list . head ) ) {
2009-01-13 22:59:41 +03:00
u64 bytes_written ;
2006-12-09 05:27:47 +03:00
result = ps3_vuart_raw_write ( dev , buf , bytes , & bytes_written ) ;
2007-06-16 02:01:06 +04:00
spin_unlock_irqrestore ( & priv - > tx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
if ( result ) {
dev_dbg ( & dev - > core ,
" %s:%d: ps3_vuart_raw_write failed \n " ,
__func__ , __LINE__ ) ;
return result ;
}
if ( bytes_written = = bytes ) {
dev_dbg ( & dev - > core , " %s:%d: wrote %xh bytes \n " ,
__func__ , __LINE__ , bytes ) ;
return 0 ;
}
bytes - = bytes_written ;
buf + = bytes_written ;
} else
2007-06-16 02:01:06 +04:00
spin_unlock_irqrestore ( & priv - > tx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
lb = kmalloc ( sizeof ( struct list_buffer ) + bytes , GFP_KERNEL ) ;
2008-01-18 23:32:19 +03:00
if ( ! lb )
2006-12-09 05:27:47 +03:00
return - ENOMEM ;
memcpy ( lb - > data , buf , bytes ) ;
lb - > head = lb - > data ;
lb - > tail = lb - > data + bytes ;
lb - > dbg_number = + + dbg_number ;
2007-06-16 02:01:06 +04:00
spin_lock_irqsave ( & priv - > tx_list . lock , flags ) ;
list_add_tail ( & lb - > link , & priv - > tx_list . head ) ;
2006-12-09 05:27:47 +03:00
ps3_vuart_enable_interrupt_tx ( dev ) ;
2007-06-16 02:01:06 +04:00
spin_unlock_irqrestore ( & priv - > tx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: queued buf_%lu, %xh bytes \n " ,
__func__ , __LINE__ , lb - > dbg_number , bytes ) ;
return 0 ;
}
2007-06-16 02:01:06 +04:00
EXPORT_SYMBOL_GPL ( ps3_vuart_write ) ;
/**
* ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list .
* @ dev : The struct ps3_system_bus_device instance .
* @ bytes_queued : Number of bytes queued to the buffer list .
*
* Must be called with priv - > rx_list . lock held .
*/
static int ps3_vuart_queue_rx_bytes ( struct ps3_system_bus_device * dev ,
u64 * bytes_queued )
{
static unsigned long dbg_number ;
int result ;
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
struct list_buffer * lb ;
u64 bytes ;
* bytes_queued = 0 ;
result = ps3_vuart_get_rx_bytes_waiting ( dev , & bytes ) ;
BUG_ON ( result ) ;
if ( result )
return - EIO ;
if ( ! bytes )
return 0 ;
/* Add some extra space for recently arrived data. */
bytes + = 128 ;
lb = kmalloc ( sizeof ( struct list_buffer ) + bytes , GFP_ATOMIC ) ;
if ( ! lb )
return - ENOMEM ;
ps3_vuart_raw_read ( dev , lb - > data , bytes , & bytes ) ;
lb - > head = lb - > data ;
lb - > tail = lb - > data + bytes ;
lb - > dbg_number = + + dbg_number ;
list_add_tail ( & lb - > link , & priv - > rx_list . head ) ;
priv - > rx_list . bytes_held + = bytes ;
2009-01-13 23:10:06 +03:00
dev_dbg ( & dev - > core , " %s:%d: buf_%lu: queued %llxh bytes \n " ,
2007-06-16 02:01:06 +04:00
__func__ , __LINE__ , lb - > dbg_number , bytes ) ;
* bytes_queued = bytes ;
return 0 ;
}
2006-12-09 05:27:47 +03:00
/**
2007-06-16 02:01:06 +04:00
* ps3_vuart_read - The entry point for reading data from a port .
2006-12-09 05:27:47 +03:00
*
2007-06-16 02:01:06 +04:00
* Queue data waiting at the port , and if enough bytes to satisfy the request
* are held in the buffer list those bytes are dequeued and copied to the
* caller ' s buffer . Emptied list buffers are retiered . If the request cannot
* be statified by bytes held in the list buffers - EAGAIN is returned .
2006-12-09 05:27:47 +03:00
*/
2007-06-16 02:01:06 +04:00
int ps3_vuart_read ( struct ps3_system_bus_device * dev , void * buf ,
2006-12-09 05:27:47 +03:00
unsigned int bytes )
{
2007-06-16 02:01:06 +04:00
int result ;
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
unsigned long flags ;
struct list_buffer * lb , * n ;
unsigned long bytes_read ;
dev_dbg ( & dev - > core , " %s:%d: %u(%xh) bytes \n " , __func__ , __LINE__ ,
bytes , bytes ) ;
2007-06-16 02:01:06 +04:00
spin_lock_irqsave ( & priv - > rx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
/* Queue rx bytes here for polled reads. */
while ( priv - > rx_list . bytes_held < bytes ) {
u64 tmp ;
result = ps3_vuart_queue_rx_bytes ( dev , & tmp ) ;
if ( result | | ! tmp ) {
dev_dbg ( & dev - > core , " %s:%d: starved for %lxh bytes \n " ,
__func__ , __LINE__ ,
bytes - priv - > rx_list . bytes_held ) ;
spin_unlock_irqrestore ( & priv - > rx_list . lock , flags ) ;
return - EAGAIN ;
}
2006-12-09 05:27:47 +03:00
}
2007-06-16 02:01:06 +04:00
list_for_each_entry_safe ( lb , n , & priv - > rx_list . head , link ) {
2006-12-09 05:27:47 +03:00
bytes_read = min ( ( unsigned int ) ( lb - > tail - lb - > head ) , bytes ) ;
memcpy ( buf , lb - > head , bytes_read ) ;
buf + = bytes_read ;
bytes - = bytes_read ;
2007-06-16 02:01:06 +04:00
priv - > rx_list . bytes_held - = bytes_read ;
2006-12-09 05:27:47 +03:00
if ( bytes_read < lb - > tail - lb - > head ) {
lb - > head + = bytes_read ;
2007-02-14 04:37:28 +03:00
dev_dbg ( & dev - > core , " %s:%d: buf_%lu: dequeued %lxh "
" bytes \n " , __func__ , __LINE__ , lb - > dbg_number ,
bytes_read ) ;
2007-06-16 02:01:06 +04:00
spin_unlock_irqrestore ( & priv - > rx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
return 0 ;
}
2007-02-14 04:37:28 +03:00
dev_dbg ( & dev - > core , " %s:%d: buf_%lu: free, dequeued %lxh "
" bytes \n " , __func__ , __LINE__ , lb - > dbg_number ,
bytes_read ) ;
2006-12-09 05:27:47 +03:00
list_del ( & lb - > link ) ;
kfree ( lb ) ;
}
2007-06-16 02:01:06 +04:00
spin_unlock_irqrestore ( & priv - > rx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
return 0 ;
}
2007-06-16 02:01:06 +04:00
EXPORT_SYMBOL_GPL ( ps3_vuart_read ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
/**
* ps3_vuart_work - Asynchronous read handler .
*/
static void ps3_vuart_work ( struct work_struct * work )
{
struct ps3_system_bus_device * dev =
ps3_vuart_work_to_system_bus_dev ( work ) ;
struct ps3_vuart_port_driver * drv =
ps3_system_bus_dev_to_vuart_drv ( dev ) ;
BUG_ON ( ! drv ) ;
drv - > work ( dev ) ;
}
int ps3_vuart_read_async ( struct ps3_system_bus_device * dev , unsigned int bytes )
2007-02-07 01:23:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2007-02-07 01:23:47 +03:00
unsigned long flags ;
2007-06-16 02:01:06 +04:00
if ( priv - > rx_list . work . trigger ) {
2007-02-07 01:23:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: warning, multiple calls \n " ,
__func__ , __LINE__ ) ;
return - EAGAIN ;
}
BUG_ON ( ! bytes ) ;
2007-06-16 02:01:06 +04:00
PREPARE_WORK ( & priv - > rx_list . work . work , ps3_vuart_work ) ;
2007-02-07 01:23:47 +03:00
2007-06-16 02:01:06 +04:00
spin_lock_irqsave ( & priv - > rx_list . lock , flags ) ;
if ( priv - > rx_list . bytes_held > = bytes ) {
2007-02-07 01:23:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: schedule_work %xh bytes \n " ,
__func__ , __LINE__ , bytes ) ;
2007-06-16 02:01:06 +04:00
schedule_work ( & priv - > rx_list . work . work ) ;
spin_unlock_irqrestore ( & priv - > rx_list . lock , flags ) ;
2007-02-07 01:23:47 +03:00
return 0 ;
}
2007-06-16 02:01:06 +04:00
priv - > rx_list . work . trigger = bytes ;
spin_unlock_irqrestore ( & priv - > rx_list . lock , flags ) ;
2007-02-07 01:23:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: waiting for %u(%xh) bytes \n " , __func__ ,
__LINE__ , bytes , bytes ) ;
return 0 ;
}
2007-06-16 02:01:06 +04:00
EXPORT_SYMBOL_GPL ( ps3_vuart_read_async ) ;
2007-02-07 01:23:47 +03:00
2007-06-16 02:01:06 +04:00
void ps3_vuart_cancel_async ( struct ps3_system_bus_device * dev )
2007-02-07 01:23:47 +03:00
{
2007-06-16 02:01:06 +04:00
to_port_priv ( dev ) - > rx_list . work . trigger = 0 ;
2007-02-07 01:23:47 +03:00
}
2007-06-16 02:01:06 +04:00
EXPORT_SYMBOL_GPL ( ps3_vuart_cancel_async ) ;
2007-02-07 01:23:47 +03:00
2006-12-09 05:27:47 +03:00
/**
* ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler
*
* Services the transmit interrupt for the port . Writes as much data from the
* buffer list as the port will accept . Retires any emptied list buffers and
* adjusts the final list buffer state for a partial write .
*/
2007-06-16 02:01:06 +04:00
static int ps3_vuart_handle_interrupt_tx ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
int result = 0 ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
unsigned long flags ;
struct list_buffer * lb , * n ;
unsigned long bytes_total = 0 ;
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
2007-06-16 02:01:06 +04:00
spin_lock_irqsave ( & priv - > tx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
list_for_each_entry_safe ( lb , n , & priv - > tx_list . head , link ) {
2006-12-09 05:27:47 +03:00
2009-01-13 22:59:41 +03:00
u64 bytes_written ;
2006-12-09 05:27:47 +03:00
result = ps3_vuart_raw_write ( dev , lb - > head , lb - > tail - lb - > head ,
& bytes_written ) ;
if ( result ) {
dev_dbg ( & dev - > core ,
" %s:%d: ps3_vuart_raw_write failed \n " ,
__func__ , __LINE__ ) ;
break ;
}
bytes_total + = bytes_written ;
if ( bytes_written < lb - > tail - lb - > head ) {
lb - > head + = bytes_written ;
dev_dbg ( & dev - > core ,
2009-01-13 22:59:41 +03:00
" %s:%d cleared buf_%lu, %llxh bytes \n " ,
2006-12-09 05:27:47 +03:00
__func__ , __LINE__ , lb - > dbg_number ,
bytes_written ) ;
goto port_full ;
}
dev_dbg ( & dev - > core , " %s:%d free buf_%lu \n " , __func__ , __LINE__ ,
lb - > dbg_number ) ;
list_del ( & lb - > link ) ;
kfree ( lb ) ;
}
ps3_vuart_disable_interrupt_tx ( dev ) ;
port_full :
2007-06-16 02:01:06 +04:00
spin_unlock_irqrestore ( & priv - > tx_list . lock , flags ) ;
2006-12-09 05:27:47 +03:00
dev_dbg ( & dev - > core , " %s:%d wrote %lxh bytes total \n " ,
__func__ , __LINE__ , bytes_total ) ;
return result ;
}
/**
* ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler
*
* Services the receive interrupt for the port . Creates a list buffer and
* copies all waiting port data to that buffer and enqueues the buffer in the
* buffer list . Buffer list data is dequeued via ps3_vuart_read .
*/
2007-06-16 02:01:06 +04:00
static int ps3_vuart_handle_interrupt_rx ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
int result ;
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
unsigned long flags ;
2007-06-16 02:01:06 +04:00
u64 bytes ;
2006-12-09 05:27:47 +03:00
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
2007-06-16 02:01:06 +04:00
spin_lock_irqsave ( & priv - > rx_list . lock , flags ) ;
result = ps3_vuart_queue_rx_bytes ( dev , & bytes ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( result ) {
spin_unlock_irqrestore ( & priv - > rx_list . lock , flags ) ;
return result ;
}
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( priv - > rx_list . work . trigger & & priv - > rx_list . bytes_held
> = priv - > rx_list . work . trigger ) {
2007-02-07 01:23:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: schedule_work %lxh bytes \n " ,
2007-06-16 02:01:06 +04:00
__func__ , __LINE__ , priv - > rx_list . work . trigger ) ;
priv - > rx_list . work . trigger = 0 ;
schedule_work ( & priv - > rx_list . work . work ) ;
2007-02-07 01:23:47 +03:00
}
2007-06-16 02:01:06 +04:00
spin_unlock_irqrestore ( & priv - > rx_list . lock , flags ) ;
return result ;
2006-12-09 05:27:47 +03:00
}
static int ps3_vuart_handle_interrupt_disconnect (
2007-06-16 02:01:06 +04:00
struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
BUG_ON ( " no support " ) ;
return - 1 ;
}
/**
* ps3_vuart_handle_port_interrupt - second stage interrupt handler
*
* Services any pending interrupt types for the port . Passes control to the
* third stage type specific interrupt handler . Returns control to the first
* stage handler after one iteration .
*/
2007-06-16 02:01:06 +04:00
static int ps3_vuart_handle_port_interrupt ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
int result ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
2006-12-09 05:27:47 +03:00
unsigned long status ;
2007-02-14 04:37:28 +03:00
result = ps3_vuart_get_interrupt_status ( dev , & status ) ;
2006-12-09 05:27:47 +03:00
if ( result )
return result ;
dev_dbg ( & dev - > core , " %s:%d: status: %lxh \n " , __func__ , __LINE__ ,
status ) ;
if ( status & INTERRUPT_MASK_DISCONNECT ) {
2007-06-16 02:01:06 +04:00
priv - > stats . disconnect_interrupts + + ;
2006-12-09 05:27:47 +03:00
result = ps3_vuart_handle_interrupt_disconnect ( dev ) ;
if ( result )
ps3_vuart_disable_interrupt_disconnect ( dev ) ;
}
if ( status & INTERRUPT_MASK_TX ) {
2007-06-16 02:01:06 +04:00
priv - > stats . tx_interrupts + + ;
2006-12-09 05:27:47 +03:00
result = ps3_vuart_handle_interrupt_tx ( dev ) ;
if ( result )
ps3_vuart_disable_interrupt_tx ( dev ) ;
}
if ( status & INTERRUPT_MASK_RX ) {
2007-06-16 02:01:06 +04:00
priv - > stats . rx_interrupts + + ;
2006-12-09 05:27:47 +03:00
result = ps3_vuart_handle_interrupt_rx ( dev ) ;
if ( result )
ps3_vuart_disable_interrupt_rx ( dev ) ;
}
return 0 ;
}
2007-02-14 04:37:28 +03:00
struct vuart_bus_priv {
2007-06-16 02:01:06 +04:00
struct ports_bmp * bmp ;
2006-12-09 05:27:47 +03:00
unsigned int virq ;
2008-01-18 23:32:53 +03:00
struct mutex probe_mutex ;
2007-02-14 04:37:28 +03:00
int use_count ;
2007-06-16 02:01:06 +04:00
struct ps3_system_bus_device * devices [ PORT_COUNT ] ;
2007-02-14 04:37:28 +03:00
} static vuart_bus_priv ;
2006-12-09 05:27:47 +03:00
/**
* ps3_vuart_irq_handler - first stage interrupt handler
*
* Loops finding any interrupting port and its associated instance data .
* Passes control to the second stage port specific interrupt handler . Loops
* until all outstanding interrupts are serviced .
*/
static irqreturn_t ps3_vuart_irq_handler ( int irq , void * _private )
{
2007-06-16 02:01:06 +04:00
struct vuart_bus_priv * bus_priv = _private ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
BUG_ON ( ! bus_priv ) ;
2006-12-09 05:27:47 +03:00
while ( 1 ) {
unsigned int port ;
2007-06-16 02:01:06 +04:00
dump_ports_bmp ( bus_priv - > bmp ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
port = ( BITS_PER_LONG - 1 ) - __ilog2 ( bus_priv - > bmp - > status ) ;
2006-12-09 05:27:47 +03:00
if ( port = = BITS_PER_LONG )
break ;
BUG_ON ( port > = PORT_COUNT ) ;
2007-02-14 04:37:28 +03:00
BUG_ON ( ! bus_priv - > devices [ port ] ) ;
2006-12-09 05:27:47 +03:00
2007-02-14 04:37:28 +03:00
ps3_vuart_handle_port_interrupt ( bus_priv - > devices [ port ] ) ;
2006-12-09 05:27:47 +03:00
}
return IRQ_HANDLED ;
}
2007-06-16 02:01:06 +04:00
static int ps3_vuart_bus_interrupt_get ( void )
2006-12-09 05:27:47 +03:00
{
int result ;
2007-06-16 02:01:06 +04:00
pr_debug ( " -> %s:%d \n " , __func__ , __LINE__ ) ;
vuart_bus_priv . use_count + + ;
BUG_ON ( vuart_bus_priv . use_count > 2 ) ;
2008-01-18 23:32:19 +03:00
if ( vuart_bus_priv . use_count ! = 1 )
2007-06-16 02:01:06 +04:00
return 0 ;
BUG_ON ( vuart_bus_priv . bmp ) ;
vuart_bus_priv . bmp = kzalloc ( sizeof ( struct ports_bmp ) , GFP_KERNEL ) ;
if ( ! vuart_bus_priv . bmp ) {
pr_debug ( " %s:%d: kzalloc failed. \n " , __func__ , __LINE__ ) ;
result = - ENOMEM ;
goto fail_bmp_malloc ;
}
result = ps3_vuart_irq_setup ( PS3_BINDING_CPU_ANY , vuart_bus_priv . bmp ,
& vuart_bus_priv . virq ) ;
if ( result ) {
pr_debug ( " %s:%d: ps3_vuart_irq_setup failed (%d) \n " ,
__func__ , __LINE__ , result ) ;
result = - EPERM ;
goto fail_alloc_irq ;
}
result = request_irq ( vuart_bus_priv . virq , ps3_vuart_irq_handler ,
IRQF_DISABLED , " vuart " , & vuart_bus_priv ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( result ) {
pr_debug ( " %s:%d: request_irq failed (%d) \n " ,
__func__ , __LINE__ , result ) ;
goto fail_request_irq ;
}
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
pr_debug ( " <- %s:%d: ok \n " , __func__ , __LINE__ ) ;
2006-12-09 05:27:47 +03:00
return result ;
2007-06-16 02:01:06 +04:00
fail_request_irq :
ps3_vuart_irq_destroy ( vuart_bus_priv . virq ) ;
vuart_bus_priv . virq = NO_IRQ ;
fail_alloc_irq :
kfree ( vuart_bus_priv . bmp ) ;
vuart_bus_priv . bmp = NULL ;
fail_bmp_malloc :
vuart_bus_priv . use_count - - ;
pr_debug ( " <- %s:%d: failed \n " , __func__ , __LINE__ ) ;
return result ;
}
static int ps3_vuart_bus_interrupt_put ( void )
{
pr_debug ( " -> %s:%d \n " , __func__ , __LINE__ ) ;
vuart_bus_priv . use_count - - ;
BUG_ON ( vuart_bus_priv . use_count < 0 ) ;
if ( vuart_bus_priv . use_count ! = 0 )
return 0 ;
free_irq ( vuart_bus_priv . virq , & vuart_bus_priv ) ;
ps3_vuart_irq_destroy ( vuart_bus_priv . virq ) ;
vuart_bus_priv . virq = NO_IRQ ;
kfree ( vuart_bus_priv . bmp ) ;
vuart_bus_priv . bmp = NULL ;
pr_debug ( " <- %s:%d \n " , __func__ , __LINE__ ) ;
return 0 ;
2006-12-09 05:27:47 +03:00
}
2007-06-16 02:01:06 +04:00
static int ps3_vuart_probe ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
int result ;
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_driver * drv ;
struct ps3_vuart_port_priv * priv = NULL ;
2006-12-09 05:27:47 +03:00
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
2007-06-16 02:01:06 +04:00
drv = ps3_system_bus_dev_to_vuart_drv ( dev ) ;
dev_dbg ( & dev - > core , " %s:%d: (%s) \n " , __func__ , __LINE__ ,
drv - > core . core . name ) ;
2006-12-09 05:27:47 +03:00
BUG_ON ( ! drv ) ;
2007-06-16 02:01:06 +04:00
if ( dev - > port_number > = PORT_COUNT ) {
BUG ( ) ;
return - EINVAL ;
}
2007-02-14 04:37:28 +03:00
2008-01-18 23:32:53 +03:00
mutex_lock ( & vuart_bus_priv . probe_mutex ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
result = ps3_vuart_bus_interrupt_get ( ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( result )
goto fail_setup_interrupt ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( vuart_bus_priv . devices [ dev - > port_number ] ) {
2006-12-09 05:27:47 +03:00
dev_dbg ( & dev - > core , " %s:%d: port busy (%d) \n " , __func__ ,
2007-06-16 02:01:06 +04:00
__LINE__ , dev - > port_number ) ;
2006-12-09 05:27:47 +03:00
result = - EBUSY ;
2007-06-16 02:01:06 +04:00
goto fail_busy ;
2006-12-09 05:27:47 +03:00
}
2007-06-16 02:01:06 +04:00
vuart_bus_priv . devices [ dev - > port_number ] = dev ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
/* Setup dev->driver_priv. */
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
dev - > driver_priv = kzalloc ( sizeof ( struct ps3_vuart_port_priv ) ,
GFP_KERNEL ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
if ( ! dev - > driver_priv ) {
2007-02-14 04:37:28 +03:00
result = - ENOMEM ;
2007-06-16 02:01:06 +04:00
goto fail_dev_malloc ;
2007-02-14 04:37:28 +03:00
}
2007-06-16 02:01:06 +04:00
priv = to_port_priv ( dev ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
INIT_LIST_HEAD ( & priv - > tx_list . head ) ;
spin_lock_init ( & priv - > tx_list . lock ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
INIT_LIST_HEAD ( & priv - > rx_list . head ) ;
spin_lock_init ( & priv - > rx_list . lock ) ;
2007-02-07 01:23:47 +03:00
2007-06-16 02:01:06 +04:00
INIT_WORK ( & priv - > rx_list . work . work , NULL ) ;
priv - > rx_list . work . trigger = 0 ;
priv - > rx_list . work . dev = dev ;
2006-12-09 05:27:47 +03:00
/* clear stale pending interrupts */
2007-02-14 04:37:28 +03:00
ps3_vuart_clear_rx_bytes ( dev , 0 ) ;
ps3_vuart_set_interrupt_mask ( dev , INTERRUPT_MASK_RX ) ;
2006-12-09 05:27:47 +03:00
ps3_vuart_set_triggers ( dev , 1 , 1 ) ;
if ( drv - > probe )
result = drv - > probe ( dev ) ;
else {
result = 0 ;
dev_info ( & dev - > core , " %s:%d: no probe method \n " , __func__ ,
__LINE__ ) ;
}
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: drv->probe failed \n " ,
__func__ , __LINE__ ) ;
goto fail_probe ;
}
2008-01-18 23:32:53 +03:00
mutex_unlock ( & vuart_bus_priv . probe_mutex ) ;
2007-02-14 04:37:28 +03:00
2006-12-09 05:27:47 +03:00
return result ;
fail_probe :
2007-02-14 04:37:28 +03:00
ps3_vuart_set_interrupt_mask ( dev , 0 ) ;
2007-06-16 02:01:06 +04:00
kfree ( dev - > driver_priv ) ;
dev - > driver_priv = NULL ;
fail_dev_malloc :
vuart_bus_priv . devices [ dev - > port_number ] = NULL ;
fail_busy :
ps3_vuart_bus_interrupt_put ( ) ;
fail_setup_interrupt :
2008-01-18 23:32:53 +03:00
mutex_unlock ( & vuart_bus_priv . probe_mutex ) ;
2007-06-16 02:01:06 +04:00
dev_dbg ( & dev - > core , " %s:%d: failed \n " , __func__ , __LINE__ ) ;
2006-12-09 05:27:47 +03:00
return result ;
}
2007-06-16 02:01:06 +04:00
/**
* ps3_vuart_cleanup - common cleanup helper .
* @ dev : The struct ps3_system_bus_device instance .
*
* Cleans interrupts and HV resources . Must be called with
* vuart_bus_priv . probe_mutex held . Used by ps3_vuart_remove and
* ps3_vuart_shutdown . After this call , polled reading will still work .
*/
static int ps3_vuart_cleanup ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
ps3_vuart_cancel_async ( dev ) ;
ps3_vuart_set_interrupt_mask ( dev , 0 ) ;
ps3_vuart_bus_interrupt_put ( ) ;
return 0 ;
}
/**
* ps3_vuart_remove - Completely clean the device instance .
* @ dev : The struct ps3_system_bus_device instance .
*
* Cleans all memory , interrupts and HV resources . After this call the
* device can no longer be used .
*/
static int ps3_vuart_remove ( struct ps3_system_bus_device * dev )
{
struct ps3_vuart_port_priv * priv = to_port_priv ( dev ) ;
struct ps3_vuart_port_driver * drv ;
BUG_ON ( ! dev ) ;
2006-12-09 05:27:47 +03:00
2008-01-18 23:32:53 +03:00
mutex_lock ( & vuart_bus_priv . probe_mutex ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
dev_dbg ( & dev - > core , " -> %s:%d: match_id %d \n " , __func__ , __LINE__ ,
dev - > match_id ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( ! dev - > core . driver ) {
dev_dbg ( & dev - > core , " %s:%d: no driver bound \n " , __func__ ,
__LINE__ ) ;
2008-01-18 23:32:53 +03:00
mutex_unlock ( & vuart_bus_priv . probe_mutex ) ;
2007-06-16 02:01:06 +04:00
return 0 ;
}
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
drv = ps3_system_bus_dev_to_vuart_drv ( dev ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
BUG_ON ( ! drv ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( drv - > remove ) {
drv - > remove ( dev ) ;
} else {
dev_dbg ( & dev - > core , " %s:%d: no remove method \n " , __func__ ,
__LINE__ ) ;
2007-02-14 04:37:28 +03:00
BUG ( ) ;
2006-12-09 05:27:47 +03:00
}
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
ps3_vuart_cleanup ( dev ) ;
vuart_bus_priv . devices [ dev - > port_number ] = NULL ;
kfree ( priv ) ;
priv = NULL ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
dev_dbg ( & dev - > core , " <- %s:%d \n " , __func__ , __LINE__ ) ;
2008-01-18 23:32:53 +03:00
mutex_unlock ( & vuart_bus_priv . probe_mutex ) ;
2006-12-09 05:27:47 +03:00
return 0 ;
}
/**
2007-06-16 02:01:06 +04:00
* ps3_vuart_shutdown - Cleans interrupts and HV resources .
* @ dev : The struct ps3_system_bus_device instance .
2006-12-09 05:27:47 +03:00
*
2007-06-16 02:01:06 +04:00
* Cleans interrupts and HV resources . After this call the
* device can still be used in polling mode . This behavior required
* by sys - manager to be able to complete the device power operation
* sequence .
2006-12-09 05:27:47 +03:00
*/
2007-06-16 02:01:06 +04:00
static int ps3_vuart_shutdown ( struct ps3_system_bus_device * dev )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
struct ps3_vuart_port_driver * drv ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
BUG_ON ( ! dev ) ;
2007-02-14 04:37:28 +03:00
2008-01-18 23:32:53 +03:00
mutex_lock ( & vuart_bus_priv . probe_mutex ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
dev_dbg ( & dev - > core , " -> %s:%d: match_id %d \n " , __func__ , __LINE__ ,
dev - > match_id ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
if ( ! dev - > core . driver ) {
dev_dbg ( & dev - > core , " %s:%d: no driver bound \n " , __func__ ,
__LINE__ ) ;
2008-01-18 23:32:53 +03:00
mutex_unlock ( & vuart_bus_priv . probe_mutex ) ;
2007-06-16 02:01:06 +04:00
return 0 ;
}
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
drv = ps3_system_bus_dev_to_vuart_drv ( dev ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
BUG_ON ( ! drv ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
if ( drv - > shutdown )
drv - > shutdown ( dev ) ;
else if ( drv - > remove ) {
dev_dbg ( & dev - > core , " %s:%d: no shutdown, calling remove \n " ,
__func__ , __LINE__ ) ;
drv - > remove ( dev ) ;
} else {
dev_dbg ( & dev - > core , " %s:%d: no shutdown method \n " , __func__ ,
__LINE__ ) ;
BUG ( ) ;
}
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
ps3_vuart_cleanup ( dev ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
dev_dbg ( & dev - > core , " <- %s:%d \n " , __func__ , __LINE__ ) ;
2007-02-14 04:37:28 +03:00
2008-01-18 23:32:53 +03:00
mutex_unlock ( & vuart_bus_priv . probe_mutex ) ;
2007-06-16 02:01:06 +04:00
return 0 ;
2006-12-09 05:27:47 +03:00
}
2007-06-16 02:01:06 +04:00
static int __init ps3_vuart_bus_init ( void )
2006-12-09 05:27:47 +03:00
{
2007-06-16 02:01:06 +04:00
pr_debug ( " %s:%d: \n " , __func__ , __LINE__ ) ;
2007-02-14 04:37:28 +03:00
2007-06-16 02:01:06 +04:00
if ( ! firmware_has_feature ( FW_FEATURE_PS3_LV1 ) )
return - ENODEV ;
2006-12-09 05:27:47 +03:00
2008-01-18 23:32:53 +03:00
mutex_init ( & vuart_bus_priv . probe_mutex ) ;
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
return 0 ;
}
2006-12-09 05:27:47 +03:00
2007-06-16 02:01:06 +04:00
static void __exit ps3_vuart_bus_exit ( void )
{
pr_debug ( " %s:%d: \n " , __func__ , __LINE__ ) ;
2006-12-09 05:27:47 +03:00
}
2007-06-16 02:01:06 +04:00
core_initcall ( ps3_vuart_bus_init ) ;
module_exit ( ps3_vuart_bus_exit ) ;
2006-12-09 05:27:47 +03:00
/**
* ps3_vuart_port_driver_register - Add a vuart port device driver .
*/
int ps3_vuart_port_driver_register ( struct ps3_vuart_port_driver * drv )
{
int result ;
2007-06-16 02:01:06 +04:00
pr_debug ( " %s:%d: (%s) \n " , __func__ , __LINE__ , drv - > core . core . name ) ;
BUG_ON ( ! drv - > core . match_id ) ;
BUG_ON ( ! drv - > core . core . name ) ;
drv - > core . probe = ps3_vuart_probe ;
drv - > core . remove = ps3_vuart_remove ;
drv - > core . shutdown = ps3_vuart_shutdown ;
result = ps3_system_bus_driver_register ( & drv - > core ) ;
2006-12-09 05:27:47 +03:00
return result ;
}
EXPORT_SYMBOL_GPL ( ps3_vuart_port_driver_register ) ;
/**
* ps3_vuart_port_driver_unregister - Remove a vuart port device driver .
*/
void ps3_vuart_port_driver_unregister ( struct ps3_vuart_port_driver * drv )
{
2007-06-16 02:01:06 +04:00
pr_debug ( " %s:%d: (%s) \n " , __func__ , __LINE__ , drv - > core . core . name ) ;
ps3_system_bus_driver_unregister ( & drv - > core ) ;
2006-12-09 05:27:47 +03:00
}
EXPORT_SYMBOL_GPL ( ps3_vuart_port_driver_unregister ) ;