EG20T: Update PCH_UART driver to 2.6.36
PATCH v2 1/1 EG20T: Update PCH_UART driver to 2.6.36
Hi Alan,
I have modified for your indications.
(As to details, please refer previous my response mail) Please check below.
Signature · 20 lines
Thanks, Tomoya MORINAGA(OKI SEMICONDUCTOR CO., LTD.) --- UART driver of Intel EG20T(Topcliff) PCH Intel EG20T PCH is the platform controller hub that is going to be used in Intel's general embedded platform. All IO peripherals in Intel EG20T PCH are actually devices sitting on AMBA bus. Intel EG20T PCH has UART I/F. Using this I/F, it is able to access system devices connected to UART. Signed-off-by: Tomoya MORINAGA <[email protected]> --- drivers/serial/Kconfig | 8 + drivers/serial/Makefile | 1 + drivers/serial/pch_uart.c | 1462 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1471 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/pch_uart.c
drivers/serial/Kconfig +8 -0
@@ -1611,4 +1611,12 @@ config SERIAL_ALTERA_UART_CONSOLE help Enable a Altera UART port to be the system console. +config SERIAL_PCH_UART + tristate "Intel EG20T PCH UART" + select SERIAL_CORE + depends on PCI + help + This driver is for PCH(Platform controller Hub) UART of Intel EG20T + which is an IOH(Input/Output Hub) for x86 embedded processor. + Enabling PCH_DMA, this PCH UART works as DMA mode. endmenu
drivers/serial/Makefile +1 -0
@@ -88,3 +88,4 @@ obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o +obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
drivers/serial/pch_uart.c new file +1462 -0
1,462 lines not on this page · raw message
Signature · 1 line
1.6.0.6
Quoted 5 lines · On Fri, 12 Nov 2010 11:55:42 +0900 Tomoya MORINAGA…
Hi Alan,
I have modified for your indications.
(As to details, please refer previous my response mail) Please check below.
Thanks.. More comments below
I have looked at the locking and agree it looks right.
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#ifdef CONFIG_PCH_DMA
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
+#endif
You do not need this ifdef. Even if you are not using the DMA feature it will still compile. DMA/non-DMA needs to be runtime. See below
+enum {
+ PCH_UART_HAL_INVALID_PARAM = EINVAL,
+ PCH_UART_HAL_NOT_INITIALIZED = 256,
+ PCH_UART_HAL_NOT_REQUESTED,
+ PCH_UART_HAL_BASE_NOT_SET,
+ PCH_UART_HAL_INVALID_BAUD,
+ PCH_UART_HAL_INVALID_PARITY,
+ PCH_UART_HAL_INVALID_WLS,
+ PCH_UART_HAL_INVALID_FIFO_CLR,
+ PCH_UART_HAL_INVALID_DMAMODE,
+ PCH_UART_HAL_INVALID_FIFOSIZE,
+ PCH_UART_HAL_INVALID_TRIGGER,
+ PCH_UART_HAL_INVALID_STB,
+ PCH_UART_HAL_READ_ERROR,
+};
These should be replaced with ordinary Linux error codes in the functions.
+struct eg20t_port {
+ struct uart_port port;
+ int port_type;
+ void __iomem *membase;
+ resource_size_t mapbase;
+ unsigned int iobase;
+ struct pci_dev *pdev;
+ int fifo_size;
+ int base_baud;
+ int start_tx;
+ int start_rx;
+ int tx_empty;
+ int int_dis_flag;
+ int trigger;
+ int trigger_level;
+ struct pch_uart_buffer rxbuf;
+ unsigned int dmsr;
+ unsigned int fcr;
+#ifdef CONFIG_PCH_DMA
+ struct dma_async_tx_descriptor *desc_tx;
+ struct dma_async_tx_descriptor *desc_rx;
+ struct pch_dma_slave param_tx;
+ struct pch_dma_slave param_rx;
+ struct dma_chan *chan_tx;
+ struct dma_chan *chan_rx;
+ struct scatterlist sg_tx;
+ struct scatterlist sg_rx;
+ int tx_dma_use;
+ void *rx_buf_virt;
+ dma_addr_t rx_buf_dma;
+#endif
+};
+static unsigned int get_msr(struct eg20t_port *priv, void __iomem *base) +{
+ unsigned int msr = ioread8(base + PCH_UART_MSR);
+ priv->dmsr |= msr & PCH_UART_MSR_DELTA;
+
+ return msr;
msr should be a u8 ? Is there a reason priv->dmsr is unsigned int ?
+static void pch_uart_hal_enable_interrupt(struct eg20t_port *priv,
+ unsigned int flag)
+{
+ unsigned int ier = ioread8(priv->membase + PCH_UART_IER);
+ ier |= flag & PCH_UART_IER_MASK;
+ iowrite8(ier, priv->membase + PCH_UART_IER);
+}
Same for ier and fcr ?
+static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf,
+ int rx_size)
+{
+ int i;
+ unsigned int rbr, lsr;
+
+ lsr = ioread8(priv->membase + PCH_UART_LSR);
+ for (i = 0, lsr = ioread8(priv->membase + PCH_UART_LSR);
+ i < rx_size && lsr & PCH_UART_LSR_DR;
+ lsr = ioread8(priv->membase + PCH_UART_LSR)) {
+ rbr = ioread8(priv->membase + PCH_UART_RBR);
+ buf[i++] = (unsigned char)rbr;
+ }
Same again here - which would also lose the cast
+static int pch_uart_hal_get_line_status(struct eg20t_port *priv) +{
+ unsigned int lsr;
+ int ret;
+
+ lsr = ioread8(priv->membase + PCH_UART_LSR);
+ ret = (int)lsr;
And here
+static int push_rx(struct eg20t_port *priv, const unsigned char *buf,
+ int size)
+{
+ struct uart_port *port;
+ struct tty_struct *tty;
+ int sz, i, j;
+ int loop;
+ int pushed;
+
+ port = &priv->port;
+ tty = tty_port_tty_get(&port->state->port);
+ if (!tty) {
+ pr_info("%s:tty is busy now", __func__);
+ return -EBUSY;
+ }
Don't log this. It is a perfectly normal situation - no pr_info needed (pr_debug if testing perhaps)
+
+ for (pushed = 0, i = 0, loop = 1; (pushed < size) && loop;
+ pushed += sz, i++) {
+ sz = tty_insert_flip_string(tty, &buf[pushed], size - pushed);
+ for (j = 0; (j < 100000) && (sz == 0); j++) {
+ tty_flip_buffer_push(tty);
+ sz = tty_insert_flip_string(tty, &buf[pushed],
+ size - pushed);
+ }
+ if (sz == 0)
+ loop = 0;
+ }
This should not be needed. tty_insert_flip_string tries to insert as much as it can. Except when tty->low_latency is set, which requires the call is not from an interrupt handler the flip_buffer_push will queue data to the line discipline it will not make space.
+ room = tty_buffer_request_room(tty, size);
+
+ if (room < size)
+ dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
+ size - room);
+ if (!room)
+ return room;
+
+ for (i = 0; i < room; i++) {
+ tty_insert_flip_char(tty, ((u8 *)sg_virt(&priv->sg_rx))[i],
+ TTY_NORMAL);
+ }
You can replace all of that with a single call to tty_insert_flip_string ?
+ struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+ if (!tty) {
+ pr_info("%s:tty is busy now", __func__);
Again don't log this.
+static void pch_uart_send_xchar(struct uart_port *port, char ch) +{
+}
??
+ ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0,
+ fifo_size, priv->trigger);
Is this missing check intentional ?
+
+ ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, priv);
+
+ if (ret < 0)
+ return ret;
+static int pch_uart_verify_port(struct uart_port *port,
+ struct serial_struct *serinfo)
+{
+ return 0;
+}
As you don't support low latency you want to do
static int pch_uart_verify_port(struct uart_port *port,
struct serial_struct *serinfo)
{
if (serinfo->flags & UPF_LOW_LATENCY)
return -EOPNOTSUPP;
return 0;
}(A few other drivers also miss this)
What you could also consider doing is
static int pch_uart_verify_port(struct uart_port *port,
struct serial_struct *serinfo)
{
if (serinfo->flags & UPF_LOW_LATENCY)
use_pio_modes;
/* Avoid tty->low_latency being set as we do not support it */
serinfo->flags &= ~UPF_LOW_LATENCY;
} else
use_dma_modes;
return 0;
}I think that would make "setserial /dev/ttyPCH0 low_latency" select PIO and the reverse "setserial /dev/ttyPCH0 ^low_latency" select DMA mode but it would need testing to make sure that it is all that is needed.
Quoted 27 lines · Alan Cox
drivers/serial/pch_uart.c new file +7
-0+#include <linux/interrupt.h> +#include <linux/io.h> + +#ifdef CONFIG_PCH_DMA +#include <linux/dmaengine.h> +#include <linux/pch_dma.h> +#endif You do not need this ifdef. Even if you are not using the DMA feature it will still compile. DMA/non-DMA needs to be runtime. See below
drivers/serial/pch_uart.c new file +15
-0+enum { +PCH_UART_HAL_INVALID_PARAM =EINVAL , +PCH_UART_HAL_NOT_INITIALIZED =256 , +PCH_UART_HAL_NOT_REQUESTED , +PCH_UART_HAL_BASE_NOT_SET , +PCH_UART_HAL_INVALID_BAUD , +PCH_UART_HAL_INVALID_PARITY , +PCH_UART_HAL_INVALID_WLS , +PCH_UART_HAL_INVALID_FIFO_CLR , +PCH_UART_HAL_INVALID_DMAMODE , +PCH_UART_HAL_INVALID_FIFOSIZE , +PCH_UART_HAL_INVALID_TRIGGER , +PCH_UART_HAL_INVALID_STB , +PCH_UART_HAL_READ_ERROR , +};
Quoted 1 line
These should be replaced with ordinary Linux error codes in the functions.
I agree.
Quoted 41 lines
drivers/serial/pch_uart.c new file +38
-0+struct eg20t_port { +struct uart_port port; +int port_type; +void __iomem *membase; +resource_size_t mapbase; +unsigned int iobase; +struct pci_dev *pdev; +int fifo_size; +int base_baud; +int start_tx; +int start_rx; +int tx_empty; +int int_dis_flag; +int trigger; +int trigger_level; +struct pch_uart_buffer rxbuf; +unsigned int dmsr; +unsigned int fcr; +#ifdef CONFIG_PCH_DMA +struct dma_async_tx_descriptor *desc_tx; +struct dma_async_tx_descriptor *desc_rx; +struct pch_dma_slave param_tx; +struct pch_dma_slave param_rx; +struct dma_chan *chan_tx; +struct dma_chan *chan_rx; +struct scatterlist sg_tx; +struct scatterlist sg_rx; +int tx_dma_use; +void *rx_buf_virt; +dma_addr_t rx_buf_dma; +#endif +}; +static unsigned int get_msr (struct eg20t_port *priv,void __iomem *base) +{ +unsigned int msr =ioread8 (base +PCH_UART_MSR ); + priv->dmsr |= msr &PCH_UART_MSR_DELTA ; + +return msr;
Quoted 1 line
msr should be a u8 ? Is there a reason
priv->dmsris unsigned int ?
There is no reason.
I will modify.
Quoted 10 lines
drivers/serial/pch_uart.c new file +7
-0+static void pch_uart_hal_enable_interrupt (struct eg20t_port *priv, +unsigned int flag) +{ +unsigned int ier =ioread8 (priv->membase +PCH_UART_IER ); + ier |= flag &PCH_UART_IER_MASK ; +iowrite8 (ier, priv->membase +PCH_UART_IER ); +}Same for ier and fcr ?
I will modify.
Quoted 15 lines
drivers/serial/pch_uart.c new file +13
-0+static int pch_uart_hal_read (struct eg20t_port *priv,unsigned char *buf, +int rx_size) +{ +int i; +unsigned int rbr, lsr; + + lsr =ioread8 (priv->membase +PCH_UART_LSR ); +for (i =0 , lsr =ioread8 (priv->membase +PCH_UART_LSR ); + i < rx_size && lsr &PCH_UART_LSR_DR ; + lsr =ioread8 (priv->membase +PCH_UART_LSR )) { + rbr =ioread8 (priv->membase +PCH_UART_RBR ); + buf[i++] = (unsigned char )rbr; + }
Quoted 1 line
Same again here - which would also lose the cast
I will modify.
Quoted 10 lines
drivers/serial/pch_uart.c new file +7
-0+static int pch_uart_hal_get_line_status (struct eg20t_port *priv) +{ +unsigned int lsr; +int ret; + + lsr =ioread8 (priv->membase +PCH_UART_LSR ); + ret = (int )lsr;And here
I will.
Quoted 17 lines
drivers/serial/pch_uart.c new file +15
-0+static int push_rx (struct eg20t_port *priv,const unsigned char *buf, +int size) +{ +struct uart_port *port; +struct tty_struct *tty; +int sz, i, j; +int loop; +int pushed; + + port = &priv->port; + tty =tty_port_tty_get (&port->state->port); +if (!tty) { +pr_info ("%s:tty is busy now" , __func__); +return -EBUSY ; + }
Quoted 2 lines
Don't log this. It is a perfectly normal situation - no
pr_infoneeded (pr_debugif testing perhaps)
I agree.
I will modify.
Quoted 14 lines
drivers/serial/pch_uart.c new file +12
-0+ +for (pushed =0 , i =0 , loop =1 ; (pushed < size) && loop; + pushed += sz, i++) { + sz =tty_insert_flip_string (tty, &buf[pushed], size - pushed); +for (j =0 ; (j <100000 ) && (sz ==0 ); j++) { +tty_flip_buffer_push (tty); + sz =tty_insert_flip_string (tty, &buf[pushed], + size - pushed); + } +if (sz ==0 ) + loop =0 ; + }
Quoted 4 lines
This should not be needed.
tty_insert_flip_stringtries to insert as much as it can. Except whentty->low_latencyis set, which requires the call is not from an interrupt handler theflip_buffer_pushwill queue data to the line discipline it will not make space.
I will delete the above (if (sz == 0) ~)
Quoted 14 lines
+ room =
tty_buffer_request_room(tty, size);drivers/serial/pch_uart.c new file +11
-0+ +if (room < size) +dev_warn (port->dev,"Rx overrun: dropping %u bytes\n" , + size - room); +if (!room) +return room; + +for (i =0 ; i < room; i++) { +tty_insert_flip_char (tty, ((u8 *)sg_virt (&priv->sg_rx))[i], +TTY_NORMAL ); + }
Quoted 1 line
You can replace all of that with a single call to
tty_insert_flip_string?
I will replace.
Quoted 11 lines
+ struct
tty_struct*tty =tty_port_tty_get(&port->state->port);
+ if (!tty) {
+pr_info("%s:tty is busy now", __func__);Again don't log this.
drivers/serial/pch_uart.c new file +3
-0+static void pch_uart_send_xchar (struct uart_port *port,char ch) +{ +}
Quoted 1 line
??
I will delete.
Quoted 5 lines
+ ret = pch_uart_hal_set_fifo(priv,
PCH_UART_HAL_DMA_MODE0,
+fifo_size,priv->trigger);Is this missing check intentional ?
I will add checking processing of returned value.
Quoted 43 lines
drivers/serial/pch_uart.c new file +11
-0+ + ret =request_irq (priv->port.irq, pch_uart_interrupt,IRQF_SHARED , +KBUILD_MODNAME , priv); + +if (ret <0 ) +return ret; +static int pch_uart_verify_port (struct uart_port *port, +struct serial_struct *serinfo) +{ +return 0 ; +}As you don't support low latency you want to do
static int pch_uart_verify_port(struct
uart_port*port,
structserial_struct*serinfo)
{
if (serinfo->flags&UPF_LOW_LATENCY)
return-EOPNOTSUPP;
return 0;
}(A few other drivers also miss this)
What you could also consider doing is
static int pch_uart_verify_port(struct
uart_port*port,
structserial_struct*serinfo)
{
if (serinfo->flags&UPF_LOW_LATENCY)use_pio_modes;
/* Avoidtty->low_latencybeing set as we do not support it */serinfo->flags&= ~UPF_LOW_LATENCY;
} elseuse_dma_modes;
return 0;
}
Quoted 3 lines
I think that would make "setserial
/dev/ttyPCH0low_latency" select PIO and the reverse "setserial/dev/ttyPCH0^low_latency" select DMA mode but it would need testing to make sure that it is all that is needed.
I will modify like above so that it can switch w/wo DMA dynamically.
I have a question.
Does our driver care CONFIG_PCH_DMA configuration ?
Thanks,
Tomoya MORINAGA
OKI SEMICONDUCTOR CO., LTD.