Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[pandora-kernel.git] / drivers / tty / hvc / hvc_vio.c
index ade73fa..130aace 100644 (file)
@@ -71,41 +71,53 @@ struct hvterm_priv {
        u32                     termno; /* HV term number */
        hv_protocol_t           proto;  /* Raw data or HVSI packets */
        struct hvsi_priv        hvsi;   /* HVSI specific data */
+       spinlock_t              buf_lock;
+       char                    buf[SIZE_VIO_GET_CHARS];
+       int                     left;
+       int                     offset;
 };
 static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES];
-
 /* For early boot console */
 static struct hvterm_priv hvterm_priv0;
 
 static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count)
 {
        struct hvterm_priv *pv = hvterm_privs[vtermno];
-       unsigned long got, i;
+       unsigned long i;
+       unsigned long flags;
+       int got;
 
        if (WARN_ON(!pv))
                return 0;
 
-       /*
-        * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion
-        * so we play safe and avoid the situation where got > count which could
-        * overload the flip buffer.
-        */
-       if (count < SIZE_VIO_GET_CHARS)
-               return -EAGAIN;
-
-       got = hvc_get_chars(pv->termno, buf, count);
-
-       /*
-        * Work around a HV bug where it gives us a null
-        * after every \r.  -- paulus
-        */
-       for (i = 1; i < got; ++i) {
-               if (buf[i] == 0 && buf[i-1] == '\r') {
-                       --got;
-                       if (i < got)
-                               memmove(&buf[i], &buf[i+1], got - i);
+       spin_lock_irqsave(&pv->buf_lock, flags);
+
+       if (pv->left == 0) {
+               pv->offset = 0;
+               pv->left = hvc_get_chars(pv->termno, pv->buf, count);
+
+               /*
+                * Work around a HV bug where it gives us a null
+                * after every \r.  -- paulus
+                */
+               for (i = 1; i < pv->left; ++i) {
+                       if (pv->buf[i] == 0 && pv->buf[i-1] == '\r') {
+                               --pv->left;
+                               if (i < pv->left) {
+                                       memmove(&pv->buf[i], &pv->buf[i+1],
+                                               pv->left - i);
+                               }
+                       }
                }
        }
+
+       got = min(count, pv->left);
+       memcpy(buf, &pv->buf[pv->offset], got);
+       pv->offset += got;
+       pv->left -= got;
+
+       spin_unlock_irqrestore(&pv->buf_lock, flags);
+
        return got;
 }
 
@@ -134,7 +146,7 @@ static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
        if (WARN_ON(!pv))
                return 0;
 
-       return hvsi_get_chars(&pv->hvsi, buf, count);
+       return hvsilib_get_chars(&pv->hvsi, buf, count);
 }
 
 static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
@@ -144,7 +156,7 @@ static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
        if (WARN_ON(!pv))
                return 0;
 
-       return hvsi_put_chars(&pv->hvsi, buf, count);
+       return hvsilib_put_chars(&pv->hvsi, buf, count);
 }
 
 static int hvterm_hvsi_open(struct hvc_struct *hp, int data)
@@ -158,7 +170,7 @@ static int hvterm_hvsi_open(struct hvc_struct *hp, int data)
        if (rc)
                return rc;
 
-       return hvsi_open(&pv->hvsi, hp);
+       return hvsilib_open(&pv->hvsi, hp);
 }
 
 static void hvterm_hvsi_close(struct hvc_struct *hp, int data)
@@ -167,7 +179,7 @@ static void hvterm_hvsi_close(struct hvc_struct *hp, int data)
 
        pr_devel("HVSI@%x: do close !\n", pv->termno);
 
-       hvsi_close(&pv->hvsi, hp);
+       hvsilib_close(&pv->hvsi, hp);
 
        notifier_del_irq(hp, data);
 }
@@ -178,7 +190,7 @@ void hvterm_hvsi_hangup(struct hvc_struct *hp, int data)
 
        pr_devel("HVSI@%x: do hangup !\n", pv->termno);
 
-       hvsi_close(&pv->hvsi, hp);
+       hvsilib_close(&pv->hvsi, hp);
 
        notifier_hangup_irq(hp, data);
 }
@@ -201,9 +213,9 @@ static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
                 pv->termno, set, clear);
 
        if (set & TIOCM_DTR)
-               hvsi_write_mctrl(&pv->hvsi, 1);
+               hvsilib_write_mctrl(&pv->hvsi, 1);
        else if (clear & TIOCM_DTR)
-               hvsi_write_mctrl(&pv->hvsi, 0);
+               hvsilib_write_mctrl(&pv->hvsi, 0);
 
        return 0;
 }
@@ -266,9 +278,10 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev,
                        return -ENOMEM;
                pv->termno = vdev->unit_address;
                pv->proto = proto;
+               spin_lock_init(&pv->buf_lock);
                hvterm_privs[termno] = pv;
-               hvsi_init(&pv->hvsi, hvc_get_chars, hvc_put_chars,
-                         pv->termno, 0);
+               hvsilib_init(&pv->hvsi, hvc_get_chars, hvc_put_chars,
+                            pv->termno, 0);
        }
 
        hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS);
@@ -406,6 +419,7 @@ void __init hvc_vio_init_early(void)
        if (termno == NULL)
                goto out;
        hvterm_priv0.termno = *termno;
+       spin_lock_init(&hvterm_priv0.buf_lock);
        hvterm_privs[0] = &hvterm_priv0;
 
        /* Check the protocol */
@@ -416,10 +430,10 @@ void __init hvc_vio_init_early(void)
        else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) {
                hvterm_priv0.proto = HV_PROTOCOL_HVSI;
                ops = &hvterm_hvsi_ops;
-               hvsi_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars,
-                         hvterm_priv0.termno, 1);
+               hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars,
+                            hvterm_priv0.termno, 1);
                /* HVSI, perform the handshake now */
-               hvsi_establish(&hvterm_priv0.hvsi);
+               hvsilib_establish(&hvterm_priv0.hvsi);
        } else
                goto out;
        udbg_putc = udbg_hvc_putc;
@@ -447,6 +461,7 @@ void __init udbg_init_debug_lpar(void)
        hvterm_privs[0] = &hvterm_priv0;
        hvterm_priv0.termno = 0;
        hvterm_priv0.proto = HV_PROTOCOL_RAW;
+       spin_lock_init(&hvterm_priv0.buf_lock);
        udbg_putc = udbg_hvc_putc;
        udbg_getc = udbg_hvc_getc;
        udbg_getc_poll = udbg_hvc_getc_poll;
@@ -459,11 +474,12 @@ void __init udbg_init_debug_lpar_hvsi(void)
        hvterm_privs[0] = &hvterm_priv0;
        hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO;
        hvterm_priv0.proto = HV_PROTOCOL_HVSI;
+       spin_lock_init(&hvterm_priv0.buf_lock);
        udbg_putc = udbg_hvc_putc;
        udbg_getc = udbg_hvc_getc;
        udbg_getc_poll = udbg_hvc_getc_poll;
-       hvsi_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars,
-                 hvterm_priv0.termno, 1);
-       hvsi_establish(&hvterm_priv0.hvsi);
+       hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars,
+                    hvterm_priv0.termno, 1);
+       hvsilib_establish(&hvterm_priv0.hvsi);
 }
 #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */