Further serial driver bits
cover Further serial driver bits
Some cleanups for the 3110 driver and an initial release of the driver for the hsu high speed serial.
Again these stand alone, there are other bits for early printk support and the like to follow once the things they depend upon are integrated
---
Arjan van de Ven (2):
serial: fix wakup races in the mrst_max3110 driver
serial: replace open coded mutex with a real mutex in mrst_max3110.c
Feng Tang (2):
hsu: add a periodic timer to check dma rx channel
hsu: driver for Medfield High Speed UART device
drivers/serial/Kconfig | 9 drivers/serial/Makefile | 1 drivers/serial/mfd.c | 1532 +++++++++++++++++++++++++++++++++++++++++ drivers/serial/mrst_max3110.c | 61 +- include/linux/serial_core.h | 3 include/linux/serial_mfd.h | 47 + include/linux/serial_reg.h | 16 7 files changed, 1634 insertions(+), 35 deletions(-) create mode 100644 drivers/serial/mfd.c create mode 100644 include/linux/serial_mfd.h
PATCH 1/4 hsu: driver for Medfield High Speed UART device
From: Feng Tang <[email protected]>
This is a PCI & UART driver, which suppors both PIO and DMA mode UART operation. It has 3 identical UART ports and one internal DMA controller.
Current FW will export 4 pci devices for hsu: 3 uart ports and 1 dma controller, each has one IRQ line. And we need to discuss the device model, one PCI device covering whole HSU should be a better model, but there is a problem of how to export the 4 IRQs info
Current driver set the highest baud rate to 2746800bps, which is easy to scale down to 115200/230400.... To suport higher baud rate, we need add special process, change DLAB/DLH/PS/DIV/MUL registers all together.
921600 is the highest baud rate that has been tested with Bluetooth modem connected to HSU port 0. Will test more when there is right BT firmware.
---
drivers/serial/Kconfig | 9 drivers/serial/Makefile | 1 drivers/serial/mfd.c | 1488 +++++++++++++++++++++++++++++++++++++++++++ include/linux/serial_core.h | 3 include/linux/serial_mfd.h | 47 + include/linux/serial_reg.h | 16 6 files changed, 1564 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/mfd.c create mode 100644 include/linux/serial_mfd.h
drivers/serial/Kconfig +9 -0
@@ -715,6 +715,15 @@ config MRST_MAX3110_IRQ help This has to be enabled after Moorestown GPIO driver is loaded +config SERIAL_MFD_HSU + tristate "Medfield High Speed UART support" + select SERIAL_CORE + +config SERIAL_MFD_HSU_CONSOLE + boolean "Medfile HSU serial console support" + depends on SERIAL_MFD_HSU=y + select SERIAL_CORE_CONSOLE + config SERIAL_BFIN tristate "Blackfin serial port support" depends on BLACKFIN
drivers/serial/Makefile +1 -0
@@ -85,3 +85,4 @@ obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o 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
drivers/serial/mfd.c new file +1488 -0
1,488 lines not on this page · raw message
include/linux/serial_core.h +3 -0
@@ -186,6 +186,9 @@#define PORT_ALTERA_JTAGUART 91#define PORT_ALTERA_UART 92 +/* High Speed UART for Medfield */ +#define PORT_MFD 93 +#ifdef __KERNEL__#include <linux/compiler.h>
include/linux/serial_mfd.h new file +47 -0
47 lines not on this page · raw message
include/linux/serial_reg.h +16 -0
@@ -221,8 +221,24 @@#define UART_FCR_PXAR16 0x80/* receive FIFO threshold = 16 */ #define UART_FCR_PXAR32 0xc0/* receive FIFO threshold = 32 */ +/* +* Intel MID on-chip HSU (High Speed UART) defined bits +*/ +#define UART_FCR_HSU_64_1B 0x00/* receive FIFO treshold = 1 */ +#define UART_FCR_HSU_64_16B 0x40/* receive FIFO treshold = 16 */ +#define UART_FCR_HSU_64_32B 0x80/* receive FIFO treshold = 32 */ +#define UART_FCR_HSU_64_56B 0xc0/* receive FIFO treshold = 56 */ + +#define UART_FCR_HSU_16_1B 0x00/* receive FIFO treshold = 1 */ +#define UART_FCR_HSU_16_4B 0x40/* receive FIFO treshold = 4 */ +#define UART_FCR_HSU_16_8B 0x80/* receive FIFO treshold = 8 */ +#define UART_FCR_HSU_16_14B 0xc0/* receive FIFO treshold = 14 */ +#define UART_FCR_HSU_64B_FIFO 0x20/* chose 64 bytes FIFO */ +#define UART_FCR_HSU_16B_FIFO 0x00/* chose 16 bytes FIFO */ +#define UART_FCR_HALF_EMPT_TXI 0x00/* trigger TX_EMPT IRQ for half empty */ +#define UART_FCR_FULL_EMPT_TXI 0x08/* trigger TX_EMPT IRQ for full empty */ /* * These register definitions are for the 16C950
Re: [PATCH 1/4] hsu: driver for Medfield High Speed UART device
Quoted 20 lines · Alan Cox
From: Feng Tang <[email protected]>
This is a PCI & UART driver, which suppors both PIO and DMA mode UART operation. It has 3 identical UART ports and one internal DMA controller.
Current FW will export 4 pci devices for hsu: 3 uart ports and 1 dma controller, each has one IRQ line. And we need to discuss the device model, one PCI device covering whole HSU should be a better model, but there is a problem of how to export the 4 IRQs info
Current driver set the highest baud rate to 2746800bps, which is easy to scale down to 115200/230400.... To suport higher baud rate, we need add special process, change DLAB/DLH/PS/DIV/MUL registers all together.
921600 is the highest baud rate that has been tested with Bluetooth modem connected to HSU port 0. Will test more when there is right BT firmware.
Quoted 7 lines
Signed-off-by Feng Tang <[email protected]>Signed-off-by Alan Cox <[email protected]>---
drivers/serial/Kconfig | 9 drivers/serial/Makefile | 1 drivers/serial/mfd.c | 1488 +++++++++++++++++++++++++++++++++++++++++++
When building with this patch, I get the following build error:
CC [M] drivers/serial/mfd.o
drivers/serial/mfd.c: In function ‘serial_hsu_probe’:
drivers/serial/mfd.c:1338:22: error: ‘serial_hsu_console’ undeclared (first use in this function)
So I can't take this, or the second patch (which doesn't fix this problem).
Care to redo them so that the build is not broken?
thanks,
greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH 2/4 hsu: add a periodic timer to check dma rx channel
In reply to Alan CoxFrom: Feng Tang <[email protected]>
A general problem for uart rx dma channel is you never know when and how much data will be received, so usually preset it a DMA descriptor with a big size, and rely on DMA RX timeout IRQ to know there is some data in rx channel.
For a RX data size of multiple of MOTSR, there will be no timeout IRQ issued, thus OS will never be notified about that.
This is a work around for that, current timer frequency is 5 times per second, it should vary according to the baud rate
When future silicon version fix the problem, this workaround need be removed
---
drivers/serial/mfd.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 47 insertions(+), 3 deletions(-)
drivers/serial/mfd.c +47 -3
@@ -64,6 +64,8 @@#define mfd_readl (obj, offset) readl(obj->reg + offset)#define mfd_writel (obj, offset, val) writel(val, obj->reg + offset) +#define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10) +struct hsu_dma_buffer {u8 *buf;dma_addr_t dma_addr;
@@ -75,7 +77,8 @@ struct hsu_dma_chan {u32 id;u32 dirt;/* to or from device */ struct uart_hsu_port *uport; -void __iomem *reg; +void __iomem *reg; +struct timer_list rx_timer;/* only needed by RX channel */ };struct uart_hsu_port {
@@ -377,6 +380,8 @@ void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ );chan_writel (rxc,HSU_CH_CR ,0x3 ); + +mod_timer (&rxc->rx_timer, jiffies +HSU_DMA_TIMEOUT_CHECK_FREQ ); }/* Protected by spin_lock_irqsave(port->lock) */
@@ -437,8 +442,13 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)/* We can use 2 ways to calc the actual transfer len */ count =chan_readl (chan,HSU_CH_D0SAR ) - dbuf->dma_addr; -if (!count) +if (!count) { +/* restart the channel before we leave */ +chan_writel (chan,HSU_CH_CR ,0x3 );return ; + } + +del_timer (&chan->rx_timer);dma_sync_single_for_cpu (port->dev, dbuf->dma_addr, dbuf->dma_size,DMA_FROM_DEVICE );
@@ -463,9 +473,12 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) | (0x1 << 16) | (0x1 <<24 )/* timeout bit, see HSU Errata 1 */ ); +tty_flip_buffer_push (tty); +chan_writel (chan,HSU_CH_CR ,0x3 ); + chan->rx_timer.expires = jiffies +HSU_DMA_TIMEOUT_CHECK_FREQ ; +add_timer (&chan->rx_timer); -tty_flip_buffer_push (tty); }static void serial_hsu_stop_rx (struct uart_port *port)
@@ -893,6 +906,8 @@ static void serial_hsu_shutdown(struct uart_port *port) container_of(port, struct uart_hsu_port, port);unsigned long flags; +del_timer_sync (&up->rxc->rx_timer); +/* Disable interrupts from this port */ up->ier =0 ;serial_out (up,UART_IER ,0 );
@@ -1348,6 +1363,28 @@ err_disable:return ret; } +static void hsu_dma_rx_timeout (unsigned long data) +{ +struct hsu_dma_chan *chan = (void *)data; +struct uart_hsu_port *up = chan->uport; +struct hsu_dma_buffer *dbuf = &up->rxbuf; +int count =0 ; +unsigned long flags; + +spin_lock_irqsave (&up->port.lock, flags); + + count =chan_readl (chan,HSU_CH_D0SAR ) - dbuf->dma_addr; + +if (!count) { +mod_timer (&chan->rx_timer, jiffies +HSU_DMA_TIMEOUT_CHECK_FREQ ); + goto exit; + } + +hsu_dma_rx (up,0 ); +exit: +spin_unlock_irqrestore (&up->port.lock, flags); +} +static void hsu_global_init (void ) {struct hsu_port *hsu;
@@ -1409,6 +1446,13 @@ static void hsu_global_init(void) dchan->uport = &hsu->port[i/2 ]; dchan->reg = hsu->reg +HSU_DMA_CHANS_REG_OFFSET + i *HSU_DMA_CHANS_REG_LENGTH ; + +/* Work around for RX */ +if (dchan->dirt ==DMA_FROM_DEVICE ) { +init_timer (&dchan->rx_timer); + dchan->rx_timer.function = hsu_dma_rx_timeout; + dchan->rx_timer.data = (unsigned long )dchan; + } dchan++; }
PATCH 3/4 serial: replace open coded mutex with a real mutex in mrst_max3110.c
In reply to Alan CoxFrom: Arjan van de Ven <[email protected]>
The mrst_max3110.c driver uses an open coded, non atomic variable to create exclusion between two of its worker threads. More than that, while the main thread does a proper set-work-clear sequence, the other thread only does a test, with the result that no actual exclusion is happening.
this patch replaces this open coded variable with a proper mutex
in addition, the 'lock' spinlock is removed from the per adapter structure, the lock was only ever initialized but never used
---
drivers/serial/mrst_max3110.c | 15 ++++++++------- 1 files changed, 8 insertions(+), 7 deletions(-)
drivers/serial/mrst_max3110.c +8 -7
@@ -56,8 +56,7 @@ struct uart_max3110 {wait_queue_head_t wq;struct task_struct *main_thread;struct task_struct *read_thread; -int mthread_up; -spinlock_t lock; +struct mutex thread_mutex;;u32 baud;u16 cur_conf;
@@ -397,7 +396,8 @@ static int max3110_main_thread(void *_max)atomic_read (&max->con_tx_need) || atomic_read(&max->uart_tx_need)) || kthread_should_stop()); - max->mthread_up =1 ; + +mutex_lock (&max->thread_mutex);#ifdef CONFIG_MRST_MAX3110_IRQ if (atomic_read (&max->irq_pending)) {
@@ -417,7 +417,7 @@ static int max3110_main_thread(void *_max)transmit_char (max);atomic_set (&max->uart_tx_need,0 ); } - max->mthread_up =0 ; +mutex_unlock (&max->thread_mutex); }while (!kthread_should_stop ());return ret;
@@ -444,8 +444,9 @@ static int max3110_read_thread(void *_max)pr_info (PR_FMT "start read thread\n" ); do { - if (!max->mthread_up) - max3110_console_receive(max); + mutex_lock(&max->thread_mutex); + max3110_console_receive(max); + mutex_unlock(&max->thread_mutex); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ / 20);
@@ -745,7 +746,7 @@ static int serial_m3110_probe(struct spi_device *spi) max->name = spi->modalias;/* use spi name as the name */ max->irq = (u16 )spi->irq; -spin_lock_init (&max->lock); +mutex_init (&max->thread_mutex); max->word_7bits =0 ; max->parity =0 ;
PATCH 4/4 serial: fix wakup races in the mrst_max3110 driver
In reply to Alan CoxFrom: Arjan van de Ven <[email protected]>
The mrst_max3110 driver had a set of unsafe wakeup sequences along the following line:
if (!atomic_read(&foo)) {
atomic_set(&foo, 1);
wake_up(worker_thread);
}
and the worker thread would do
if (atomic_read(&foo)) {
do_work();
atomic_set(&foo, 0);
}
which can result in various missed wakups due to test-then-set races, as well as due to clear-after-work instead of clear-before-work.
This patch fixes these races by using the proper bit test-and-set operations, and by doing clear-before-work.
---
drivers/serial/mrst_max3110.c | 46 ++++++++++++++++------------------------- 1 files changed, 18 insertions(+), 28 deletions(-)
drivers/serial/mrst_max3110.c +18 -28
@@ -48,6 +48,10 @@#define PR_FMT "mrst_max3110: " +#define UART_TX_NEEDED 1 +#define CON_TX_NEEDED 2 +#define BIT_IRQ_PENDING 3 +struct uart_max3110 {struct uart_port port;struct spi_device *spi;
@@ -63,15 +67,13 @@ struct uart_max3110 {u8 clock;u8 parity, word_7bits; -atomic_t uart_tx_need; +unsigned long uart_flags;/* console related */ struct circ_buf con_xmit; -atomic_t con_tx_need;/* irq related */ u16 irq; -atomic_t irq_pending; };/* global data structure, may need be removed */
@@ -176,10 +178,9 @@ static void serial_m3110_con_putchar(struct uart_port *port, int ch) xmit->head = (xmit->head +1 ) & (PAGE_SIZE -1 ); } -if (!atomic_read (&max->con_tx_need)) { -atomic_set (&max->con_tx_need,1 ); + +if (!test_and_set_bit (CON_TX_NEEDED , &max->uart_flags))wake_up_process (max->main_thread); - } }/*
@@ -318,10 +319,8 @@ static void serial_m3110_start_tx(struct uart_port *port)struct uart_max3110 *max = container_of(port, struct uart_max3110, port); -if (!atomic_read (&max->uart_tx_need)) { -atomic_set (&max->uart_tx_need,1 ); +if (!test_and_set_bit (UART_TX_NEEDED , &max->uart_flags))wake_up_process (max->main_thread); - } }static void receive_chars (struct uart_max3110 *max,unsigned char *str,int len)
@@ -392,32 +391,23 @@ static int max3110_main_thread(void *_max)pr_info (PR_FMT "start main thread\n" );do { -wait_event_interruptible (*wq, (atomic_read (&max->irq_pending) || -atomic_read (&max->con_tx_need) || -atomic_read (&max->uart_tx_need)) || -kthread_should_stop ()); +wait_event_interruptible (*wq, max->uart_flags ||kthread_should_stop ());mutex_lock (&max->thread_mutex); -#ifdef CONFIG_MRST_MAX3110_IRQ -if (atomic_read (&max->irq_pending)) { +if (test_and_clear_bit (BIT_IRQ_PENDING , &max->uart_flags))max3110_console_receive (max); -atomic_set (&max->irq_pending,0 ); - } -#endif /* first handle console output */ -if (atomic_read (&max->con_tx_need)) { +if (test_and_clear_bit (CON_TX_NEEDED , &max->uart_flags))send_circ_buf (max, xmit); -atomic_set (&max->con_tx_need,0 ); - }/* handle uart output */ -if (atomic_read (&max->uart_tx_need)) { +if (test_and_clear_bit (UART_TX_NEEDED , &max->uart_flags))transmit_char (max); -atomic_set (&max->uart_tx_need,0 ); - } +mutex_unlock (&max->thread_mutex); + }while (!kthread_should_stop ());return ret;
@@ -430,10 +420,9 @@ static irqreturn_t serial_m3110_irq(int irq, void *dev_id)/* max3110's irq is a falling edge, not level triggered, * so no need to disable the irq */ -if (!atomic_read (&max->irq_pending)) { -atomic_inc (&max->irq_pending); +if (!test_and_set_bit (BIT_IRQ_PENDING , &max->uart_flags))wake_up_process (max->main_thread); - } + return IRQ_HANDLED; }#else
@@ -753,7 +742,8 @@ static int serial_m3110_probe(struct spi_device *spi) max->baud =0 ; max->cur_conf =0 ; -atomic_set (&max->irq_pending,0 ); + max->uart_flags =0 ; +/* Check if reading configuration register returns something sane */ res =RC_TAG ;