]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/tty/serial/8250/8250_pci.c
serial/8250: Add suport for later SUNIX (TIMEDIA) boards.
[karo-tx-linux.git] / drivers / tty / serial / 8250 / 8250_pci.c
index a27a98e1b0667fc8bfd7e379eaaaa1cfa6c9229a..791c5a77ec61d473495190049ab5776e8a02a846 100644 (file)
@@ -1040,6 +1040,253 @@ static int pci_asix_setup(struct serial_private *priv,
        return pci_default_setup(priv, board, port, idx);
 }
 
+/* Quatech devices have their own extra interface features */
+
+struct quatech_feature {
+       u16 devid;
+       bool amcc;
+};
+
+#define QPCR_TEST_FOR1         0x3F
+#define QPCR_TEST_GET1         0x00
+#define QPCR_TEST_FOR2         0x40
+#define QPCR_TEST_GET2         0x40
+#define QPCR_TEST_FOR3         0x80
+#define QPCR_TEST_GET3         0x40
+#define QPCR_TEST_FOR4         0xC0
+#define QPCR_TEST_GET4         0x80
+
+#define QOPR_CLOCK_X1          0x0000
+#define QOPR_CLOCK_X2          0x0001
+#define QOPR_CLOCK_X4          0x0002
+#define QOPR_CLOCK_X8          0x0003
+#define QOPR_CLOCK_RATE_MASK   0x0003
+
+
+static struct quatech_feature quatech_cards[] = {
+       { PCI_DEVICE_ID_QUATECH_QSC100,   1 },
+       { PCI_DEVICE_ID_QUATECH_DSC100,   1 },
+       { PCI_DEVICE_ID_QUATECH_DSC100E,  0 },
+       { PCI_DEVICE_ID_QUATECH_DSC200,   1 },
+       { PCI_DEVICE_ID_QUATECH_DSC200E,  0 },
+       { PCI_DEVICE_ID_QUATECH_ESC100D,  1 },
+       { PCI_DEVICE_ID_QUATECH_ESC100M,  1 },
+       { PCI_DEVICE_ID_QUATECH_QSCP100,  1 },
+       { PCI_DEVICE_ID_QUATECH_DSCP100,  1 },
+       { PCI_DEVICE_ID_QUATECH_QSCP200,  1 },
+       { PCI_DEVICE_ID_QUATECH_DSCP200,  1 },
+       { PCI_DEVICE_ID_QUATECH_ESCLP100, 0 },
+       { PCI_DEVICE_ID_QUATECH_QSCLP100, 0 },
+       { PCI_DEVICE_ID_QUATECH_DSCLP100, 0 },
+       { PCI_DEVICE_ID_QUATECH_SSCLP100, 0 },
+       { PCI_DEVICE_ID_QUATECH_QSCLP200, 0 },
+       { PCI_DEVICE_ID_QUATECH_DSCLP200, 0 },
+       { PCI_DEVICE_ID_QUATECH_SSCLP200, 0 },
+       { PCI_DEVICE_ID_QUATECH_SPPXP_100, 0 },
+       { 0, }
+};
+
+static int pci_quatech_amcc(u16 devid)
+{
+       struct quatech_feature *qf = &quatech_cards[0];
+       while (qf->devid) {
+               if (qf->devid == devid)
+                       return qf->amcc;
+               qf++;
+       }
+       pr_err("quatech: unknown port type '0x%04X'.\n", devid);
+       return 0;
+};
+
+static int pci_quatech_rqopr(struct uart_8250_port *port)
+{
+       unsigned long base = port->port.iobase;
+       u8 LCR, val;
+
+       LCR = inb(base + UART_LCR);
+       outb(0xBF, base + UART_LCR);
+       val = inb(base + UART_SCR);
+       outb(LCR, base + UART_LCR);
+       return val;
+}
+
+static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr)
+{
+       unsigned long base = port->port.iobase;
+       u8 LCR, val;
+
+       LCR = inb(base + UART_LCR);
+       outb(0xBF, base + UART_LCR);
+       val = inb(base + UART_SCR);
+       outb(qopr, base + UART_SCR);
+       outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_rqmcr(struct uart_8250_port *port)
+{
+       unsigned long base = port->port.iobase;
+       u8 LCR, val, qmcr;
+
+       LCR = inb(base + UART_LCR);
+       outb(0xBF, base + UART_LCR);
+       val = inb(base + UART_SCR);
+       outb(val | 0x10, base + UART_SCR);
+       qmcr = inb(base + UART_MCR);
+       outb(val, base + UART_SCR);
+       outb(LCR, base + UART_LCR);
+
+       return qmcr;
+}
+
+static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr)
+{
+       unsigned long base = port->port.iobase;
+       u8 LCR, val;
+
+       LCR = inb(base + UART_LCR);
+       outb(0xBF, base + UART_LCR);
+       val = inb(base + UART_SCR);
+       outb(val | 0x10, base + UART_SCR);
+       outb(qmcr, base + UART_MCR);
+       outb(val, base + UART_SCR);
+       outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_has_qmcr(struct uart_8250_port *port)
+{
+       unsigned long base = port->port.iobase;
+       u8 LCR, val;
+
+       LCR = inb(base + UART_LCR);
+       outb(0xBF, base + UART_LCR);
+       val = inb(base + UART_SCR);
+       if (val & 0x20) {
+               outb(0x80, UART_LCR);
+               if (!(inb(UART_SCR) & 0x20)) {
+                       outb(LCR, base + UART_LCR);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int pci_quatech_test(struct uart_8250_port *port)
+{
+       u8 reg;
+       u8 qopr = pci_quatech_rqopr(port);
+       pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1);
+       reg = pci_quatech_rqopr(port) & 0xC0;
+       if (reg != QPCR_TEST_GET1)
+               return -EINVAL;
+       pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR2);
+       reg = pci_quatech_rqopr(port) & 0xC0;
+       if (reg != QPCR_TEST_GET2)
+               return -EINVAL;
+       pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR3);
+       reg = pci_quatech_rqopr(port) & 0xC0;
+       if (reg != QPCR_TEST_GET3)
+               return -EINVAL;
+       pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR4);
+       reg = pci_quatech_rqopr(port) & 0xC0;
+       if (reg != QPCR_TEST_GET4)
+               return -EINVAL;
+
+       pci_quatech_wqopr(port, qopr);
+       return 0;
+}
+
+static int pci_quatech_clock(struct uart_8250_port *port)
+{
+       u8 qopr, reg, set;
+       unsigned long clock;
+
+       if (pci_quatech_test(port) < 0)
+               return 1843200;
+
+       qopr = pci_quatech_rqopr(port);
+
+       pci_quatech_wqopr(port, qopr & ~QOPR_CLOCK_X8);
+       reg = pci_quatech_rqopr(port);
+       if (reg & QOPR_CLOCK_X8) {
+               clock = 1843200;
+               goto out;
+       }
+       pci_quatech_wqopr(port, qopr | QOPR_CLOCK_X8);
+       reg = pci_quatech_rqopr(port);
+       if (!(reg & QOPR_CLOCK_X8)) {
+               clock = 1843200;
+               goto out;
+       }
+       reg &= QOPR_CLOCK_X8;
+       if (reg == QOPR_CLOCK_X2) {
+               clock =  3685400;
+               set = QOPR_CLOCK_X2;
+       } else if (reg == QOPR_CLOCK_X4) {
+               clock = 7372800;
+               set = QOPR_CLOCK_X4;
+       } else if (reg == QOPR_CLOCK_X8) {
+               clock = 14745600;
+               set = QOPR_CLOCK_X8;
+       } else {
+               clock = 1843200;
+               set = QOPR_CLOCK_X1;
+       }
+       qopr &= ~QOPR_CLOCK_RATE_MASK;
+       qopr |= set;
+
+out:
+       pci_quatech_wqopr(port, qopr);
+       return clock;
+}
+
+static int pci_quatech_rs422(struct uart_8250_port *port)
+{
+       u8 qmcr;
+       int rs422 = 0;
+
+       if (!pci_quatech_has_qmcr(port))
+               return 0;
+       qmcr = pci_quatech_rqmcr(port);
+       pci_quatech_wqmcr(port, 0xFF);
+       if (pci_quatech_rqmcr(port))
+               rs422 = 1;
+       pci_quatech_wqmcr(port, qmcr);
+       return rs422;
+}
+
+static int pci_quatech_init(struct pci_dev *dev)
+{
+       if (pci_quatech_amcc(dev->device)) {
+               unsigned long base = pci_resource_start(dev, 0);
+               if (base) {
+                       u32 tmp;
+                       outl(inl(base + 0x38), base + 0x38);
+                       tmp = inl(base + 0x3c);
+                       outl(tmp | 0x01000000, base + 0x3c);
+                       outl(tmp, base + 0x3c);
+               }
+       }
+       return 0;
+}
+
+static int pci_quatech_setup(struct serial_private *priv,
+                 const struct pciserial_board *board,
+                 struct uart_8250_port *port, int idx)
+{
+       /* Needed by pci_quatech calls below */
+       port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags));
+       /* Set up the clocking */
+       port->port.uartclk = pci_quatech_clock(port);
+       /* For now just warn about RS422 */
+       if (pci_quatech_rs422(port))
+               pr_warn("quatech: software control of RS422 features not currently supported.\n");
+       return pci_default_setup(priv, board, port, idx);
+}
+
+static void pci_quatech_exit(struct pci_dev *dev)
+{
+}
+
 static int pci_default_setup(struct serial_private *priv,
                  const struct pciserial_board *board,
                  struct uart_8250_port *port, int idx)
