Commit 7626e78d authored by Geoff Levand's avatar Geoff Levand Committed by Paul Mackerras

[POWERPC] PS3: Vuart rework

PS3 vuart updates to reflect the new PS3 unified device support.
 - Move vuart devices to the PS3 system bus.
 - Replace use of ps3_vuart_port_device with ps3_system_bus_device.
 - Make the PS3 vuart bus driver a loadable module.
 - Add remove() and shutdown() routines.
 - Move ps3_vuart_work into ps3_vuart_port_priv.tx_list.
 - Remove redundant spinlock ps3_vuart_work.lock.
 - No longer free ps3_vuart_port_device.priv on shutdown.
 - Cleanup Kconfig defs.
 - Export symbols needed for modular port drivers.
 - Arrange to use port numbers found in repository.
 - Fix bugs in ps3_vuart_read_async() and polled reading
 - Cleanup handling of shared interrupt with ps3_vuart_bus_interrupt_get()
   and ps3_vuart_bus_interrupt_put()
 - Add more comments to vuart.c.
Signed-off-by: default avatarGeoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent a3323d1a
...@@ -73,18 +73,12 @@ config PS3_USE_LPAR_ADDR ...@@ -73,18 +73,12 @@ config PS3_USE_LPAR_ADDR
config PS3_VUART config PS3_VUART
depends on PPC_PS3 depends on PPC_PS3
bool "PS3 Virtual UART support" if PS3_ADVANCED tristate
default y
help
Include support for the PS3 Virtual UART.
This support is required for several system services
including the System Manager and AV Settings. In
general, all users will say Y.
config PS3_PS3AV config PS3_PS3AV
depends on PPC_PS3
tristate "PS3 AV settings driver" if PS3_ADVANCED tristate "PS3 AV settings driver" if PS3_ADVANCED
depends on PS3_VUART select PS3_VUART
default y default y
help help
Include support for the PS3 AV Settings driver. Include support for the PS3 AV Settings driver.
...@@ -93,13 +87,14 @@ config PS3_PS3AV ...@@ -93,13 +87,14 @@ config PS3_PS3AV
general, all users will say Y or M. general, all users will say Y or M.
config PS3_SYS_MANAGER config PS3_SYS_MANAGER
bool "PS3 System Manager driver" if PS3_ADVANCED depends on PPC_PS3
depends on PS3_VUART tristate "PS3 System Manager driver" if PS3_ADVANCED
default y select PS3_VUART
default m
help help
Include support for the PS3 System Manager. Include support for the PS3 System Manager.
This support is required for system control. In This support is required for system control. In
general, all users will say Y. general, all users will say Y or M.
endmenu endmenu
...@@ -564,6 +564,7 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, ...@@ -564,6 +564,7 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
return result; return result;
} }
EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup);
int ps3_vuart_irq_destroy(unsigned int virq) int ps3_vuart_irq_destroy(unsigned int virq)
{ {
...@@ -583,6 +584,7 @@ int ps3_vuart_irq_destroy(unsigned int virq) ...@@ -583,6 +584,7 @@ int ps3_vuart_irq_destroy(unsigned int virq)
return result; return result;
} }
EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy);
/** /**
* ps3_spe_irq_setup - Setup an spe virq. * ps3_spe_irq_setup - Setup an spe virq.
......
...@@ -70,6 +70,34 @@ enum vuart_interrupt_mask { ...@@ -70,6 +70,34 @@ enum vuart_interrupt_mask {
INTERRUPT_MASK_DISCONNECT = 4, INTERRUPT_MASK_DISCONNECT = 4,
}; };
/**
* 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;
}
/** /**
* struct ports_bmp - bitmap indicating ports needing service. * struct ports_bmp - bitmap indicating ports needing service.
* *
...@@ -89,23 +117,6 @@ static void __maybe_unused _dump_ports_bmp( ...@@ -89,23 +117,6 @@ static void __maybe_unused _dump_ports_bmp(
pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status);
} }
static int ps3_vuart_match_id_to_port(enum ps3_match_id match_id,
unsigned int *port_number)
{
switch(match_id) {
case PS3_MATCH_ID_AV_SETTINGS:
*port_number = 0;
return 0;
case PS3_MATCH_ID_SYSTEM_MANAGER:
*port_number = 2;
return 0;
default:
WARN_ON(1);
*port_number = UINT_MAX;
return -EINVAL;
};
}
#define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) #define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__)
static void __maybe_unused _dump_port_params(unsigned int port_number, static void __maybe_unused _dump_port_params(unsigned int port_number,
const char* func, int line) const char* func, int line)
...@@ -144,14 +155,14 @@ struct vuart_triggers { ...@@ -144,14 +155,14 @@ struct vuart_triggers {
unsigned long tx; unsigned long tx;
}; };
int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, int ps3_vuart_get_triggers(struct ps3_system_bus_device *dev,
struct vuart_triggers *trig) struct vuart_triggers *trig)
{ {
int result; int result;
unsigned long size; unsigned long size;
unsigned long val; unsigned long val;
result = lv1_get_virtual_uart_param(dev->priv->port_number, result = lv1_get_virtual_uart_param(dev->port_number,
PARAM_TX_TRIGGER, &trig->tx); PARAM_TX_TRIGGER, &trig->tx);
if (result) { if (result) {
...@@ -160,7 +171,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, ...@@ -160,7 +171,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev,
return result; return result;
} }
result = lv1_get_virtual_uart_param(dev->priv->port_number, result = lv1_get_virtual_uart_param(dev->port_number,
PARAM_RX_BUF_SIZE, &size); PARAM_RX_BUF_SIZE, &size);
if (result) { if (result) {
...@@ -169,7 +180,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, ...@@ -169,7 +180,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev,
return result; return result;
} }
result = lv1_get_virtual_uart_param(dev->priv->port_number, result = lv1_get_virtual_uart_param(dev->port_number,
PARAM_RX_TRIGGER, &val); PARAM_RX_TRIGGER, &val);
if (result) { if (result) {
...@@ -186,13 +197,13 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, ...@@ -186,13 +197,13 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev,
return result; return result;
} }
int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, int ps3_vuart_set_triggers(struct ps3_system_bus_device *dev, unsigned int tx,
unsigned int rx) unsigned int rx)
{ {
int result; int result;
unsigned long size; unsigned long size;
result = lv1_set_virtual_uart_param(dev->priv->port_number, result = lv1_set_virtual_uart_param(dev->port_number,
PARAM_TX_TRIGGER, tx); PARAM_TX_TRIGGER, tx);
if (result) { if (result) {
...@@ -201,7 +212,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, ...@@ -201,7 +212,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx,
return result; return result;
} }
result = lv1_get_virtual_uart_param(dev->priv->port_number, result = lv1_get_virtual_uart_param(dev->port_number,
PARAM_RX_BUF_SIZE, &size); PARAM_RX_BUF_SIZE, &size);
if (result) { if (result) {
...@@ -210,7 +221,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, ...@@ -210,7 +221,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx,
return result; return result;
} }
result = lv1_set_virtual_uart_param(dev->priv->port_number, result = lv1_set_virtual_uart_param(dev->port_number,
PARAM_RX_TRIGGER, size - rx); PARAM_RX_TRIGGER, size - rx);
if (result) { if (result) {
...@@ -225,10 +236,12 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, ...@@ -225,10 +236,12 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx,
return result; return result;
} }
static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, static int ps3_vuart_get_rx_bytes_waiting(struct ps3_system_bus_device *dev,
u64 *bytes_waiting) u64 *bytes_waiting)
{ {
int result = lv1_get_virtual_uart_param(dev->priv->port_number, int result;
result = lv1_get_virtual_uart_param(dev->port_number,
PARAM_RX_BYTES, bytes_waiting); PARAM_RX_BYTES, bytes_waiting);
if (result) if (result)
...@@ -240,17 +253,24 @@ static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, ...@@ -240,17 +253,24 @@ static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev,
return result; return result;
} }
static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, /**
* 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,
unsigned long mask) unsigned long mask)
{ {
int result; int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask); dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask);
dev->priv->interrupt_mask = mask; priv->interrupt_mask = mask;
result = lv1_set_virtual_uart_param(dev->priv->port_number, result = lv1_set_virtual_uart_param(dev->port_number,
PARAM_INTERRUPT_MASK, dev->priv->interrupt_mask); PARAM_INTERRUPT_MASK, priv->interrupt_mask);
if (result) if (result)
dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n", dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n",
...@@ -259,79 +279,96 @@ static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, ...@@ -259,79 +279,96 @@ static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev,
return result; return result;
} }
static int ps3_vuart_get_interrupt_status(struct ps3_vuart_port_device *dev, static int ps3_vuart_get_interrupt_status(struct ps3_system_bus_device *dev,
unsigned long *status) unsigned long *status)
{ {
int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
u64 tmp; u64 tmp;
int result = lv1_get_virtual_uart_param(dev->priv->port_number,
result = lv1_get_virtual_uart_param(dev->port_number,
PARAM_INTERRUPT_STATUS, &tmp); PARAM_INTERRUPT_STATUS, &tmp);
if (result) if (result)
dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n", dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n",
__func__, __LINE__, ps3_result(result)); __func__, __LINE__, ps3_result(result));
*status = tmp & dev->priv->interrupt_mask; *status = tmp & priv->interrupt_mask;
dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n",
__func__, __LINE__, dev->priv->interrupt_mask, tmp, *status); __func__, __LINE__, priv->interrupt_mask, tmp, *status);
return result; return result;
} }
int ps3_vuart_enable_interrupt_tx(struct ps3_vuart_port_device *dev) int ps3_vuart_enable_interrupt_tx(struct ps3_system_bus_device *dev)
{ {
return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0 struct ps3_vuart_port_priv *priv = to_port_priv(dev);
: ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask
return (priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0
: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
| INTERRUPT_MASK_TX); | INTERRUPT_MASK_TX);
} }
int ps3_vuart_enable_interrupt_rx(struct ps3_vuart_port_device *dev) int ps3_vuart_enable_interrupt_rx(struct ps3_system_bus_device *dev)
{ {
return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0 struct ps3_vuart_port_priv *priv = to_port_priv(dev);
: ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask
return (priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0
: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
| INTERRUPT_MASK_RX); | INTERRUPT_MASK_RX);
} }
int ps3_vuart_enable_interrupt_disconnect(struct ps3_vuart_port_device *dev) int ps3_vuart_enable_interrupt_disconnect(struct ps3_system_bus_device *dev)
{ {
return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 struct ps3_vuart_port_priv *priv = to_port_priv(dev);
: ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask
return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0
: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
| INTERRUPT_MASK_DISCONNECT); | INTERRUPT_MASK_DISCONNECT);
} }
int ps3_vuart_disable_interrupt_tx(struct ps3_vuart_port_device *dev) int ps3_vuart_disable_interrupt_tx(struct ps3_system_bus_device *dev)
{ {
return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) struct ps3_vuart_port_priv *priv = to_port_priv(dev);
? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask
return (priv->interrupt_mask & INTERRUPT_MASK_TX)
? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
& ~INTERRUPT_MASK_TX) : 0; & ~INTERRUPT_MASK_TX) : 0;
} }
int ps3_vuart_disable_interrupt_rx(struct ps3_vuart_port_device *dev) int ps3_vuart_disable_interrupt_rx(struct ps3_system_bus_device *dev)
{ {
return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) struct ps3_vuart_port_priv *priv = to_port_priv(dev);
? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask
return (priv->interrupt_mask & INTERRUPT_MASK_RX)
? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
& ~INTERRUPT_MASK_RX) : 0; & ~INTERRUPT_MASK_RX) : 0;
} }
int ps3_vuart_disable_interrupt_disconnect(struct ps3_vuart_port_device *dev) int ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev)
{ {
return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) struct ps3_vuart_port_priv *priv = to_port_priv(dev);
? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask
return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT)
? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
& ~INTERRUPT_MASK_DISCONNECT) : 0; & ~INTERRUPT_MASK_DISCONNECT) : 0;
} }
/** /**
* ps3_vuart_raw_write - Low level write helper. * ps3_vuart_raw_write - Low level write helper.
* @dev: The struct ps3_system_bus_device instance.
* *
* Do not call ps3_vuart_raw_write directly, use ps3_vuart_write. * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write.
*/ */
static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev,
const void* buf, unsigned int bytes, unsigned long *bytes_written) const void* buf, unsigned int bytes, unsigned long *bytes_written)
{ {
int result; int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
result = lv1_write_virtual_uart(dev->priv->port_number, result = lv1_write_virtual_uart(dev->port_number,
ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written); ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written);
if (result) { if (result) {
...@@ -340,28 +377,30 @@ static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, ...@@ -340,28 +377,30 @@ static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev,
return result; return result;
} }
dev->priv->stats.bytes_written += *bytes_written; priv->stats.bytes_written += *bytes_written;
dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, __LINE__, dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, __LINE__,
*bytes_written, bytes, dev->priv->stats.bytes_written); *bytes_written, bytes, priv->stats.bytes_written);
return result; return result;
} }
/** /**
* ps3_vuart_raw_read - Low level read helper. * ps3_vuart_raw_read - Low level read helper.
* @dev: The struct ps3_system_bus_device instance.
* *
* Do not call ps3_vuart_raw_read directly, use ps3_vuart_read. * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read.
*/ */
static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, static int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf,
unsigned int bytes, unsigned long *bytes_read) unsigned int bytes, unsigned long *bytes_read)
{ {
int result; int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes);
result = lv1_read_virtual_uart(dev->priv->port_number, result = lv1_read_virtual_uart(dev->port_number,
ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read); ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read);
if (result) { if (result) {
...@@ -370,25 +409,27 @@ static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, ...@@ -370,25 +409,27 @@ static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf,
return result; return result;
} }
dev->priv->stats.bytes_read += *bytes_read; priv->stats.bytes_read += *bytes_read;
dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__,
*bytes_read, bytes, dev->priv->stats.bytes_read); *bytes_read, bytes, priv->stats.bytes_read);
return result; return result;
} }
/** /**
* ps3_vuart_clear_rx_bytes - Discard bytes received. * ps3_vuart_clear_rx_bytes - Discard bytes received.
* @dev: The struct ps3_system_bus_device instance.
* @bytes: Max byte count to discard, zero = all pending. * @bytes: Max byte count to discard, zero = all pending.
* *
* Used to clear pending rx interrupt source. Will not block. * Used to clear pending rx interrupt source. Will not block.
*/ */
void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev,
unsigned int bytes) unsigned int bytes)
{ {
int result; int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
u64 bytes_waiting; u64 bytes_waiting;
void* tmp; void* tmp;
...@@ -418,8 +459,9 @@ void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, ...@@ -418,8 +459,9 @@ void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev,
/* Don't include these bytes in the stats. */ /* Don't include these bytes in the stats. */
dev->priv->stats.bytes_read -= bytes_waiting; priv->stats.bytes_read -= bytes_waiting;
} }
EXPORT_SYMBOL_GPL(ps3_vuart_clear_rx_bytes);
/** /**
* struct list_buffer - An element for a port device fifo buffer list. * struct list_buffer - An element for a port device fifo buffer list.
...@@ -435,6 +477,7 @@ struct list_buffer { ...@@ -435,6 +477,7 @@ struct list_buffer {
/** /**
* ps3_vuart_write - the entry point for writing data to a port * ps3_vuart_write - the entry point for writing data to a port
* @dev: The struct ps3_system_bus_device instance.
* *
* If the port is idle on entry as much of the incoming data is written to * 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 * the port as the port will accept. Otherwise a list buffer is created
...@@ -442,25 +485,26 @@ struct list_buffer { ...@@ -442,25 +485,26 @@ struct list_buffer {
* then enqueued for transmision via the transmit interrupt. * then enqueued for transmision via the transmit interrupt.
*/ */
int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf,
unsigned int bytes) unsigned int bytes)
{ {
static unsigned long dbg_number; static unsigned long dbg_number;
int result; int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
unsigned long flags; unsigned long flags;
struct list_buffer *lb; struct list_buffer *lb;
dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
bytes, bytes); bytes, bytes);
spin_lock_irqsave(&dev->priv->tx_list.lock, flags); spin_lock_irqsave(&priv->tx_list.lock, flags);
if (list_empty(&dev->priv->tx_list.head)) { if (list_empty(&priv->tx_list.head)) {
unsigned long bytes_written; unsigned long bytes_written;
result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written);
spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); spin_unlock_irqrestore(&priv->tx_list.lock, flags);
if (result) { if (result) {
dev_dbg(&dev->core, dev_dbg(&dev->core,
...@@ -478,7 +522,7 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, ...@@ -478,7 +522,7 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf,
bytes -= bytes_written; bytes -= bytes_written;
buf += bytes_written; buf += bytes_written;
} else } else
spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); spin_unlock_irqrestore(&priv->tx_list.lock, flags);
lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL);
...@@ -491,29 +535,86 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, ...@@ -491,29 +535,86 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf,
lb->tail = lb->data + bytes; lb->tail = lb->data + bytes;
lb->dbg_number = ++dbg_number; lb->dbg_number = ++dbg_number;
spin_lock_irqsave(&dev->priv->tx_list.lock, flags); spin_lock_irqsave(&priv->tx_list.lock, flags);
list_add_tail(&lb->link, &dev->priv->tx_list.head); list_add_tail(&lb->link, &priv->tx_list.head);
ps3_vuart_enable_interrupt_tx(dev); ps3_vuart_enable_interrupt_tx(dev);
spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); spin_unlock_irqrestore(&priv->tx_list.lock, flags);
dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n",
__func__, __LINE__, lb->dbg_number, bytes); __func__, __LINE__, lb->dbg_number, bytes);
return 0; return 0;
} }
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;
dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n",
__func__, __LINE__, lb->dbg_number, bytes);
*bytes_queued = bytes;
return 0;
}
/** /**
* ps3_vuart_read - the entry point for reading data from a port * ps3_vuart_read - The entry point for reading data from a port.
* *
* If enough bytes to satisfy the request are held in the buffer list those * Queue data waiting at the port, and if enough bytes to satisfy the request
* bytes are dequeued and copied to the caller's buffer. Emptied list buffers * are held in the buffer list those bytes are dequeued and copied to the
* are retiered. If the request cannot be statified by bytes held in the list * caller's buffer. Emptied list buffers are retiered. If the request cannot
* buffers -EAGAIN is returned. * be statified by bytes held in the list buffers -EAGAIN is returned.
*/ */
int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf,
unsigned int bytes) unsigned int bytes)
{ {
int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
unsigned long flags; unsigned long flags;
struct list_buffer *lb, *n; struct list_buffer *lb, *n;
unsigned long bytes_read; unsigned long bytes_read;
...@@ -521,30 +622,37 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, ...@@ -521,30 +622,37 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
bytes, bytes); bytes, bytes);
spin_lock_irqsave(&dev->priv->rx_list.lock, flags); spin_lock_irqsave(&priv->rx_list.lock, flags);
if (dev->priv->rx_list.bytes_held < bytes) { /* Queue rx bytes here for polled reads. */
spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags);
dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", while (priv->rx_list.bytes_held < bytes) {
__func__, __LINE__, u64 tmp;
bytes - dev->priv->rx_list.bytes_held);
return -EAGAIN; 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;
}
} }
list_for_each_entry_safe(lb, n, &dev->priv->rx_list.head, link) { list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) {
bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); bytes_read = min((unsigned int)(lb->tail - lb->head), bytes);
memcpy(buf, lb->head, bytes_read); memcpy(buf, lb->head, bytes_read);
buf += bytes_read; buf += bytes_read;
bytes -= bytes_read; bytes -= bytes_read;
dev->priv->rx_list.bytes_held -= bytes_read; priv->rx_list.bytes_held -= bytes_read;
if (bytes_read < lb->tail - lb->head) { if (bytes_read < lb->tail - lb->head) {
lb->head += bytes_read; lb->head += bytes_read;
dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh " dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh "
"bytes\n", __func__, __LINE__, lb->dbg_number, "bytes\n", __func__, __LINE__, lb->dbg_number,
bytes_read); bytes_read);
spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); spin_unlock_irqrestore(&priv->rx_list.lock, flags);
return 0; return 0;
} }
...@@ -556,16 +664,32 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, ...@@ -556,16 +664,32 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
kfree(lb); kfree(lb);
} }
spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); spin_unlock_irqrestore(&priv->rx_list.lock, flags);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ps3_vuart_read);
int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, /**
unsigned int bytes) * 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)
{ {
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
unsigned long flags; unsigned long flags;
if(dev->priv->work.trigger) { if (priv->rx_list.work.trigger) {
dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n", dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n",
__func__, __LINE__); __func__, __LINE__);
return -EAGAIN; return -EAGAIN;
...@@ -573,30 +697,32 @@ int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, ...@@ -573,30 +697,32 @@ int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func,
BUG_ON(!bytes); BUG_ON(!bytes);
PREPARE_WORK(&dev->priv->work.work, func); PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work);
spin_lock_irqsave(&dev->priv->work.lock, flags); spin_lock_irqsave(&priv->rx_list.lock, flags);
if(dev->priv->rx_list.bytes_held >= bytes) { if (priv->rx_list.bytes_held >= bytes) {
dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n", dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n",
__func__, __LINE__, bytes); __func__, __LINE__, bytes);
schedule_work(&dev->priv->work.work); schedule_work(&priv->rx_list.work.work);
spin_unlock_irqrestore(&dev->priv->work.lock, flags); spin_unlock_irqrestore(&priv->rx_list.lock, flags);
return 0; return 0;
} }
dev->priv->work.trigger = bytes; priv->rx_list.work.trigger = bytes;
spin_unlock_irqrestore(&dev->priv->work.lock, flags); spin_unlock_irqrestore(&priv->rx_list.lock, flags);
dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__, dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__,
__LINE__, bytes, bytes); __LINE__, bytes, bytes);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ps3_vuart_read_async);
void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev) void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev)
{ {
dev->priv->work.trigger = 0; to_port_priv(dev)->rx_list.work.trigger = 0;
} }
EXPORT_SYMBOL_GPL(ps3_vuart_cancel_async);
/** /**
* ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler
...@@ -606,18 +732,19 @@ void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev) ...@@ -606,18 +732,19 @@ void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev)
* adjusts the final list buffer state for a partial write. * adjusts the final list buffer state for a partial write.
*/ */
static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev)
{ {
int result = 0; int result = 0;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
unsigned long flags; unsigned long flags;
struct list_buffer *lb, *n; struct list_buffer *lb, *n;
unsigned long bytes_total = 0; unsigned long bytes_total = 0;
dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
spin_lock_irqsave(&dev->priv->tx_list.lock, flags); spin_lock_irqsave(&priv->tx_list.lock, flags);
list_for_each_entry_safe(lb, n, &dev->priv->tx_list.head, link) { list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) {
unsigned long bytes_written; unsigned long bytes_written;
...@@ -651,7 +778,7 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) ...@@ -651,7 +778,7 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev)
ps3_vuart_disable_interrupt_tx(dev); ps3_vuart_disable_interrupt_tx(dev);
port_full: port_full:
spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); spin_unlock_irqrestore(&priv->tx_list.lock, flags);
dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n",
__func__, __LINE__, bytes_total); __func__, __LINE__, bytes_total);
return result; return result;
...@@ -665,60 +792,37 @@ port_full: ...@@ -665,60 +792,37 @@ port_full:
* buffer list. Buffer list data is dequeued via ps3_vuart_read. * buffer list. Buffer list data is dequeued via ps3_vuart_read.
*/ */
static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) static int ps3_vuart_handle_interrupt_rx(struct ps3_system_bus_device *dev)
{ {
static unsigned long dbg_number; int result;
int result = 0; struct ps3_vuart_port_priv *priv = to_port_priv(dev);
unsigned long flags; unsigned long flags;
struct list_buffer *lb; u64 bytes;
unsigned long bytes;
dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); spin_lock_irqsave(&priv->rx_list.lock, flags);
result = ps3_vuart_queue_rx_bytes(dev, &bytes);
if (result)
return -EIO;
BUG_ON(!bytes);
/* Add some extra space for recently arrived data. */
bytes += 128;
lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC);
if (!lb) if (result) {
return -ENOMEM; spin_unlock_irqrestore(&priv->rx_list.lock, flags);
return result;
ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); }
lb->head = lb->data;
lb->tail = lb->data + bytes;
lb->dbg_number = ++dbg_number;
spin_lock_irqsave(&dev->priv->rx_list.lock, flags);
list_add_tail(&lb->link, &dev->priv->rx_list.head);
dev->priv->rx_list.bytes_held += bytes;
spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags);
dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n",
__func__, __LINE__, lb->dbg_number, bytes);
spin_lock_irqsave(&dev->priv->work.lock, flags); if (priv->rx_list.work.trigger && priv->rx_list.bytes_held
if(dev->priv->work.trigger >= priv->rx_list.work.trigger) {
&& dev->priv->rx_list.bytes_held >= dev->priv->work.trigger) {
dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n", dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n",
__func__, __LINE__, dev->priv->work.trigger); __func__, __LINE__, priv->rx_list.work.trigger);
dev->priv->work.trigger = 0; priv->rx_list.work.trigger = 0;
schedule_work(&dev->priv->work.work); schedule_work(&priv->rx_list.work.work);
} }
spin_unlock_irqrestore(&dev->priv->work.lock, flags);
return 0; spin_unlock_irqrestore(&priv->rx_list.lock, flags);
return result;
} }
static int ps3_vuart_handle_interrupt_disconnect( static int ps3_vuart_handle_interrupt_disconnect(
struct ps3_vuart_port_device *dev) struct ps3_system_bus_device *dev)
{ {
dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
BUG_ON("no support"); BUG_ON("no support");
...@@ -733,9 +837,10 @@ static int ps3_vuart_handle_interrupt_disconnect( ...@@ -733,9 +837,10 @@ static int ps3_vuart_handle_interrupt_disconnect(
* stage handler after one iteration. * stage handler after one iteration.
*/ */
static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev)
{ {
int result; int result;
struct ps3_vuart_port_priv *priv = to_port_priv(dev);
unsigned long status; unsigned long status;
result = ps3_vuart_get_interrupt_status(dev, &status); result = ps3_vuart_get_interrupt_status(dev, &status);
...@@ -747,21 +852,21 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) ...@@ -747,21 +852,21 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev)
status); status);
if (status & INTERRUPT_MASK_DISCONNECT) { if (status & INTERRUPT_MASK_DISCONNECT) {
dev->priv->stats.disconnect_interrupts++; priv->stats.disconnect_interrupts++;
result = ps3_vuart_handle_interrupt_disconnect(dev); result = ps3_vuart_handle_interrupt_disconnect(dev);
if (result) if (result)
ps3_vuart_disable_interrupt_disconnect(dev); ps3_vuart_disable_interrupt_disconnect(dev);
} }
if (status & INTERRUPT_MASK_TX) { if (status & INTERRUPT_MASK_TX) {
dev->priv->stats.tx_interrupts++; priv->stats.tx_interrupts++;
result = ps3_vuart_handle_interrupt_tx(dev); result = ps3_vuart_handle_interrupt_tx(dev);
if (result) if (result)
ps3_vuart_disable_interrupt_tx(dev); ps3_vuart_disable_interrupt_tx(dev);
} }
if (status & INTERRUPT_MASK_RX) { if (status & INTERRUPT_MASK_RX) {
dev->priv->stats.rx_interrupts++; priv->stats.rx_interrupts++;
result = ps3_vuart_handle_interrupt_rx(dev); result = ps3_vuart_handle_interrupt_rx(dev);
if (result) if (result)
ps3_vuart_disable_interrupt_rx(dev); ps3_vuart_disable_interrupt_rx(dev);
...@@ -771,11 +876,11 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) ...@@ -771,11 +876,11 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev)
} }
struct vuart_bus_priv { struct vuart_bus_priv {
const struct ports_bmp bmp; struct ports_bmp *bmp;
unsigned int virq; unsigned int virq;
struct semaphore probe_mutex; struct semaphore probe_mutex;
int use_count; int use_count;
struct ps3_vuart_port_device *devices[PORT_COUNT]; struct ps3_system_bus_device *devices[PORT_COUNT];
} static vuart_bus_priv; } static vuart_bus_priv;
/** /**
...@@ -788,17 +893,16 @@ struct vuart_bus_priv { ...@@ -788,17 +893,16 @@ struct vuart_bus_priv {
static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private)
{ {
struct vuart_bus_priv *bus_priv; struct vuart_bus_priv *bus_priv = _private;
BUG_ON(!_private); BUG_ON(!bus_priv);
bus_priv = (struct vuart_bus_priv *)_private;
while (1) { while (1) {
unsigned int port; unsigned int port;
dump_ports_bmp(&bus_priv->bmp); dump_ports_bmp(bus_priv->bmp);
port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp.status); port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp->status);
if (port == BITS_PER_LONG) if (port == BITS_PER_LONG)
break; break;
...@@ -812,100 +916,144 @@ static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) ...@@ -812,100 +916,144 @@ static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int ps3_vuart_match(struct device *_dev, struct device_driver *_drv) static int ps3_vuart_bus_interrupt_get(void)
{ {
int result; int result;
struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_drv);
struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev);
result = dev->match_id == drv->match_id; pr_debug(" -> %s:%d\n", __func__, __LINE__);
vuart_bus_priv.use_count++;
BUG_ON(vuart_bus_priv.use_count > 2);
if (vuart_bus_priv.use_count != 1) {
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);
dev_info(&dev->core, "%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, if (result) {
__LINE__, dev->match_id, dev->core.bus_id, drv->match_id, pr_debug("%s:%d: request_irq failed (%d)\n",
drv->core.name, (result ? "match" : "miss")); __func__, __LINE__, result);
goto fail_request_irq;
}
pr_debug(" <- %s:%d: ok\n", __func__, __LINE__);
return result; return result;
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;
} }
static int ps3_vuart_probe(struct device *_dev) static int ps3_vuart_probe(struct ps3_system_bus_device *dev)
{ {
int result; int result;
unsigned int port_number; struct ps3_vuart_port_driver *drv;
struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); struct ps3_vuart_port_priv *priv = NULL;
struct ps3_vuart_port_driver *drv =
to_ps3_vuart_port_driver(_dev->driver);
dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
drv = ps3_system_bus_dev_to_vuart_drv(dev);
dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
drv->core.core.name);
BUG_ON(!drv); BUG_ON(!drv);
down(&vuart_bus_priv.probe_mutex); if (dev->port_number >= PORT_COUNT) {
BUG();
return -EINVAL;
}
/* Setup vuart_bus_priv.devices[]. */ down(&vuart_bus_priv.probe_mutex);
result = ps3_vuart_match_id_to_port(dev->match_id, result = ps3_vuart_bus_interrupt_get();
&port_number);
if (result) { if (result)
dev_dbg(&dev->core, "%s:%d: unknown match_id (%d)\n", goto fail_setup_interrupt;
__func__, __LINE__, dev->match_id);
result = -EINVAL;
goto fail_match;
}
if (vuart_bus_priv.devices[port_number]) { if (vuart_bus_priv.devices[dev->port_number]) {
dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__, dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__,
__LINE__, port_number); __LINE__, dev->port_number);
result = -EBUSY; result = -EBUSY;
goto fail_match; goto fail_busy;
} }
vuart_bus_priv.devices[port_number] = dev; vuart_bus_priv.devices[dev->port_number] = dev;
/* Setup dev->priv. */ /* Setup dev->driver_priv. */
dev->priv = kzalloc(sizeof(struct ps3_vuart_port_priv), GFP_KERNEL); dev->driver_priv = kzalloc(sizeof(struct ps3_vuart_port_priv),
GFP_KERNEL);
if (!dev->priv) { if (!dev->driver_priv) {
result = -ENOMEM; result = -ENOMEM;
goto fail_alloc; goto fail_dev_malloc;
} }
dev->priv->port_number = port_number; priv = to_port_priv(dev);
INIT_LIST_HEAD(&dev->priv->tx_list.head);
spin_lock_init(&dev->priv->tx_list.lock);
INIT_LIST_HEAD(&dev->priv->rx_list.head); INIT_LIST_HEAD(&priv->tx_list.head);
spin_lock_init(&dev->priv->rx_list.lock); spin_lock_init(&priv->tx_list.lock);
INIT_WORK(&dev->priv->work.work, NULL); INIT_LIST_HEAD(&priv->rx_list.head);
spin_lock_init(&dev->priv->work.lock); spin_lock_init(&priv->rx_list.lock);
dev->priv->work.trigger = 0;
dev->priv->work.dev = dev;
if (++vuart_bus_priv.use_count == 1) { INIT_WORK(&priv->rx_list.work.work, NULL);
priv->rx_list.work.trigger = 0;
result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, priv->rx_list.work.dev = dev;
(void*)&vuart_bus_priv.bmp.status, &vuart_bus_priv.virq);
if (result) {
dev_dbg(&dev->core,
"%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);
if (result) {
dev_info(&dev->core, "%s:%d: request_irq failed (%d)\n",
__func__, __LINE__, result);
goto fail_request_irq;
}
}
/* clear stale pending interrupts */ /* clear stale pending interrupts */
...@@ -936,150 +1084,158 @@ static int ps3_vuart_probe(struct device *_dev) ...@@ -936,150 +1084,158 @@ static int ps3_vuart_probe(struct device *_dev)
fail_probe: fail_probe:
ps3_vuart_set_interrupt_mask(dev, 0); ps3_vuart_set_interrupt_mask(dev, 0);
fail_request_irq: kfree(dev->driver_priv);
ps3_vuart_irq_destroy(vuart_bus_priv.virq); dev->driver_priv = NULL;
vuart_bus_priv.virq = NO_IRQ; fail_dev_malloc:
fail_alloc_irq: vuart_bus_priv.devices[dev->port_number] = NULL;
--vuart_bus_priv.use_count; fail_busy:
kfree(dev->priv); ps3_vuart_bus_interrupt_put();
dev->priv = NULL; fail_setup_interrupt:
fail_alloc:
vuart_bus_priv.devices[port_number] = NULL;
fail_match:
up(&vuart_bus_priv.probe_mutex); up(&vuart_bus_priv.probe_mutex);
dev_dbg(&dev->core, "%s:%d failed\n", __func__, __LINE__); dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__);
return result; return result;
} }
static int ps3_vuart_remove(struct device *_dev) /**
* 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)
{ {
struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
struct ps3_vuart_port_driver *drv =
to_ps3_vuart_port_driver(_dev->driver); 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);
down(&vuart_bus_priv.probe_mutex); down(&vuart_bus_priv.probe_mutex);
dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,
dev->core.bus_id); dev->match_id);
BUG_ON(vuart_bus_priv.use_count < 1); if (!dev->core.driver) {
dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
__LINE__);
up(&vuart_bus_priv.probe_mutex);
return 0;
}
if (drv->remove) drv = ps3_system_bus_dev_to_vuart_drv(dev);
drv->remove(dev);
else
dev_dbg(&dev->core, "%s:%d: %s no remove method\n", __func__,
__LINE__, dev->core.bus_id);
vuart_bus_priv.devices[dev->priv->port_number] = NULL; BUG_ON(!drv);
if (--vuart_bus_priv.use_count == 0) { if (drv->remove) {
drv->remove(dev);
} else {
dev_dbg(&dev->core, "%s:%d: no remove method\n", __func__,
__LINE__);
BUG(); BUG();
free_irq(vuart_bus_priv.virq, &vuart_bus_priv);
ps3_vuart_irq_destroy(vuart_bus_priv.virq);
vuart_bus_priv.virq = NO_IRQ;
} }
kfree(dev->priv); ps3_vuart_cleanup(dev);
dev->priv = NULL;
vuart_bus_priv.devices[dev->port_number] = NULL;
kfree(priv);
priv = NULL;
dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
up(&vuart_bus_priv.probe_mutex); up(&vuart_bus_priv.probe_mutex);
return 0; return 0;
} }
static void ps3_vuart_shutdown(struct device *_dev)
{
struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev);
struct ps3_vuart_port_driver *drv =
to_ps3_vuart_port_driver(_dev->driver);
dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__,
dev->core.bus_id);
if (drv->shutdown)
drv->shutdown(dev);
else
dev_dbg(&dev->core, "%s:%d: %s no shutdown method\n", __func__,
__LINE__, dev->core.bus_id);
}
/** /**
* ps3_vuart_bus - The vuart bus instance. * ps3_vuart_shutdown - Cleans interrupts and HV resources.
* @dev: The struct ps3_system_bus_device instance.
* *
* The vuart is managed as a bus that port devices connect to. * 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.
*/ */
struct bus_type ps3_vuart_bus = { static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev)
.name = "ps3_vuart",
.match = ps3_vuart_match,
.probe = ps3_vuart_probe,
.remove = ps3_vuart_remove,
.shutdown = ps3_vuart_shutdown,
};
int __init ps3_vuart_bus_init(void)
{ {
int result; struct ps3_vuart_port_driver *drv;
pr_debug("%s:%d:\n", __func__, __LINE__); BUG_ON(!dev);
if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) down(&vuart_bus_priv.probe_mutex);
return -ENODEV;
init_MUTEX(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,
result = bus_register(&ps3_vuart_bus); dev->match_id);
BUG_ON(result);
return result; if (!dev->core.driver) {
} dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
__LINE__);
up(&vuart_bus_priv.probe_mutex);
return 0;
}
void __exit ps3_vuart_bus_exit(void) drv = ps3_system_bus_dev_to_vuart_drv(dev);
{
pr_debug("%s:%d:\n", __func__, __LINE__);
bus_unregister(&ps3_vuart_bus);
}
core_initcall(ps3_vuart_bus_init); BUG_ON(!drv);
module_exit(ps3_vuart_bus_exit);
/** if (drv->shutdown)
* ps3_vuart_port_release_device - Remove a vuart port device. 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();
}
static void ps3_vuart_port_release_device(struct device *_dev) ps3_vuart_cleanup(dev);
{
#if defined(DEBUG)
struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev);
dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
BUG_ON(dev->priv && "forgot to free"); up(&vuart_bus_priv.probe_mutex);
memset(&dev->core, 0, sizeof(dev->core)); return 0;
#endif
} }
/** static int __init ps3_vuart_bus_init(void)
* ps3_vuart_port_device_register - Add a vuart port device.
*/
int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev)
{ {
static unsigned int dev_count = 1; pr_debug("%s:%d:\n", __func__, __LINE__);
BUG_ON(dev->priv && "forgot to free");
dev->core.parent = NULL; if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
dev->core.bus = &ps3_vuart_bus; return -ENODEV;
dev->core.release = ps3_vuart_port_release_device;
snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x", init_MUTEX(&vuart_bus_priv.probe_mutex);
dev_count++);
dev_dbg(&dev->core, "%s:%d register\n", __func__, __LINE__); return 0;
}
return device_register(&dev->core); static void __exit ps3_vuart_bus_exit(void)
{
pr_debug("%s:%d:\n", __func__, __LINE__);
} }
EXPORT_SYMBOL_GPL(ps3_vuart_port_device_register); core_initcall(ps3_vuart_bus_init);
module_exit(ps3_vuart_bus_exit);
/** /**
* ps3_vuart_port_driver_register - Add a vuart port device driver. * ps3_vuart_port_driver_register - Add a vuart port device driver.
...@@ -1089,12 +1245,18 @@ int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv) ...@@ -1089,12 +1245,18 @@ int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv)
{ {
int result; int result;
pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name);
drv->core.bus = &ps3_vuart_bus;
result = driver_register(&drv->core); 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);
return result; return result;
} }
EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register);
/** /**
...@@ -1103,8 +1265,7 @@ EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); ...@@ -1103,8 +1265,7 @@ EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register);
void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv) void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv)
{ {
pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name);
driver_unregister(&drv->core); ps3_system_bus_driver_unregister(&drv->core);
} }
EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister); EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister);
...@@ -34,29 +34,7 @@ struct ps3_vuart_stats { ...@@ -34,29 +34,7 @@ struct ps3_vuart_stats {
struct ps3_vuart_work { struct ps3_vuart_work {
struct work_struct work; struct work_struct work;
unsigned long trigger; unsigned long trigger;
spinlock_t lock; struct ps3_system_bus_device *dev; /* to convert work to device */
struct ps3_vuart_port_device* dev; /* to convert work to device */
};
/**
* struct ps3_vuart_port_priv - private vuart device data.
*/
struct ps3_vuart_port_priv {
unsigned int port_number;
u64 interrupt_mask;
struct {
spinlock_t lock;
struct list_head head;
} tx_list;
struct {
unsigned long bytes_held;
spinlock_t lock;
struct list_head head;
} rx_list;
struct ps3_vuart_stats stats;
struct ps3_vuart_work work;
}; };
/** /**
...@@ -64,32 +42,30 @@ struct ps3_vuart_port_priv { ...@@ -64,32 +42,30 @@ struct ps3_vuart_port_priv {
*/ */
struct ps3_vuart_port_driver { struct ps3_vuart_port_driver {
enum ps3_match_id match_id; struct ps3_system_bus_driver core;
struct device_driver core; int (*probe)(struct ps3_system_bus_device *);
int (*probe)(struct ps3_vuart_port_device *); int (*remove)(struct ps3_system_bus_device *);
int (*remove)(struct ps3_vuart_port_device *); void (*shutdown)(struct ps3_system_bus_device *);
void (*shutdown)(struct ps3_vuart_port_device *); void (*work)(struct ps3_system_bus_device *);
int (*tx_event)(struct ps3_vuart_port_device *dev); /* int (*tx_event)(struct ps3_system_bus_device *dev); */
int (*rx_event)(struct ps3_vuart_port_device *dev); /* int (*rx_event)(struct ps3_system_bus_device *dev); */
int (*disconnect_event)(struct ps3_vuart_port_device *dev); /* int (*disconnect_event)(struct ps3_system_bus_device *dev); */
/* int (*suspend)(struct ps3_vuart_port_device *, pm_message_t); */ /* int (*suspend)(struct ps3_system_bus_device *, pm_message_t); */
/* int (*resume)(struct ps3_vuart_port_device *); */ /* int (*resume)(struct ps3_system_bus_device *); */
}; };
int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv); int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv);
void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv); void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv);
static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver( static inline struct ps3_vuart_port_driver *
struct device_driver *_drv) ps3_system_bus_dev_to_vuart_drv(struct ps3_system_bus_device *_dev)
{
return container_of(_drv, struct ps3_vuart_port_driver, core);
}
static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device(
struct device *_dev)
{ {
return container_of(_dev, struct ps3_vuart_port_device, core); struct ps3_system_bus_driver *sbd =
ps3_system_bus_dev_to_system_bus_drv(_dev);
BUG_ON(!sbd);
return container_of(sbd, struct ps3_vuart_port_driver, core);
} }
static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device( static inline struct ps3_system_bus_device *ps3_vuart_work_to_system_bus_dev(
struct work_struct *_work) struct work_struct *_work)
{ {
struct ps3_vuart_work *vw = container_of(_work, struct ps3_vuart_work, struct ps3_vuart_work *vw = container_of(_work, struct ps3_vuart_work,
...@@ -97,14 +73,13 @@ static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device( ...@@ -97,14 +73,13 @@ static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device(
return vw->dev; return vw->dev;
} }
int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf,
unsigned int bytes);
int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
unsigned int bytes); unsigned int bytes);
int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf,
unsigned int bytes); unsigned int bytes);
void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev); int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes);
void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev);
void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev,
unsigned int bytes); unsigned int bytes);
#endif #endif
...@@ -407,23 +407,6 @@ static inline void *ps3_system_bus_get_driver_data( ...@@ -407,23 +407,6 @@ static inline void *ps3_system_bus_get_driver_data(
extern struct bus_type ps3_system_bus_type; extern struct bus_type ps3_system_bus_type;
/* vuart routines */
struct ps3_vuart_port_priv;
/**
* struct ps3_vuart_port_device - a device on a vuart port
*/
struct ps3_vuart_port_device {
enum ps3_match_id match_id;
struct device core;
struct ps3_vuart_port_priv* priv; /* private driver variables */
};
int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev);
/* system manager */ /* system manager */
#ifdef CONFIG_PS3_SYS_MANAGER #ifdef CONFIG_PS3_SYS_MANAGER
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment