aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/8250/8250_pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/8250/8250_pci.c')
-rw-r--r--drivers/tty/serial/8250/8250_pci.c414
1 files changed, 296 insertions, 118 deletions
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 892eb32cdef4..08da4d3e2162 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -21,12 +21,14 @@
#include <linux/serial_core.h>
#include <linux/8250_pci.h>
#include <linux/bitops.h>
+#include <linux/rational.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <linux/dmaengine.h>
#include <linux/platform_data/dma-dw.h>
+#include <linux/platform_data/dma-hsu.h>
#include "8250.h"
@@ -1392,45 +1394,22 @@ byt_set_termios(struct uart_port *p, struct ktermios *termios,
struct ktermios *old)
{
unsigned int baud = tty_termios_baud_rate(termios);
- unsigned int m, n;
+ unsigned long fref = 100000000, fuart = baud * 16;
+ unsigned long w = BIT(15) - 1;
+ unsigned long m, n;
u32 reg;
+ /* Get Fuart closer to Fref */
+ fuart *= rounddown_pow_of_two(fref / fuart);
+
/*
* For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the
* dividers must be adjusted.
*
* uartclk = (m / n) * 100 MHz, where m <= n
*/
- switch (baud) {
- case 500000:
- case 1000000:
- case 2000000:
- case 4000000:
- m = 64;
- n = 100;
- p->uartclk = 64000000;
- break;
- case 3500000:
- m = 56;
- n = 100;
- p->uartclk = 56000000;
- break;
- case 1500000:
- case 3000000:
- m = 48;
- n = 100;
- p->uartclk = 48000000;
- break;
- case 2500000:
- m = 40;
- n = 100;
- p->uartclk = 40000000;
- break;
- default:
- m = 2304;
- n = 3125;
- p->uartclk = 73728000;
- }
+ rational_best_approximation(fuart, fref, w, w, &m, &n);
+ p->uartclk = fuart;
/* Reset the clock */
reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
@@ -1525,6 +1504,167 @@ byt_serial_setup(struct serial_private *priv,
return ret;
}
+#define INTEL_MID_UART_PS 0x30
+#define INTEL_MID_UART_MUL 0x34
+#define INTEL_MID_UART_DIV 0x38
+
+static void intel_mid_set_termios(struct uart_port *p,
+ struct ktermios *termios,
+ struct ktermios *old,
+ unsigned long fref)
+{
+ unsigned int baud = tty_termios_baud_rate(termios);
+ unsigned short ps = 16;
+ unsigned long fuart = baud * ps;
+ unsigned long w = BIT(24) - 1;
+ unsigned long mul, div;
+
+ if (fref < fuart) {
+ /* Find prescaler value that satisfies Fuart < Fref */
+ if (fref > baud)
+ ps = fref / baud; /* baud rate too high */
+ else
+ ps = 1; /* PLL case */
+ fuart = baud * ps;
+ } else {
+ /* Get Fuart closer to Fref */
+ fuart *= rounddown_pow_of_two(fref / fuart);
+ }
+
+ rational_best_approximation(fuart, fref, w, w, &mul, &div);
+ p->uartclk = fuart * 16 / ps; /* core uses ps = 16 always */
+
+ writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */
+ writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */
+ writel(div, p->membase + INTEL_MID_UART_DIV);
+
+ serial8250_do_set_termios(p, termios, old);
+}
+
+static void intel_mid_set_termios_38_4M(struct uart_port *p,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ intel_mid_set_termios(p, termios, old, 38400000);
+}
+
+static void intel_mid_set_termios_50M(struct uart_port *p,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ /*
+ * The uart clk is 50Mhz, and the baud rate come from:
+ * baud = 50M * MUL / (DIV * PS * DLAB)
+ */
+ intel_mid_set_termios(p, termios, old, 50000000);
+}
+
+static bool intel_mid_dma_filter(struct dma_chan *chan, void *param)
+{
+ struct hsu_dma_slave *s = param;
+
+ if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id)
+ return false;
+
+ chan->private = s;
+ return true;
+}
+
+static int intel_mid_serial_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx,
+ int index, struct pci_dev *dma_dev)
+{
+ struct device *dev = port->port.dev;
+ struct uart_8250_dma *dma;
+ struct hsu_dma_slave *tx_param, *rx_param;
+
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL);
+ if (!tx_param)
+ return -ENOMEM;
+
+ rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL);
+ if (!rx_param)
+ return -ENOMEM;
+
+ rx_param->chan_id = index * 2 + 1;
+ tx_param->chan_id = index * 2;
+
+ dma->rxconf.src_maxburst = 64;
+ dma->txconf.dst_maxburst = 64;
+
+ rx_param->dma_dev = &dma_dev->dev;
+ tx_param->dma_dev = &dma_dev->dev;
+
+ dma->fn = intel_mid_dma_filter;
+ dma->rx_param = rx_param;
+ dma->tx_param = tx_param;
+
+ port->port.type = PORT_16750;
+ port->port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
+ port->dma = dma;
+
+ return pci_default_setup(priv, board, port, idx);
+}
+
+#define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b
+#define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c
+#define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d
+
+static int pnw_serial_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ struct pci_dev *pdev = priv->dev;
+ struct pci_dev *dma_dev;
+ int index;
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_PNW_UART1:
+ index = 0;
+ break;
+ case PCI_DEVICE_ID_INTEL_PNW_UART2:
+ index = 1;
+ break;
+ case PCI_DEVICE_ID_INTEL_PNW_UART3:
+ index = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 3));
+
+ port->port.set_termios = intel_mid_set_termios_50M;
+
+ return intel_mid_serial_setup(priv, board, port, idx, index, dma_dev);
+}
+
+#define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191
+
+static int tng_serial_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ struct pci_dev *pdev = priv->dev;
+ struct pci_dev *dma_dev;
+ int index = PCI_FUNC(pdev->devfn);
+
+ /* Currently no support for HSU port0 */
+ if (index-- == 0)
+ return -ENODEV;
+
+ dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0));
+
+ port->port.set_termios = intel_mid_set_termios_38_4M;
+
+ return intel_mid_serial_setup(priv, board, port, idx, index, dma_dev);
+}
+
static int
pci_omegapci_setup(struct serial_private *priv,
const struct pciserial_board *board,
@@ -1550,95 +1690,71 @@ static int pci_fintek_setup(struct serial_private *priv,
struct uart_8250_port *port, int idx)
{
struct pci_dev *pdev = priv->dev;
- unsigned long base;
- unsigned long iobase;
- unsigned long ciobase = 0;
u8 config_base;
+ u16 iobase;
+
+ config_base = 0x40 + 0x08 * idx;
+
+ /* Get the io address from configuration space */
+ pci_read_config_word(pdev, config_base + 4, &iobase);
+
+ dev_dbg(&pdev->dev, "%s: idx=%d iobase=0x%x", __func__, idx, iobase);
+
+ port->port.iotype = UPIO_PORT;
+ port->port.iobase = iobase;
+
+ return 0;
+}
+
+static int pci_fintek_init(struct pci_dev *dev)
+{
+ unsigned long iobase;
+ u32 max_port, i;
u32 bar_data[3];
+ u8 config_base;
- /*
- * Find each UARTs offset in PCI configuraion space
- */
- switch (idx) {
- case 0:
- config_base = 0x40;
+ switch (dev->device) {
+ case 0x1104: /* 4 ports */
+ case 0x1108: /* 8 ports */
+ max_port = dev->device & 0xff;
break;
- case 1:
- config_base = 0x48;
- break;
- case 2:
- config_base = 0x50;
- break;
- case 3:
- config_base = 0x58;
- break;
- case 4:
- config_base = 0x60;
- break;
- case 5:
- config_base = 0x68;
- break;
- case 6:
- config_base = 0x70;
- break;
- case 7:
- config_base = 0x78;
- break;
- case 8:
- config_base = 0x80;
- break;
- case 9:
- config_base = 0x88;
- break;
- case 10:
- config_base = 0x90;
- break;
- case 11:
- config_base = 0x98;
+ case 0x1112: /* 12 ports */
+ max_port = 12;
break;
default:
- /* Unknown number of ports, get out of here */
return -EINVAL;
}
- if (idx < 4) {
- base = pci_resource_start(priv->dev, 3);
- ciobase = (int)(base + (0x8 * idx));
- }
-
/* Get the io address dispatch from the BIOS */
- pci_read_config_dword(pdev, 0x24, &bar_data[0]);
- pci_read_config_dword(pdev, 0x20, &bar_data[1]);
- pci_read_config_dword(pdev, 0x1c, &bar_data[2]);
-
- /* Calculate Real IO Port */
- iobase = (bar_data[idx/4] & 0xffffffe0) + (idx % 4) * 8;
+ pci_read_config_dword(dev, 0x24, &bar_data[0]);
+ pci_read_config_dword(dev, 0x20, &bar_data[1]);
+ pci_read_config_dword(dev, 0x1c, &bar_data[2]);
- dev_dbg(&pdev->dev, "%s: idx=%d iobase=0x%lx ciobase=0x%lx config_base=0x%2x\n",
- __func__, idx, iobase, ciobase, config_base);
+ for (i = 0; i < max_port; ++i) {
+ /* UART0 configuration offset start from 0x40 */
+ config_base = 0x40 + 0x08 * i;
- /* Enable UART I/O port */
- pci_write_config_byte(pdev, config_base + 0x00, 0x01);
+ /* Calculate Real IO Port */
+ iobase = (bar_data[i / 4] & 0xffffffe0) + (i % 4) * 8;
- /* Select 128-byte FIFO and 8x FIFO threshold */
- pci_write_config_byte(pdev, config_base + 0x01, 0x33);
+ /* Enable UART I/O port */
+ pci_write_config_byte(dev, config_base + 0x00, 0x01);
- /* LSB UART */
- pci_write_config_byte(pdev, config_base + 0x04, (u8)(iobase & 0xff));
+ /* Select 128-byte FIFO and 8x FIFO threshold */
+ pci_write_config_byte(dev, config_base + 0x01, 0x33);
- /* MSB UART */
- pci_write_config_byte(pdev, config_base + 0x05, (u8)((iobase & 0xff00) >> 8));
+ /* LSB UART */
+ pci_write_config_byte(dev, config_base + 0x04,
+ (u8)(iobase & 0xff));
- /* irq number, this usually fails, but the spec says to do it anyway. */
- pci_write_config_byte(pdev, config_base + 0x06, pdev->irq);
+ /* MSB UART */
+ pci_write_config_byte(dev, config_base + 0x05,
+ (u8)((iobase & 0xff00) >> 8));
- port->port.iotype = UPIO_PORT;
- port->port.iobase = iobase;
- port->port.mapbase = 0;
- port->port.membase = NULL;
- port->port.regshift = 0;
+ pci_write_config_byte(dev, config_base + 0x06, dev->irq);
+ }
- return 0;
+ return max_port;
}
static int skip_tx_en_setup(struct serial_private *priv,
@@ -1989,6 +2105,34 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
},
{
.vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_PNW_UART1,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pnw_serial_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_PNW_UART2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pnw_serial_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_PNW_UART3,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pnw_serial_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_TNG_UART,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = tng_serial_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_BSW_UART1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
@@ -2653,6 +2797,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_fintek_setup,
+ .init = pci_fintek_init,
},
{
.vendor = 0x1c29,
@@ -2660,6 +2805,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_fintek_setup,
+ .init = pci_fintek_init,
},
{
.vendor = 0x1c29,
@@ -2667,6 +2813,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_fintek_setup,
+ .init = pci_fintek_init,
},
/*
@@ -2864,6 +3011,8 @@ enum pci_board_num_t {
pbn_ADDIDATA_PCIe_8_3906250,
pbn_ce4100_1_115200,
pbn_byt,
+ pbn_pnw,
+ pbn_tng,
pbn_qrk,
pbn_omegapci,
pbn_NETMOS9900_2s_115200,
@@ -3630,6 +3779,16 @@ static struct pciserial_board pci_boards[] = {
.uart_offset = 0x80,
.reg_shift = 2,
},
+ [pbn_pnw] = {
+ .flags = FL_BASE0,
+ .num_ports = 1,
+ .base_baud = 115200,
+ },
+ [pbn_tng] = {
+ .flags = FL_BASE0,
+ .num_ports = 1,
+ .base_baud = 1843200,
+ },
[pbn_qrk] = {
.flags = FL_BASE0,
.num_ports = 1,
@@ -4006,41 +4165,41 @@ static void pciserial_remove_one(struct pci_dev *dev)
pci_disable_device(dev);
}
-#ifdef CONFIG_PM
-static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int pciserial_suspend_one(struct device *dev)
{
- struct serial_private *priv = pci_get_drvdata(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct serial_private *priv = pci_get_drvdata(pdev);
if (priv)
pciserial_suspend_ports(priv);
- pci_save_state(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
return 0;
}
-static int pciserial_resume_one(struct pci_dev *dev)
+static int pciserial_resume_one(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct serial_private *priv = pci_get_drvdata(pdev);
int err;
- struct serial_private *priv = pci_get_drvdata(dev);
-
- pci_set_power_state(dev, PCI_D0);
- pci_restore_state(dev);
if (priv) {
/*
* The device may have been disabled. Re-enable it.
*/
- err = pci_enable_device(dev);
+ err = pci_enable_device(pdev);
/* FIXME: We cannot simply error out here */
if (err)
- dev_err(&dev->dev, "Unable to re-enable ports, trying to continue.\n");
+ dev_err(dev, "Unable to re-enable ports, trying to continue.\n");
pciserial_resume_ports(priv);
}
return 0;
}
#endif
+static SIMPLE_DEV_PM_OPS(pciserial_pm_ops, pciserial_suspend_one,
+ pciserial_resume_one);
+
static struct pci_device_id serial_pci_tbl[] = {
/* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620,
@@ -5363,6 +5522,26 @@ static struct pci_device_id serial_pci_tbl[] = {
pbn_byt },
/*
+ * Intel Penwell
+ */
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART1,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pnw},
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pnw},
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pnw},
+
+ /*
+ * Intel Tangier
+ */
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TNG_UART,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_tng},
+
+ /*
* Intel Quark x1000
*/
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_UART,
@@ -5510,10 +5689,9 @@ static struct pci_driver serial_pci_driver = {
.name = "serial",
.probe = pciserial_init_one,
.remove = pciserial_remove_one,
-#ifdef CONFIG_PM
- .suspend = pciserial_suspend_one,
- .resume = pciserial_resume_one,
-#endif
+ .driver = {
+ .pm = &pciserial_pm_ops,
+ },
.id_table = serial_pci_tbl,
.err_handler = &serial8250_err_handler,
};