@@ -1318,6 +1565,9 @@ pci_wch_ch353_setup(struct serial_private *priv,
 #define PCI_DEVICE_ID_COMMTECH_4222PCIE        0x0022
 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
 
+#define PCI_VENDOR_ID_SUNIX            0x1fd4
+#define PCI_DEVICE_ID_SUNIX_1999       0x1999
+
 
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584        0x1584
@@ -1541,6 +1791,16 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .setup          = pci_ni8430_setup,
                .exit           = pci_ni8430_exit,
        },
+       /* Quatech */
+       {
+               .vendor         = PCI_VENDOR_ID_QUATECH,
+               .device         = PCI_ANY_ID,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .init           = pci_quatech_init,
+               .setup          = pci_quatech_setup,
+               .exit           = pci_quatech_exit,
+       },
        /*
         * Panacom
         */
@@ -1703,6 +1963,23 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .subdevice      = PCI_ANY_ID,
                .setup          = pci_timedia_setup,
        },
+       /*
+        * SUNIX (Timedia) cards
+        * Do not "probe" for these cards as there is at least one combination
+        * card that should be handled by parport_pc that doesn't match the
+        * rule in pci_timedia_probe.
+        * It is part number is MIO5079A but its subdevice ID is 0x0102.
+        * There are some boards with part number SER5037AL that report
+        * subdevice ID 0x0002.
+        */
+       {
+               .vendor         = PCI_VENDOR_ID_SUNIX,
+               .device         = PCI_DEVICE_ID_SUNIX_1999,
+               .subvendor      = PCI_VENDOR_ID_SUNIX,
+               .subdevice      = PCI_ANY_ID,
+               .init           = pci_timedia_init,
+               .setup          = pci_timedia_setup,
+       },
        /*
         * Exar cards
         */
