serial: 8250_pci: add support for Intel BayTrail
authorHeikki Krogerus <heikki.krogerus@linux.intel.com>
Fri, 27 Sep 2013 07:52:59 +0000 (10:52 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 28 Sep 2013 00:39:11 +0000 (17:39 -0700)
Intel BayTrail has two HS-UARTs with 64 byte fifo, support
for DMA and support for 16750 compatible Auto Flow Control.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_pci.c

index 515fd0f..d917bbb 100644 (file)
@@ -1324,6 +1324,120 @@ ce4100_serial_setup(struct serial_private *priv,
        return ret;
 }
 
+#define PCI_DEVICE_ID_INTEL_BYT_UART1  0x0f0a
+#define PCI_DEVICE_ID_INTEL_BYT_UART2  0x0f0c
+
+#define BYT_PRV_CLK                    0x800
+#define BYT_PRV_CLK_EN                 (1 << 0)
+#define BYT_PRV_CLK_M_VAL_SHIFT                1
+#define BYT_PRV_CLK_N_VAL_SHIFT                16
+#define BYT_PRV_CLK_UPDATE             (1 << 31)
+
+#define BYT_GENERAL_REG                        0x808
+#define BYT_GENERAL_DIS_RTS_N_OVERRIDE (1 << 3)
+
+#define BYT_TX_OVF_INT                 0x820
+#define BYT_TX_OVF_INT_MASK            (1 << 1)
+
+static void
+byt_set_termios(struct uart_port *p, struct ktermios *termios,
+               struct ktermios *old)
+{
+       unsigned int baud = tty_termios_baud_rate(termios);
+       unsigned int m = 6912;
+       unsigned int n = 15625;
+       u32 reg;
+
+       /* For baud rates 1M, 2M, 3M and 4M the dividers must be adjusted. */
+       if (baud == 1000000 || baud == 2000000 || baud == 4000000) {
+               m = 64;
+               n = 100;
+
+               p->uartclk = 64000000;
+       } else if (baud == 3000000) {
+               m = 48;
+               n = 100;
+
+               p->uartclk = 48000000;
+       } else {
+               p->uartclk = 44236800;
+       }
+
+       /* Reset the clock */
+       reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
+       writel(reg, p->membase + BYT_PRV_CLK);
+       reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
+       writel(reg, p->membase + BYT_PRV_CLK);
+
+       /*
+        * If auto-handshake mechanism is not enabled,
+        * disable rts_n override
+        */
+       reg = readl(p->membase + BYT_GENERAL_REG);
+       reg &= ~BYT_GENERAL_DIS_RTS_N_OVERRIDE;
+       if (termios->c_cflag & CRTSCTS)
+               reg |= BYT_GENERAL_DIS_RTS_N_OVERRIDE;
+       writel(reg, p->membase + BYT_GENERAL_REG);
+
+       serial8250_do_set_termios(p, termios, old);
+}
+
+static bool byt_dma_filter(struct dma_chan *chan, void *param)
+{
+       return chan->chan_id == *(int *)param;
+}
+
+static int
+byt_serial_setup(struct serial_private *priv,
+                const struct pciserial_board *board,
+                struct uart_8250_port *port, int idx)
+{
+       struct uart_8250_dma *dma;
+       int ret;
+
+       dma = devm_kzalloc(port->port.dev, sizeof(*dma), GFP_KERNEL);
+       if (!dma)
+               return -ENOMEM;
+
+       switch (priv->dev->device) {
+       case PCI_DEVICE_ID_INTEL_BYT_UART1:
+               dma->rx_chan_id = 3;
+               dma->tx_chan_id = 2;
+               break;
+       case PCI_DEVICE_ID_INTEL_BYT_UART2:
+               dma->rx_chan_id = 5;
+               dma->tx_chan_id = 4;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dma->rxconf.slave_id = dma->rx_chan_id;
+       dma->rxconf.src_maxburst = 16;
+
+       dma->txconf.slave_id = dma->tx_chan_id;
+       dma->txconf.dst_maxburst = 16;
+
+       dma->fn = byt_dma_filter;
+       dma->rx_param = &dma->rx_chan_id;
+       dma->tx_param = &dma->tx_chan_id;
+
+       ret = pci_default_setup(priv, board, port, idx);
+       port->port.iotype = UPIO_MEM;
+       port->port.type = PORT_16550A;
+       port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+       port->port.set_termios = byt_set_termios;
+       port->port.fifosize = 64;
+       port->tx_loadsz = 64;
+       port->dma = dma;
+       port->capabilities = UART_CAP_FIFO | UART_CAP_AFE;
+
+       /* Disable Tx counter interrupts */
+       writel(BYT_TX_OVF_INT_MASK, port->port.membase + BYT_TX_OVF_INT);
+
+       return ret;
+}
+
 static int
 pci_omegapci_setup(struct serial_private *priv,
                      const struct pciserial_board *board,
@@ -1662,6 +1776,20 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .subdevice      = PCI_ANY_ID,
                .setup          = kt_serial_setup,
        },
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BYT_UART1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = byt_serial_setup,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BYT_UART2,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = byt_serial_setup,
+       },
        /*
         * ITE
         */
@@ -2449,6 +2577,7 @@ enum pci_board_num_t {
        pbn_ADDIDATA_PCIe_4_3906250,
        pbn_ADDIDATA_PCIe_8_3906250,
        pbn_ce4100_1_115200,
+       pbn_byt,
        pbn_omegapci,
        pbn_NETMOS9900_2s_115200,
        pbn_brcm_trumanage,
@@ -3185,6 +3314,13 @@ static struct pciserial_board pci_boards[] = {
                .base_baud      = 921600,
                .reg_shift      = 2,
        },
+       [pbn_byt] = {
+               .flags          = FL_BASE0,
+               .num_ports      = 1,
+               .base_baud      = 2764800,
+               .uart_offset    = 0x80,
+               .reg_shift      = 2,
+       },
        [pbn_omegapci] = {
                .flags          = FL_BASE0,
                .num_ports      = 8,
@@ -4846,6 +4982,15 @@ static struct pci_device_id serial_pci_tbl[] = {
        {       PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART,
                PCI_ANY_ID,  PCI_ANY_ID, 0, 0,
                pbn_ce4100_1_115200 },
+       /* Intel BayTrail */
+       {       PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART1,
+               PCI_ANY_ID,  PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
+               pbn_byt },
+       {       PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART2,
+               PCI_ANY_ID,  PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
+               pbn_byt },
 
        /*
         * Cronyx Omega PCI