Commit ea1547d3 authored by Geoff Levand's avatar Geoff Levand Committed by Paul Mackerras

[POWERPC] PS3: Vuart add async read

Add asynchronous read support to the PS3 vuart driver.  This is needed to
support the PS3 system manager driver.
Signed-off-by: default avatarGeoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 75c86e74
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <asm/ps3.h> #include <asm/ps3.h>
#include <asm/firmware.h> #include <asm/firmware.h>
...@@ -567,6 +568,44 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, ...@@ -567,6 +568,44 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
return 0; return 0;
} }
int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func,
unsigned int bytes)
{
unsigned long flags;
if(dev->priv->work.trigger) {
dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n",
__func__, __LINE__);
return -EAGAIN;
}
BUG_ON(!bytes);
PREPARE_WORK(&dev->priv->work.work, func);
spin_lock_irqsave(&dev->priv->work.lock, flags);
if(dev->priv->rx_list.bytes_held >= bytes) {
dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n",
__func__, __LINE__, bytes);
schedule_work(&dev->priv->work.work);
spin_unlock_irqrestore(&dev->priv->work.lock, flags);
return 0;
}
dev->priv->work.trigger = bytes;
spin_unlock_irqrestore(&dev->priv->work.lock, flags);
dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__,
__LINE__, bytes, bytes);
return 0;
}
void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev)
{
dev->priv->work.trigger = 0;
}
/** /**
* ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler
* *
...@@ -674,6 +713,15 @@ static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) ...@@ -674,6 +713,15 @@ static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev)
dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n",
__func__, __LINE__, lb->dbg_number, bytes); __func__, __LINE__, lb->dbg_number, bytes);
spin_lock_irqsave(&dev->priv->work.lock, flags);
if(dev->priv->work.trigger
&& dev->priv->rx_list.bytes_held >= dev->priv->work.trigger) {
dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n",
__func__, __LINE__, dev->priv->work.trigger);
dev->priv->work.trigger = 0;
schedule_work(&dev->priv->work.work);
}
spin_unlock_irqrestore(&dev->priv->work.lock, flags);
return 0; return 0;
} }
...@@ -839,6 +887,11 @@ static int ps3_vuart_probe(struct device *_dev) ...@@ -839,6 +887,11 @@ static int ps3_vuart_probe(struct device *_dev)
INIT_LIST_HEAD(&dev->priv->rx_list.head); INIT_LIST_HEAD(&dev->priv->rx_list.head);
spin_lock_init(&dev->priv->rx_list.lock); spin_lock_init(&dev->priv->rx_list.lock);
INIT_WORK(&dev->priv->work.work, NULL);
spin_lock_init(&dev->priv->work.lock);
dev->priv->work.trigger = 0;
dev->priv->work.dev = dev;
if (++vuart_bus_priv.use_count == 1) { if (++vuart_bus_priv.use_count == 1) {
result = ps3_alloc_vuart_irq(PS3_BINDING_CPU_ANY, result = ps3_alloc_vuart_irq(PS3_BINDING_CPU_ANY,
......
...@@ -31,6 +31,13 @@ struct ps3_vuart_stats { ...@@ -31,6 +31,13 @@ struct ps3_vuart_stats {
unsigned long disconnect_interrupts; unsigned long disconnect_interrupts;
}; };
struct ps3_vuart_work {
struct work_struct work;
unsigned long trigger;
spinlock_t lock;
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 - private vuart device data.
*/ */
...@@ -49,6 +56,7 @@ struct ps3_vuart_port_priv { ...@@ -49,6 +56,7 @@ struct ps3_vuart_port_priv {
struct list_head head; struct list_head head;
} rx_list; } rx_list;
struct ps3_vuart_stats stats; struct ps3_vuart_stats stats;
struct ps3_vuart_work work;
}; };
/** /**
...@@ -71,10 +79,6 @@ struct ps3_vuart_port_driver { ...@@ -71,10 +79,6 @@ struct ps3_vuart_port_driver {
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);
int ps3_vuart_write(struct ps3_vuart_port_device *dev,
const void* buf, unsigned int bytes);
int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
unsigned int bytes);
static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver( static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver(
struct device_driver *_drv) struct device_driver *_drv)
{ {
...@@ -85,5 +89,22 @@ static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device( ...@@ -85,5 +89,22 @@ static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device(
{ {
return container_of(_dev, struct ps3_vuart_port_device, core); return container_of(_dev, struct ps3_vuart_port_device, core);
} }
static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device(
struct work_struct *_work)
{
struct ps3_vuart_work *vw = container_of(_work, struct ps3_vuart_work,
work);
return vw->dev;
}
int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf,
unsigned int bytes);
int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
unsigned int bytes);
int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func,
unsigned int bytes);
void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev);
void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev,
unsigned int bytes);
#endif #endif
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