@@ -3506,18 +3783,70 @@ static struct pci_device_id serial_pci_tbl[] = {
        {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
                0x10b5, 0x106a, 0, 0,
                pbn_plx_romulus },
+       /*
+        * Quatech cards. These actually have configurable clocks but for
+        * now we just use the default.
+        *
+        * 100 series are RS232, 200 series RS422,
+        */
        {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
                PCI_ANY_ID, PCI_ANY_ID, 0, 0,
                pbn_b1_4_115200 },
        {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
                PCI_ANY_ID, PCI_ANY_ID, 0, 0,
                pbn_b1_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b1_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b1_4_115200 },
        {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
                PCI_ANY_ID, PCI_ANY_ID, 0, 0,
                pbn_b1_8_115200 },
        {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
                PCI_ANY_ID, PCI_ANY_ID, 0, 0,
                pbn_b1_8_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b1_4_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b1_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b1_4_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b1_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_4_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_1_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_4_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_1_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_8_115200 },
+
        {       PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
                PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
                0, 0,
@@ -3901,6 +4230,19 @@ static struct pci_device_id serial_pci_tbl[] = {
                PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0,
                pbn_b0_bt_1_921600 },
 
+       /*
+        * SUNIX (TIMEDIA)
+        */
+       {       PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+               PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00,
+               pbn_b0_bt_1_921600 },
+
+       {       PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+               PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
+               pbn_b0_bt_1_921600 },
+
        /*
         * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
         */