mmc-omap: add support for 16-bit and 32-bit registers
[safe/jmp/linux-2.6] / drivers / char / nozomi.c
index d6102b6..a663800 100644 (file)
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
+#include <linux/sched.h>
 #include <linux/serial.h>
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
 #include <linux/init.h>
 #include <linux/kfifo.h>
 #include <linux/uaccess.h>
+#include <linux/slab.h>
 #include <asm/byteorder.h>
 
 #include <linux/delay.h>
@@ -135,10 +137,6 @@ static int debug;
 #define RECEIVE_BUF_MAX                4
 
 
-/* Define all types of vendors and devices to support */
-#define VENDOR1                0x1931  /* Vendor Option */
-#define DEVICE1                0x000c  /* HSDPA card */
-
 #define R_IIR          0x0000  /* Interrupt Identity Register */
 #define R_FCR          0x0000  /* Flow Control Register */
 #define R_IER          0x0004  /* Interrupt Enable Register */
@@ -357,7 +355,7 @@ struct port {
        u8 update_flow_control;
        struct ctrl_ul ctrl_ul;
        struct ctrl_dl ctrl_dl;
-       struct kfifo *fifo_ul;
+       struct kfifo fifo_ul;
        void __iomem *dl_addr[2];
        u32 dl_size[2];
        u8 toggle_dl;
@@ -370,6 +368,8 @@ struct port {
        struct mutex tty_sem;
        wait_queue_head_t tty_wait;
        struct async_icount tty_icount;
+
+       struct nozomi *dc;
 };
 
 /* Private data one for each card in the system */
@@ -404,7 +404,7 @@ struct buffer {
 
 /*    Global variables */
 static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = {
-       {PCI_DEVICE(VENDOR1, DEVICE1)},
+       {PCI_DEVICE(0x1931, 0x000c)},   /* Nozomi HSDPA */
        {},
 };
 
@@ -413,6 +413,8 @@ MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl);
 static struct nozomi *ndevs[NOZOMI_MAX_CARDS];
 static struct tty_driver *ntty_driver;
 
+static const struct tty_port_operations noz_tty_port_ops;
+
 /*
  * find card by tty_index
  */
@@ -684,8 +686,6 @@ static int nozomi_read_config_table(struct nozomi *dc)
                dump_table(dc);
 
                for (i = PORT_MDM; i < MAX_PORT; i++) {
-                       dc->port[i].fifo_ul =
-                           kfifo_alloc(FIFO_BUFFER_SIZE_UL, GFP_ATOMIC, NULL);
                        memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl));
                        memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul));
                }
@@ -797,7 +797,7 @@ static int send_data(enum port_type index, struct nozomi *dc)
        struct tty_struct *tty = tty_port_tty_get(&port->port);
 
        /* Get data from tty and place in buf for now */
-       size = __kfifo_get(port->fifo_ul, dc->send_buf,
+       size = kfifo_out(&port->fifo_ul, dc->send_buf,
                           ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX);
 
        if (size == 0) {
@@ -828,7 +828,7 @@ static int receive_data(enum port_type index, struct nozomi *dc)
        struct port *port = &dc->port[index];
        void __iomem *addr = port->dl_addr[port->toggle_dl];
        struct tty_struct *tty = tty_port_tty_get(&port->port);
-       int i;
+       int i, ret;
 
        if (unlikely(!tty)) {
                DBG1("tty not open for port: %d?", index);
@@ -844,16 +844,16 @@ static int receive_data(enum port_type index, struct nozomi *dc)
 
                /* disable interrupt in downlink... */
                disable_transmit_dl(index, dc);
-               return 0;
+               ret = 0;
+               goto put;
        }
 
        if (unlikely(size == 0)) {
                dev_err(&dc->pdev->dev, "size == 0?\n");
-               return 1;
+               ret = 1;
+               goto put;
        }
 
-       tty_buffer_request_room(tty, size);
-
        while (size > 0) {
                read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX);
 
@@ -871,8 +871,10 @@ static int receive_data(enum port_type index, struct nozomi *dc)
        }
 
        set_bit(index, &dc->flip);
+       ret = 1;
+put:
        tty_kref_put(tty);
-       return 1;
+       return ret;
 }
 
 /* Debug for interrupts */
@@ -983,11 +985,11 @@ static int receive_flow_control(struct nozomi *dc)
 
        } else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) {
 
-               if (__kfifo_len(dc->port[port].fifo_ul)) {
+               if (kfifo_len(&dc->port[port].fifo_ul)) {
                        DBG1("Enable interrupt (0x%04X) on port: %d",
                                enable_ier, port);
                        DBG1("Data in buffer [%d], enable transmit! ",
-                               __kfifo_len(dc->port[port].fifo_ul));
+                               kfifo_len(&dc->port[port].fifo_ul));
                        enable_transmit_ul(port, dc);
                } else {
                        DBG1("No data in buffer...");
@@ -1428,6 +1430,16 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
                goto err_free_sbuf;
        }
 
+       for (i = PORT_MDM; i < MAX_PORT; i++) {
+               if (kfifo_alloc(&dc->port[i].fifo_ul,
+                     FIFO_BUFFER_SIZE_UL, GFP_ATOMIC)) {
+                       dev_err(&pdev->dev,
+                                       "Could not allocate kfifo buffer\n");
+                       ret = -ENOMEM;
+                       goto err_free_kfifo;
+               }
+       }
+
        spin_lock_init(&dc->spin_mutex);
 
        nozomi_setup_private_data(dc);
@@ -1440,7 +1452,7 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
                        NOZOMI_NAME, dc);
        if (unlikely(ret)) {
                dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq);
