diff options
Diffstat (limited to 'drivers/ps3/vuart.c')
-rw-r--r-- | drivers/ps3/vuart.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c index 90b3d1ca5172..746298107d6f 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/vuart.c @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/workqueue.h> #include <asm/ps3.h> #include <asm/firmware.h> @@ -567,6 +568,44 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, 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 * @@ -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", __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; } @@ -839,6 +887,11 @@ static int ps3_vuart_probe(struct device *_dev) INIT_LIST_HEAD(&dev->priv->rx_list.head); 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) { result = ps3_alloc_vuart_irq(PS3_BINDING_CPU_ANY, |