/*
- *Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD.
+ *Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
*This program is free software; you can redistribute it and/or modify
*it under the terms of the GNU General Public License as published by
*along with this program; if not, write to the Free Software
*Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/kernel.h>
#include <linux/serial_reg.h>
-#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/dmi.h>
#include <linux/dmaengine.h>
#include <linux/pch_dma.h>
/* Set the max number of UART port
* Intel EG20T PCH: 4 port
- * OKI SEMICONDUCTOR ML7213 IOH: 3 port
+ * LAPIS Semiconductor ML7213 IOH: 3 port
+ * LAPIS Semiconductor ML7223 IOH: 2 port
*/
#define PCH_UART_NR 4
#define PCH_UART_DLL 0x00
#define PCH_UART_DLM 0x01
-#define DIV_ROUND(a, b) (((a) + ((b)/2)) / (b))
-
#define PCH_UART_IID_RLS (PCH_UART_IIR_REI)
#define PCH_UART_IID_RDR (PCH_UART_IIR_RRI)
#define PCH_UART_IID_RDR_TO (PCH_UART_IIR_RRI | PCH_UART_IIR_TOI)
struct pch_uart_buffer rxbuf;
unsigned int dmsr;
unsigned int fcr;
+ unsigned int mcr;
unsigned int use_dma;
unsigned int use_dma_flag;
struct dma_async_tx_descriptor *desc_tx;
dma_addr_t rx_buf_dma;
};
+/**
+ * struct pch_uart_driver_data - private data structure for UART-DMA
+ * @port_type: The number of DMA channel
+ * @line_no: UART port line number (0, 1, 2...)
+ */
+struct pch_uart_driver_data {
+ int port_type;
+ int line_no;
+};
+
+enum pch_uart_num_t {
+ pch_et20t_uart0 = 0,
+ pch_et20t_uart1,
+ pch_et20t_uart2,
+ pch_et20t_uart3,
+ pch_ml7213_uart0,
+ pch_ml7213_uart1,
+ pch_ml7213_uart2,
+ pch_ml7223_uart0,
+ pch_ml7223_uart1,
+ pch_ml7831_uart0,
+ pch_ml7831_uart1,
+};
+
+static struct pch_uart_driver_data drv_dat[] = {
+ [pch_et20t_uart0] = {PCH_UART_8LINE, 0},
+ [pch_et20t_uart1] = {PCH_UART_2LINE, 1},
+ [pch_et20t_uart2] = {PCH_UART_2LINE, 2},
+ [pch_et20t_uart3] = {PCH_UART_2LINE, 3},
+ [pch_ml7213_uart0] = {PCH_UART_8LINE, 0},
+ [pch_ml7213_uart1] = {PCH_UART_2LINE, 1},
+ [pch_ml7213_uart2] = {PCH_UART_2LINE, 2},
+ [pch_ml7223_uart0] = {PCH_UART_8LINE, 0},
+ [pch_ml7223_uart1] = {PCH_UART_2LINE, 1},
+ [pch_ml7831_uart0] = {PCH_UART_8LINE, 0},
+ [pch_ml7831_uart1] = {PCH_UART_2LINE, 1},
+};
+
static unsigned int default_baud = 9600;
static const int trigger_level_256[4] = { 1, 64, 128, 224 };
static const int trigger_level_64[4] = { 1, 16, 32, 56 };
unsigned int dll, dlm, lcr;
int div;
- div = DIV_ROUND(priv->base_baud / 16, baud);
+ div = DIV_ROUND_CLOSEST(priv->base_baud / 16, baud);
if (div < 0 || USHRT_MAX <= div) {
- pr_err("Invalid Baud(div=0x%x)\n", div);
+ dev_err(priv->port.dev, "Invalid Baud(div=0x%x)\n", div);
return -EINVAL;
}
dlm = ((unsigned int)div >> 8) & 0x00FFU;
if (parity & ~(PCH_UART_LCR_PEN | PCH_UART_LCR_EPS | PCH_UART_LCR_SP)) {
- pr_err("Invalid parity(0x%x)\n", parity);
+ dev_err(priv->port.dev, "Invalid parity(0x%x)\n", parity);
return -EINVAL;
}
if (bits & ~PCH_UART_LCR_WLS) {
- pr_err("Invalid bits(0x%x)\n", bits);
+ dev_err(priv->port.dev, "Invalid bits(0x%x)\n", bits);
return -EINVAL;
}
if (stb & ~PCH_UART_LCR_STB) {
- pr_err("Invalid STB(0x%x)\n", stb);
+ dev_err(priv->port.dev, "Invalid STB(0x%x)\n", stb);
return -EINVAL;
}
lcr |= bits;
lcr |= stb;
- pr_debug("%s:baud = %d, div = %04x, lcr = %02x (%lu)\n",
+ dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n",
__func__, baud, div, lcr, jiffies);
iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
iowrite8(dll, priv->membase + PCH_UART_DLL);
unsigned int flag)
{
if (flag & ~(PCH_UART_FCR_TFR | PCH_UART_FCR_RFR)) {
- pr_err("%s:Invalid flag(0x%x)\n", __func__, flag);
+ dev_err(priv->port.dev, "%s:Invalid flag(0x%x)\n",
+ __func__, flag);
return -EINVAL;
}
u8 fcr;
if (dmamode & ~PCH_UART_FCR_DMS) {
- pr_err("%s:Invalid DMA Mode(0x%x)\n", __func__, dmamode);
+ dev_err(priv->port.dev, "%s:Invalid DMA Mode(0x%x)\n",
+ __func__, dmamode);
return -EINVAL;
}
if (fifo_size & ~(PCH_UART_FCR_FIFOE | PCH_UART_FCR_FIFO256)) {
- pr_err("%s:Invalid FIFO SIZE(0x%x)\n", __func__, fifo_size);
+ dev_err(priv->port.dev, "%s:Invalid FIFO SIZE(0x%x)\n",
+ __func__, fifo_size);
return -EINVAL;
}
if (trigger & ~PCH_UART_FCR_RFTL) {
- pr_err("%s:Invalid TRIGGER(0x%x)\n", __func__, trigger);
+ dev_err(priv->port.dev, "%s:Invalid TRIGGER(0x%x)\n",
+ __func__, trigger);
return -EINVAL;
}
port = &priv->port;
tty = tty_port_tty_get(&port->state->port);
if (!tty) {
- pr_debug("%s:tty is busy now", __func__);
+ dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
return -EBUSY;
}
struct uart_port *port = &priv->port;
if (port->x_char) {
- pr_debug("%s:X character send %02x (%lu)\n", __func__,
- port->x_char, jiffies);
+ dev_dbg(priv->port.dev, "%s:X character send %02x (%lu)\n",
+ __func__, port->x_char, jiffies);
buf[0] = port->x_char;
port->x_char = 0;
ret = 1;
port = &priv->port;
tty = tty_port_tty_get(&port->state->port);
if (!tty) {
- pr_debug("%s:tty is busy now", __func__);
+ dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
return 0;
}
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0xa, 0)); /* Get DMA's dev
+ dma_dev = pci_get_bus_and_slot(priv->pdev->bus->number,
+ PCI_DEVFN(0xa, 0)); /* Get DMA's dev
information */
/* Set Tx DMA */
param = &priv->param_tx;
param->dma_dev = &dma_dev->dev;
- param->chan_id = priv->port.line;
+ param->chan_id = priv->port.line * 2; /* Tx = 0, 2, 4, ... */
+
param->tx_reg = port->mapbase + UART_TX;
chan = dma_request_channel(mask, filter, param);
if (!chan) {
- pr_err("%s:dma_request_channel FAILS(Tx)\n", __func__);
+ dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n",
+ __func__);
return;
}
priv->chan_tx = chan;
/* Set Rx DMA */
param = &priv->param_rx;
param->dma_dev = &dma_dev->dev;
- param->chan_id = priv->port.line + 1; /* Rx = Tx + 1 */
+ param->chan_id = priv->port.line * 2 + 1; /* Rx = Tx + 1 */
+
param->rx_reg = port->mapbase + UART_RX;
chan = dma_request_channel(mask, filter, param);
if (!chan) {
- pr_err("%s:dma_request_channel FAILS(Rx)\n", __func__);
+ dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n",
+ __func__);
dma_release_channel(priv->chan_tx);
+ priv->chan_tx = NULL;
return;
}
int count;
if (!tty) {
- pr_debug("%s:tty is busy now", __func__);
+ dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
return;
}
priv->tx_dma_use = 0;
priv->nent = 0;
kfree(priv->sg_tx_p);
- if (uart_circ_chars_pending(xmit))
- pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT);
+ pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT);
}
static int pop_tx(struct eg20t_port *priv, int size)
} while (!uart_circ_empty(xmit) && count < size);
pop_tx_end:
- pr_debug("%d characters. Remained %d characters. (%lu)\n",
+ dev_dbg(priv->port.dev, "%d characters. Remained %d characters.(%lu)\n",
count, size - count, jiffies);
return count;
int tx_empty;
if (!priv->start_tx) {
- pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies);
+ dev_info(priv->port.dev, "%s:Tx isn't started. (%lu)\n",
+ __func__, jiffies);
pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
priv->tx_empty = 1;
return 0;
int rem;
if (!priv->start_tx) {
- pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies);
+ dev_info(priv->port.dev, "%s:Tx isn't started. (%lu)\n",
+ __func__, jiffies);
+ pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
+ priv->tx_empty = 1;
+ return 0;
+ }
+
+ if (priv->tx_dma_use) {
+ dev_dbg(priv->port.dev, "%s:Tx is not completed. (%lu)\n",
+ __func__, jiffies);
pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
priv->tx_empty = 1;
return 0;
UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head,
xmit->tail, UART_XMIT_SIZE));
if (!bytes) {
+ dev_dbg(priv->port.dev, "%s 0 bytes return\n", __func__);
pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
uart_write_wakeup(port);
return 0;
rem = bytes;
}
+ dev_dbg(priv->port.dev, "%s num=%d size=%d rem=%d\n",
+ __func__, num, size, rem);
+
priv->tx_dma_use = 1;
priv->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
sg = priv->sg_tx_p;
nent = dma_map_sg(port->dev, sg, num, DMA_TO_DEVICE);
if (!nent) {
- pr_err("%s:dma_map_sg Failed\n", __func__);
+ dev_err(priv->port.dev, "%s:dma_map_sg Failed\n", __func__);
return 0;
}
priv->nent = nent;
priv->sg_tx_p, nent, DMA_TO_DEVICE,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
- pr_err("%s:device_prep_slave_sg Failed\n", __func__);
+ dev_err(priv->port.dev, "%s:device_prep_slave_sg Failed\n",
+ __func__);
return 0;
}
dma_sync_sg_for_device(port->dev, priv->sg_tx_p, nent, DMA_TO_DEVICE);
ret = PCH_UART_HANDLED_MS_INT;
break;
default: /* Never junp to this label */
- pr_err("%s:iid=%d (%lu)\n", __func__, iid, jiffies);
+ dev_err(priv->port.dev, "%s:iid=%d (%lu)\n", __func__,
+ iid, jiffies);
ret = -1;
break;
}
static void pch_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
u32 mcr = 0;
- unsigned int dat;
struct eg20t_port *priv = container_of(port, struct eg20t_port, port);
if (mctrl & TIOCM_DTR)
if (mctrl & TIOCM_LOOP)
mcr |= UART_MCR_LOOP;
- if (mctrl) {
- dat = pch_uart_get_mctrl(port);
- dat |= mcr;
- iowrite8(dat, priv->membase + UART_MCR);
- }
+ if (priv->mcr & UART_MCR_AFE)
+ mcr |= UART_MCR_AFE;
+
+ if (mctrl)
+ iowrite8(mcr, priv->membase + UART_MCR);
}
static void pch_uart_stop_tx(struct uart_port *port)
priv = container_of(port, struct eg20t_port, port);
- if (priv->use_dma)
- if (priv->tx_dma_use)
+ if (priv->use_dma) {
+ if (priv->tx_dma_use) {
+ dev_dbg(priv->port.dev, "%s : Tx DMA is NOT empty.\n",
+ __func__);
return;
+ }
+ }
priv->start_tx = 1;
pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT);
priv = container_of(port, struct eg20t_port, port);
priv->tx_empty = 1;
- port->uartclk = priv->base_baud;
+
+ if (port->uartclk)
+ priv->base_baud = port->uartclk;
+ else
+ port->uartclk = priv->base_baud;
+
pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
ret = pch_uart_hal_set_line(priv, default_baud,
PCH_UART_HAL_PARITY_NONE, PCH_UART_HAL_8BIT,
ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0,
PCH_UART_HAL_FIFO_DIS, PCH_UART_HAL_TRIGGER1);
if (ret)
- pr_err("pch_uart_hal_set_fifo Failed(ret=%d)\n", ret);
+ dev_err(priv->port.dev,
+ "pch_uart_hal_set_fifo Failed(ret=%d)\n", ret);
- if (priv->use_dma_flag)
- pch_free_dma(port);
+ pch_free_dma(port);
free_irq(priv->port.irq, priv);
}
} else {
parity = PCH_UART_HAL_PARITY_NONE;
}
+
+ /* Only UART0 has auto hardware flow function */
+ if ((termios->c_cflag & CRTSCTS) && (priv->fifo_size == 256))
+ priv->mcr |= UART_MCR_AFE;
+ else
+ priv->mcr &= ~UART_MCR_AFE;
+
termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
if (rtn)
goto out;
+ pch_uart_set_mctrl(&priv->port, priv->port.mctrl);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
priv = container_of(port, struct eg20t_port, port);
if (serinfo->flags & UPF_LOW_LATENCY) {
- pr_info("PCH UART : Use PIO Mode (without DMA)\n");
+ dev_info(priv->port.dev,
+ "PCH UART : Use PIO Mode (without DMA)\n");
priv->use_dma = 0;
serinfo->flags &= ~UPF_LOW_LATENCY;
} else {
#ifndef CONFIG_PCH_DMA
- pr_err("%s : PCH DMA is not Loaded.\n", __func__);
+ dev_err(priv->port.dev, "%s : PCH DMA is not Loaded.\n",
+ __func__);
return -EOPNOTSUPP;
#endif
priv->use_dma = 1;
priv->use_dma_flag = 1;
- pr_info("PCH UART : Use DMA Mode\n");
+ dev_info(priv->port.dev, "PCH UART : Use DMA Mode\n");
}
return 0;
unsigned int mapbase;
unsigned char *rxbuf;
int fifosize, base_baud;
- static int num;
- int port_type = id->driver_data;
+ int port_type;
+ struct pch_uart_driver_data *board;
+ const char *board_name;
+
+ board = &drv_dat[id->driver_data];
+ port_type = board->port_type;
priv = kzalloc(sizeof(struct eg20t_port), GFP_KERNEL);
if (priv == NULL)
if (!rxbuf)
goto init_port_free_txbuf;
+ base_baud = 1843200; /* 1.8432MHz */
+
+ /* quirk for CM-iTC board */
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+ if (board_name && strstr(board_name, "CM-iTC"))
+ base_baud = 192000000; /* 192.0MHz */
+
switch (port_type) {
case PORT_UNKNOWN:
fifosize = 256; /* EG20T/ML7213: UART0 */
- base_baud = 1843200; /* 1.8432MHz */
break;
case PORT_8250:
fifosize = 64; /* EG20T:UART1~3 ML7213: UART1~2*/
- base_baud = 1843200; /* 1.8432MHz */
break;
default:
dev_err(&pdev->dev, "Invalid Port Type(=%d)\n", port_type);
goto init_port_hal_free;
}
+ pci_enable_msi(pdev);
+ pci_set_master(pdev);
+
iobase = pci_resource_start(pdev, 0);
mapbase = pci_resource_start(pdev, 1);
priv->mapbase = mapbase;
priv->port.ops = &pch_uart_ops;
priv->port.flags = UPF_BOOT_AUTOCONF;
priv->port.fifosize = fifosize;
- priv->port.line = num++;
+ priv->port.line = board->line_no;
priv->trigger = PCH_UART_HAL_TRIGGER_M;
spin_lock_init(&priv->port.lock);
struct eg20t_port *priv;
priv = (struct eg20t_port *)pci_get_drvdata(pdev);
+
+ pci_disable_msi(pdev);
pch_uart_exit_port(priv);
pci_disable_device(pdev);
kfree(priv);
static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811),
- .driver_data = PCH_UART_8LINE},
+ .driver_data = pch_et20t_uart0},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812),
- .driver_data = PCH_UART_2LINE},
+ .driver_data = pch_et20t_uart1},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8813),
- .driver_data = PCH_UART_2LINE},
+ .driver_data = pch_et20t_uart2},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8814),
- .driver_data = PCH_UART_2LINE},
+ .driver_data = pch_et20t_uart3},
{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8027),
- .driver_data = PCH_UART_8LINE},
+ .driver_data = pch_ml7213_uart0},
{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8028),
- .driver_data = PCH_UART_2LINE},
+ .driver_data = pch_ml7213_uart1},
{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8029),
- .driver_data = PCH_UART_2LINE},
+ .driver_data = pch_ml7213_uart2},
+ {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x800C),
+ .driver_data = pch_ml7223_uart0},
+ {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x800D),
+ .driver_data = pch_ml7223_uart1},
+ {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8811),
+ .driver_data = pch_ml7831_uart0},
+ {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8812),
+ .driver_data = pch_ml7831_uart1},
{0,},
};
return ret;
probe_disable_device:
+ pci_disable_msi(pdev);
pci_disable_device(pdev);
probe_error:
return ret;