-               goto err_free_sbuf;
+               goto err_free_kfifo;
        }
 
        DBG1("base_addr: %p", dc->base_addr);
@@ -1459,13 +1471,30 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
        dc->state = NOZOMI_STATE_ENABLED;
 
        for (i = 0; i < MAX_PORT; i++) {
-               mutex_init(&dc->port[i].tty_sem);
-               tty_port_init(&dc->port[i].port);
-               tty_register_device(ntty_driver, dc->index_start + i,
+               struct device *tty_dev;
+               struct port *port = &dc->port[i];
+               port->dc = dc;
+               mutex_init(&port->tty_sem);
+               tty_port_init(&port->port);
+               port->port.ops = &noz_tty_port_ops;
+               tty_dev = tty_register_device(ntty_driver, dc->index_start + i,
                                                        &pdev->dev);
+
+               if (IS_ERR(tty_dev)) {
+                       ret = PTR_ERR(tty_dev);
+                       dev_err(&pdev->dev, "Could not allocate tty?\n");
+                       goto err_free_tty;
+               }
        }
+
        return 0;
 
+err_free_tty:
+       for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
+               tty_unregister_device(ntty_driver, i);
+err_free_kfifo:
+       for (i = 0; i < MAX_PORT; i++)
+               kfifo_free(&dc->port[i].fifo_ul);
 err_free_sbuf:
        kfree(dc->send_buf);
        iounmap(dc->base_addr);
@@ -1531,8 +1560,7 @@ static void __devexit nozomi_card_exit(struct pci_dev *pdev)
        free_irq(pdev->irq, dc);
 
        for (i = 0; i < MAX_PORT; i++)
-               if (dc->port[i].fifo_ul)
-                       kfifo_free(dc->port[i].fifo_ul);
+               kfifo_free(&dc->port[i].fifo_ul);
 
        kfree(dc->send_buf);
 
@@ -1573,69 +1601,74 @@ static void set_dtr(const struct tty_struct *tty, int dtr)
  * ----------------------------------------------------------------------------
  */
 
-/* Called when the userspace process opens the tty, /dev/noz*.  */
-static int ntty_open(struct tty_struct *tty, struct file *file)
+static int ntty_install(struct tty_driver *driver, struct tty_struct *tty)
 {
        struct port *port = get_port_by_tty(tty);
        struct nozomi *dc = get_dc_by_tty(tty);
-       unsigned long flags;
-
+       int ret;
        if (!port || !dc || dc->state != NOZOMI_STATE_READY)
                return -ENODEV;
-
-       if (mutex_lock_interruptible(&port->tty_sem))
-               return -ERESTARTSYS;
-
-       port->port.count++;
-       dc->open_ttys++;
-
-       /* Enable interrupt downlink for channel */
-       if (port->port.count == 1) {
-               /* FIXME: is this needed now ? */
-               tty->low_latency = 1;
-               tty->driver_data = port;
-               tty_port_tty_set(&port->port, tty);
-               DBG1("open: %d", port->token_dl);
-               spin_lock_irqsave(&dc->spin_mutex, flags);
-               dc->last_ier = dc->last_ier | port->token_dl;
-               writew(dc->last_ier, dc->reg_ier);
-               spin_unlock_irqrestore(&dc->spin_mutex, flags);
+       ret = tty_init_termios(tty);
+       if (ret == 0) {
+               tty_driver_kref_get(driver);
+               driver->ttys[tty->index] = tty;
        }
-       mutex_unlock(&port->tty_sem);
-       return 0;
+       return ret;
 }
 
-/* Called when the userspace process close the tty, /dev/noz*. Also
-   called immediately if ntty_open fails in which case tty->driver_data
-   will be NULL an we exit by the first return */
+static void ntty_cleanup(struct tty_struct *tty)
+{
+       tty->driver_data = NULL;
+}
 
-static void ntty_close(struct tty_struct *tty, struct file *file)
+static int ntty_activate(struct tty_port *tport, struct tty_struct *tty)
 {
-       struct nozomi *dc = get_dc_by_tty(tty);
-       struct port *nport = tty->driver_data;
-       struct tty_port *port = &nport->port;
+       struct port *port = container_of(tport, struct port, port);
+       struct nozomi *dc = port->dc;
        unsigned long flags;
 
-       if (!dc || !nport)
-               return;
+       DBG1("open: %d", port->token_dl);
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       dc->last_ier = dc->last_ier | port->token_dl;
+       writew(dc->last_ier, dc->reg_ier);
+       dc->open_ttys++;
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+       printk("noz: activated %d: %p\n", tty->index, tport);
+       return 0;
+}
 
-       /* Users cannot interrupt a close */
-       mutex_lock(&nport->tty_sem);
+static int ntty_open(struct tty_struct *tty, struct file *filp)
+{
+       struct port *port = get_port_by_tty(tty);
+       return tty_port_open(&port->port, tty, filp);
+}
 
-       WARN_ON(!port->count);
+static void ntty_shutdown(struct tty_port *tport)
+{
+       struct port *port = container_of(tport, struct port, port);
+       struct nozomi *dc = port->dc;
+       unsigned long flags;
 
+       DBG1("close: %d", port->token_dl);
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       dc->last_ier &= ~(port->token_dl);
+       writew(dc->last_ier, dc->reg_ier);
        dc->open_ttys--;
-       port->count--;
-       tty_port_tty_set(port, NULL);
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+       printk("noz: shutdown %p\n", tport);
+}
 
-       if (port->count == 0) {
-               DBG1("close: %d", nport->token_dl);
-               spin_lock_irqsave(&dc->spin_mutex, flags);
-               dc->last_ier &= ~(nport->token_dl);
-               writew(dc->last_ier, dc->reg_ier);
-               spin_unlock_irqrestore(&dc->spin_mutex, flags);
-       }
-       mutex_unlock(&nport->tty_sem);
+static void ntty_close(struct tty_struct *tty, struct file *filp)
+{
+       struct port *port = tty->driver_data;
+       if (port)
+               tty_port_close(&port->port, tty, filp);
+}
+
+static void ntty_hangup(struct tty_struct *tty)
+{
+       struct port *port = tty->driver_data;
+       tty_port_hangup(&port->port);
 }
 
 /*
@@ -1655,22 +1688,14 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer,
        if (!dc || !port)
                return -ENODEV;
 
-       if (unlikely(!mutex_trylock(&port->tty_sem))) {
-               /*
-                * must test lock as tty layer wraps calls
-                * to this function with BKL
-                */
-               dev_err(&dc->pdev->dev, "Would have deadlocked - "
-                       "return EAGAIN\n");
-               return -EAGAIN;
-       }
+       mutex_lock(&port->tty_sem);
 
        if (unlikely(!port->port.count)) {
                DBG1(" ");
                goto exit;
        }
 
-       rval = __kfifo_put(port->fifo_ul, (unsigned char *)buffer, count);
+       rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count);
 
        /* notify card */
        if (unlikely(dc == NULL)) {
@@ -1703,25 +1728,23 @@ exit:
  * This method is called by the upper tty layer.
  *   #according to sources N_TTY.c it expects a value >= 0 and
  *    does not check for negative values.
+ *
+ * If the port is unplugged report lots of room and let the bits
+ * dribble away so we don't block anything.
  */
 static int ntty_write_room(struct tty_struct *tty)
 {
        struct port *port = tty->driver_data;
-       int room = 0;
+       int room = 4096;
        const struct nozomi *dc = get_dc_by_tty(tty);
 
-       if (!dc || !port)
-               return 0;
-       if (!mutex_trylock(&port->tty_sem))
-               return 0;
-
-       if (!port->port.count)
-               goto exit;
-
-       room = port->fifo_ul->size - __kfifo_len(port->fifo_ul);
-
-exit:
-       mutex_unlock(&port->tty_sem);
+       if (dc) {
+               mutex_lock(&port->tty_sem);
+               if (port->port.count)
+                       room = port->fifo_ul.size -
+                                       kfifo_len(&port->fifo_ul);
+               mutex_unlock(&port->tty_sem);
+       }
        return room;
 }
 
@@ -1864,29 +1887,33 @@ static s32 ntty_chars_in_buffer(struct tty_struct *tty)
 {
        struct port *port = tty->driver_data;
        struct nozomi *dc = get_dc_by_tty(tty);
-       s32 rval;
+       s32 rval = 0;
 
        if (unlikely(!dc || !port)) {
-               rval = -ENODEV;
                goto exit_in_buffer;
        }
 
        if (unlikely(!port->port.count)) {
                dev_err(&dc->pdev->dev, "No tty open?\n");
-               rval = -ENODEV;
                goto exit_in_buffer;
        }
 
-       rval = __kfifo_len(port->fifo_ul);
+       rval = kfifo_len(&port->fifo_ul);
 
 exit_in_buffer:
        return rval;
 }
 
+static const struct tty_port_operations noz_tty_port_ops = {
+       .activate = ntty_activate,
+       .shutdown = ntty_shutdown,
+};
+
 static const struct tty_operations tty_ops = {
        .ioctl = ntty_ioctl,
        .open = ntty_open,
        .close = ntty_close,
+       .hangup = ntty_hangup,
        .write = ntty_write,
        .write_room = ntty_write_room,
        .unthrottle = ntty_unthrottle,
@@ -1894,6 +1921,8 @@ static const struct tty_operations tty_ops = {
        .chars_in_buffer = ntty_chars_in_buffer,
        .tiocmget = ntty_tiocmget,
        .tiocmset = ntty_tiocmset,
+       .install = ntty_install,
+       .cleanup = ntty_cleanup,
 };
 
 /* Module initialization */