2010-11-17 03:55:54 +03:00
/*
2011-10-28 04:38:50 +04:00
* Copyright ( C ) 2011 LAPIS Semiconductor Co . , Ltd .
2010-11-17 03:55:54 +03:00
*
* 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
* the Free Software Foundation ; version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
2013-01-19 13:52:11 +04:00
# if defined(CONFIG_SERIAL_PCH_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
# define SUPPORT_SYSRQ
# endif
2011-05-26 12:41:17 +04:00
# include <linux/kernel.h>
2010-11-17 03:55:54 +03:00
# include <linux/serial_reg.h>
2011-05-25 04:13:44 +04:00
# include <linux/slab.h>
2010-11-17 03:55:54 +03:00
# include <linux/module.h>
# include <linux/pci.h>
2013-01-19 13:52:11 +04:00
# include <linux/console.h>
2010-11-17 03:55:54 +03:00
# include <linux/serial_core.h>
2011-09-01 18:20:57 +04:00
# include <linux/tty.h>
# include <linux/tty_flip.h>
2010-11-17 03:55:54 +03:00
# include <linux/interrupt.h>
# include <linux/io.h>
2011-03-10 16:14:00 +03:00
# include <linux/dmi.h>
2011-11-16 03:04:07 +04:00
# include <linux/nmi.h>
# include <linux/delay.h>
2010-11-17 03:55:54 +03:00
2012-02-06 13:24:43 +04:00
# include <linux/debugfs.h>
2010-11-17 03:55:54 +03:00
# include <linux/dmaengine.h>
# include <linux/pch_dma.h>
enum {
PCH_UART_HANDLED_RX_INT_SHIFT ,
PCH_UART_HANDLED_TX_INT_SHIFT ,
PCH_UART_HANDLED_RX_ERR_INT_SHIFT ,
PCH_UART_HANDLED_RX_TRG_INT_SHIFT ,
PCH_UART_HANDLED_MS_INT_SHIFT ,
2012-03-26 09:43:05 +04:00
PCH_UART_HANDLED_LS_INT_SHIFT ,
2010-11-17 03:55:54 +03:00
} ;
enum {
PCH_UART_8LINE ,
PCH_UART_2LINE ,
} ;
# define PCH_UART_DRIVER_DEVICE "ttyPCH"
2011-01-28 12:00:01 +03:00
/* Set the max number of UART port
* Intel EG20T PCH : 4 port
2011-10-28 04:38:50 +04:00
* LAPIS Semiconductor ML7213 IOH : 3 port
* LAPIS Semiconductor ML7223 IOH : 2 port
2011-01-28 12:00:01 +03:00
*/
# define PCH_UART_NR 4
2010-11-17 03:55:54 +03:00
# define PCH_UART_HANDLED_RX_INT (1<<((PCH_UART_HANDLED_RX_INT_SHIFT)<<1))
# define PCH_UART_HANDLED_TX_INT (1<<((PCH_UART_HANDLED_TX_INT_SHIFT)<<1))
# define PCH_UART_HANDLED_RX_ERR_INT (1<<((\
PCH_UART_HANDLED_RX_ERR_INT_SHIFT ) < < 1 ) )
# define PCH_UART_HANDLED_RX_TRG_INT (1<<((\
PCH_UART_HANDLED_RX_TRG_INT_SHIFT ) < < 1 ) )
# define PCH_UART_HANDLED_MS_INT (1<<((PCH_UART_HANDLED_MS_INT_SHIFT)<<1))
2012-03-26 09:43:05 +04:00
# define PCH_UART_HANDLED_LS_INT (1<<((PCH_UART_HANDLED_LS_INT_SHIFT)<<1))
2010-11-17 03:55:54 +03:00
# define PCH_UART_RBR 0x00
# define PCH_UART_THR 0x00
# define PCH_UART_IER_MASK (PCH_UART_IER_ERBFI|PCH_UART_IER_ETBEI|\
PCH_UART_IER_ELSI | PCH_UART_IER_EDSSI )
# define PCH_UART_IER_ERBFI 0x00000001
# define PCH_UART_IER_ETBEI 0x00000002
# define PCH_UART_IER_ELSI 0x00000004
# define PCH_UART_IER_EDSSI 0x00000008
# define PCH_UART_IIR_IP 0x00000001
# define PCH_UART_IIR_IID 0x00000006
# define PCH_UART_IIR_MSI 0x00000000
# define PCH_UART_IIR_TRI 0x00000002
# define PCH_UART_IIR_RRI 0x00000004
# define PCH_UART_IIR_REI 0x00000006
# define PCH_UART_IIR_TOI 0x00000008
# define PCH_UART_IIR_FIFO256 0x00000020
# define PCH_UART_IIR_FIFO64 PCH_UART_IIR_FIFO256
# define PCH_UART_IIR_FE 0x000000C0
# define PCH_UART_FCR_FIFOE 0x00000001
# define PCH_UART_FCR_RFR 0x00000002
# define PCH_UART_FCR_TFR 0x00000004
# define PCH_UART_FCR_DMS 0x00000008
# define PCH_UART_FCR_FIFO256 0x00000020
# define PCH_UART_FCR_RFTL 0x000000C0
# define PCH_UART_FCR_RFTL1 0x00000000
# define PCH_UART_FCR_RFTL64 0x00000040
# define PCH_UART_FCR_RFTL128 0x00000080
# define PCH_UART_FCR_RFTL224 0x000000C0
# define PCH_UART_FCR_RFTL16 PCH_UART_FCR_RFTL64
# define PCH_UART_FCR_RFTL32 PCH_UART_FCR_RFTL128
# define PCH_UART_FCR_RFTL56 PCH_UART_FCR_RFTL224
# define PCH_UART_FCR_RFTL4 PCH_UART_FCR_RFTL64
# define PCH_UART_FCR_RFTL8 PCH_UART_FCR_RFTL128
# define PCH_UART_FCR_RFTL14 PCH_UART_FCR_RFTL224
# define PCH_UART_FCR_RFTL_SHIFT 6
# define PCH_UART_LCR_WLS 0x00000003
# define PCH_UART_LCR_STB 0x00000004
# define PCH_UART_LCR_PEN 0x00000008
# define PCH_UART_LCR_EPS 0x00000010
# define PCH_UART_LCR_SP 0x00000020
# define PCH_UART_LCR_SB 0x00000040
# define PCH_UART_LCR_DLAB 0x00000080
# define PCH_UART_LCR_NP 0x00000000
# define PCH_UART_LCR_OP PCH_UART_LCR_PEN
# define PCH_UART_LCR_EP (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS)
# define PCH_UART_LCR_1P (PCH_UART_LCR_PEN | PCH_UART_LCR_SP)
# define PCH_UART_LCR_0P (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS |\
PCH_UART_LCR_SP )
# define PCH_UART_LCR_5BIT 0x00000000
# define PCH_UART_LCR_6BIT 0x00000001
# define PCH_UART_LCR_7BIT 0x00000002
# define PCH_UART_LCR_8BIT 0x00000003
# define PCH_UART_MCR_DTR 0x00000001
# define PCH_UART_MCR_RTS 0x00000002
# define PCH_UART_MCR_OUT 0x0000000C
# define PCH_UART_MCR_LOOP 0x00000010
# define PCH_UART_MCR_AFE 0x00000020
# define PCH_UART_LSR_DR 0x00000001
# define PCH_UART_LSR_ERR (1<<7)
# define PCH_UART_MSR_DCTS 0x00000001
# define PCH_UART_MSR_DDSR 0x00000002
# define PCH_UART_MSR_TERI 0x00000004
# define PCH_UART_MSR_DDCD 0x00000008
# define PCH_UART_MSR_CTS 0x00000010
# define PCH_UART_MSR_DSR 0x00000020
# define PCH_UART_MSR_RI 0x00000040
# define PCH_UART_MSR_DCD 0x00000080
# define PCH_UART_MSR_DELTA (PCH_UART_MSR_DCTS | PCH_UART_MSR_DDSR |\
PCH_UART_MSR_TERI | PCH_UART_MSR_DDCD )
# define PCH_UART_DLL 0x00
# define PCH_UART_DLM 0x01
2012-02-06 13:24:43 +04:00
# define PCH_UART_BRCSR 0x0E
2010-11-17 03:55:54 +03:00
# 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)
# define PCH_UART_IID_THRE (PCH_UART_IIR_TRI)
# define PCH_UART_IID_MS (PCH_UART_IIR_MSI)
# define PCH_UART_HAL_PARITY_NONE (PCH_UART_LCR_NP)
# define PCH_UART_HAL_PARITY_ODD (PCH_UART_LCR_OP)
# define PCH_UART_HAL_PARITY_EVEN (PCH_UART_LCR_EP)
# define PCH_UART_HAL_PARITY_FIX1 (PCH_UART_LCR_1P)
# define PCH_UART_HAL_PARITY_FIX0 (PCH_UART_LCR_0P)
# define PCH_UART_HAL_5BIT (PCH_UART_LCR_5BIT)
# define PCH_UART_HAL_6BIT (PCH_UART_LCR_6BIT)
# define PCH_UART_HAL_7BIT (PCH_UART_LCR_7BIT)
# define PCH_UART_HAL_8BIT (PCH_UART_LCR_8BIT)
# define PCH_UART_HAL_STB1 0
# define PCH_UART_HAL_STB2 (PCH_UART_LCR_STB)
# define PCH_UART_HAL_CLR_TX_FIFO (PCH_UART_FCR_TFR)
# define PCH_UART_HAL_CLR_RX_FIFO (PCH_UART_FCR_RFR)
# define PCH_UART_HAL_CLR_ALL_FIFO (PCH_UART_HAL_CLR_TX_FIFO | \
PCH_UART_HAL_CLR_RX_FIFO )
# define PCH_UART_HAL_DMA_MODE0 0
# define PCH_UART_HAL_FIFO_DIS 0
# define PCH_UART_HAL_FIFO16 (PCH_UART_FCR_FIFOE)
# define PCH_UART_HAL_FIFO256 (PCH_UART_FCR_FIFOE | \
PCH_UART_FCR_FIFO256 )
# define PCH_UART_HAL_FIFO64 (PCH_UART_HAL_FIFO256)
# define PCH_UART_HAL_TRIGGER1 (PCH_UART_FCR_RFTL1)
# define PCH_UART_HAL_TRIGGER64 (PCH_UART_FCR_RFTL64)
# define PCH_UART_HAL_TRIGGER128 (PCH_UART_FCR_RFTL128)
# define PCH_UART_HAL_TRIGGER224 (PCH_UART_FCR_RFTL224)
# define PCH_UART_HAL_TRIGGER16 (PCH_UART_FCR_RFTL16)
# define PCH_UART_HAL_TRIGGER32 (PCH_UART_FCR_RFTL32)
# define PCH_UART_HAL_TRIGGER56 (PCH_UART_FCR_RFTL56)
# define PCH_UART_HAL_TRIGGER4 (PCH_UART_FCR_RFTL4)
# define PCH_UART_HAL_TRIGGER8 (PCH_UART_FCR_RFTL8)
# define PCH_UART_HAL_TRIGGER14 (PCH_UART_FCR_RFTL14)
# define PCH_UART_HAL_TRIGGER_L (PCH_UART_FCR_RFTL64)
# define PCH_UART_HAL_TRIGGER_M (PCH_UART_FCR_RFTL128)
# define PCH_UART_HAL_TRIGGER_H (PCH_UART_FCR_RFTL224)
# define PCH_UART_HAL_RX_INT (PCH_UART_IER_ERBFI)
# define PCH_UART_HAL_TX_INT (PCH_UART_IER_ETBEI)
# define PCH_UART_HAL_RX_ERR_INT (PCH_UART_IER_ELSI)
# define PCH_UART_HAL_MS_INT (PCH_UART_IER_EDSSI)
# define PCH_UART_HAL_ALL_INT (PCH_UART_IER_MASK)
# define PCH_UART_HAL_DTR (PCH_UART_MCR_DTR)
# define PCH_UART_HAL_RTS (PCH_UART_MCR_RTS)
# define PCH_UART_HAL_OUT (PCH_UART_MCR_OUT)
# define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP)
# define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE)
2011-01-28 12:00:01 +03:00
# define PCI_VENDOR_ID_ROHM 0x10DB
2011-11-16 03:04:07 +04:00
# define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
2012-03-09 21:51:49 +04:00
# define DEFAULT_UARTCLK 1843200 /* 1.8432 MHz */
# define CMITC_UARTCLK 192000000 /* 192.0000 MHz */
# define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */
# define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */
2012-03-23 14:06:37 +04:00
# define NTC1_UARTCLK 64000000 /* 64.0000 MHz */
2013-06-26 05:53:22 +04:00
# define MINNOW_UARTCLK 50000000 /* 50.0000 MHz */
2011-11-16 03:04:07 +04:00
2010-11-17 03:55:54 +03:00
struct pch_uart_buffer {
unsigned char * buf ;
int size ;
} ;
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 ;
2013-07-30 02:15:07 +04:00
unsigned int uartclk ;
2010-11-17 03:55:54 +03:00
int start_tx ;
int start_rx ;
int tx_empty ;
int trigger ;
int trigger_level ;
struct pch_uart_buffer rxbuf ;
unsigned int dmsr ;
unsigned int fcr ;
2011-02-23 04:03:17 +03:00
unsigned int mcr ;
2010-11-17 03:55:54 +03:00
unsigned int use_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 ;
2011-02-23 04:03:12 +03:00
struct scatterlist * sg_tx_p ;
int nent ;
2010-11-17 03:55:54 +03:00
struct scatterlist sg_rx ;
int tx_dma_use ;
void * rx_buf_virt ;
dma_addr_t rx_buf_dma ;
2012-02-06 13:24:43 +04:00
struct dentry * debugfs ;
2014-03-25 17:05:08 +04:00
# define IRQ_NAME_SIZE 17
char irq_name [ IRQ_NAME_SIZE ] ;
2012-06-20 01:00:18 +04:00
/* protect the eg20t_port private structure and io access to membase */
spinlock_t lock ;
2010-11-17 03:55:54 +03:00
} ;
2011-02-23 04:03:19 +03:00
/**
* 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 ,
2011-05-09 12:25:20 +04:00
pch_ml7223_uart0 ,
pch_ml7223_uart1 ,
2011-10-28 04:38:49 +04:00
pch_ml7831_uart0 ,
pch_ml7831_uart1 ,
2011-02-23 04:03:19 +03:00
} ;
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 } ,
2011-05-09 12:25:20 +04:00
[ pch_ml7223_uart0 ] = { PCH_UART_8LINE , 0 } ,
[ pch_ml7223_uart1 ] = { PCH_UART_2LINE , 1 } ,
2011-10-28 04:38:49 +04:00
[ pch_ml7831_uart0 ] = { PCH_UART_8LINE , 0 } ,
[ pch_ml7831_uart1 ] = { PCH_UART_2LINE , 1 } ,
2011-02-23 04:03:19 +03:00
} ;
2011-11-16 03:04:07 +04:00
# ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
static struct eg20t_port * pch_uart_ports [ PCH_UART_NR ] ;
# endif
2010-11-17 03:55:54 +03:00
static unsigned int default_baud = 9600 ;
2012-03-09 21:51:50 +04:00
static unsigned int user_uartclk = 0 ;
2010-11-17 03:55:54 +03:00
static const int trigger_level_256 [ 4 ] = { 1 , 64 , 128 , 224 } ;
static const int trigger_level_64 [ 4 ] = { 1 , 16 , 32 , 56 } ;
static const int trigger_level_16 [ 4 ] = { 1 , 4 , 8 , 14 } ;
static const int trigger_level_1 [ 4 ] = { 1 , 1 , 1 , 1 } ;
2012-02-06 13:24:43 +04:00
# ifdef CONFIG_DEBUG_FS
# define PCH_REGS_BUFSIZE 1024
2012-04-06 01:25:11 +04:00
2012-02-06 13:24:43 +04:00
static ssize_t port_show_regs ( struct file * file , char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct eg20t_port * priv = file - > private_data ;
char * buf ;
u32 len = 0 ;
ssize_t ret ;
unsigned char lcr ;
buf = kzalloc ( PCH_REGS_BUFSIZE , GFP_KERNEL ) ;
if ( ! buf )
return 0 ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" PCH EG20T port[%d] regs: \n " , priv - > port . line ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" ================================= \n " ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" IER: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_IER ) ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" IIR: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_IIR ) ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" LCR: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_LCR ) ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" MCR: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_MCR ) ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" LSR: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_LSR ) ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" MSR: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_MSR ) ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" BRCSR: \t 0x%02x \n " ,
ioread8 ( priv - > membase + PCH_UART_BRCSR ) ) ;
lcr = ioread8 ( priv - > membase + UART_LCR ) ;
iowrite8 ( PCH_UART_LCR_DLAB , priv - > membase + UART_LCR ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" DLL: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_DLL ) ) ;
len + = snprintf ( buf + len , PCH_REGS_BUFSIZE - len ,
" DLM: \t 0x%02x \n " , ioread8 ( priv - > membase + UART_DLM ) ) ;
iowrite8 ( lcr , priv - > membase + UART_LCR ) ;
if ( len > PCH_REGS_BUFSIZE )
len = PCH_REGS_BUFSIZE ;
ret = simple_read_from_buffer ( user_buf , count , ppos , buf , len ) ;
kfree ( buf ) ;
return ret ;
}
static const struct file_operations port_regs_ops = {
. owner = THIS_MODULE ,
2012-04-06 01:25:11 +04:00
. open = simple_open ,
2012-02-06 13:24:43 +04:00
. read = port_show_regs ,
. llseek = default_llseek ,
} ;
# endif /* CONFIG_DEBUG_FS */
2013-07-29 20:58:14 +04:00
static struct dmi_system_id pch_uart_dmi_table [ ] = {
2013-07-13 04:58:05 +04:00
{
. ident = " CM-iTC " ,
{
DMI_MATCH ( DMI_BOARD_NAME , " CM-iTC " ) ,
} ,
( void * ) CMITC_UARTCLK ,
} ,
{
. ident = " FRI2 " ,
{
DMI_MATCH ( DMI_BIOS_VERSION , " FRI2 " ) ,
} ,
( void * ) FRI2_64_UARTCLK ,
} ,
{
. ident = " Fish River Island II " ,
{
DMI_MATCH ( DMI_PRODUCT_NAME , " Fish River Island II " ) ,
} ,
( void * ) FRI2_48_UARTCLK ,
} ,
{
. ident = " COMe-mTT " ,
{
DMI_MATCH ( DMI_BOARD_NAME , " COMe-mTT " ) ,
} ,
( void * ) NTC1_UARTCLK ,
} ,
{
. ident = " nanoETXexpress-TT " ,
{
DMI_MATCH ( DMI_BOARD_NAME , " nanoETXexpress-TT " ) ,
} ,
( void * ) NTC1_UARTCLK ,
} ,
{
. ident = " MinnowBoard " ,
{
DMI_MATCH ( DMI_BOARD_NAME , " MinnowBoard " ) ,
} ,
( void * ) MINNOW_UARTCLK ,
} ,
} ;
2012-03-09 21:51:49 +04:00
/* Return UART clock, checking for board specific clocks. */
2013-07-30 02:15:07 +04:00
static unsigned int pch_uart_get_uartclk ( void )
2012-03-09 21:51:49 +04:00
{
2013-07-13 04:58:05 +04:00
const struct dmi_system_id * d ;
2012-03-09 21:51:49 +04:00
2012-03-09 21:51:50 +04:00
if ( user_uartclk )
return user_uartclk ;
2013-07-13 04:58:05 +04:00
d = dmi_first_match ( pch_uart_dmi_table ) ;
if ( d )
2013-07-30 02:15:07 +04:00
return ( unsigned long ) d - > driver_data ;
2012-03-23 14:06:37 +04:00
2012-03-09 21:51:49 +04:00
return DEFAULT_UARTCLK ;
}
2010-11-17 03:55:54 +03:00
static void pch_uart_hal_enable_interrupt ( struct eg20t_port * priv ,
unsigned int flag )
{
u8 ier = ioread8 ( priv - > membase + UART_IER ) ;
ier | = flag & PCH_UART_IER_MASK ;
iowrite8 ( ier , priv - > membase + UART_IER ) ;
}
static void pch_uart_hal_disable_interrupt ( struct eg20t_port * priv ,
unsigned int flag )
{
u8 ier = ioread8 ( priv - > membase + UART_IER ) ;
ier & = ~ ( flag & PCH_UART_IER_MASK ) ;
iowrite8 ( ier , priv - > membase + UART_IER ) ;
}
2013-07-30 02:15:07 +04:00
static int pch_uart_hal_set_line ( struct eg20t_port * priv , unsigned int baud ,
2010-11-17 03:55:54 +03:00
unsigned int parity , unsigned int bits ,
unsigned int stb )
{
unsigned int dll , dlm , lcr ;
int div ;
2012-03-09 21:51:48 +04:00
div = DIV_ROUND_CLOSEST ( priv - > uartclk / 16 , baud ) ;
2010-11-17 03:55:54 +03:00
if ( div < 0 | | USHRT_MAX < = div ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " Invalid Baud(div=0x%x) \n " , div ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
dll = ( unsigned int ) div & 0x00FFU ;
dlm = ( ( unsigned int ) div > > 8 ) & 0x00FFU ;
if ( parity & ~ ( PCH_UART_LCR_PEN | PCH_UART_LCR_EPS | PCH_UART_LCR_SP ) ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " Invalid parity(0x%x) \n " , parity ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
if ( bits & ~ PCH_UART_LCR_WLS ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " Invalid bits(0x%x) \n " , bits ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
if ( stb & ~ PCH_UART_LCR_STB ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " Invalid STB(0x%x) \n " , stb ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
lcr = parity ;
lcr | = bits ;
lcr | = stb ;
2013-07-30 02:15:07 +04:00
dev_dbg ( priv - > port . dev , " %s:baud = %u, div = %04x, lcr = %02x (%lu) \n " ,
2010-11-17 03:55:54 +03:00
__func__ , baud , div , lcr , jiffies ) ;
iowrite8 ( PCH_UART_LCR_DLAB , priv - > membase + UART_LCR ) ;
iowrite8 ( dll , priv - > membase + PCH_UART_DLL ) ;
iowrite8 ( dlm , priv - > membase + PCH_UART_DLM ) ;
iowrite8 ( lcr , priv - > membase + UART_LCR ) ;
return 0 ;
}
static int pch_uart_hal_fifo_reset ( struct eg20t_port * priv ,
unsigned int flag )
{
if ( flag & ~ ( PCH_UART_FCR_TFR | PCH_UART_FCR_RFR ) ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s:Invalid flag(0x%x) \n " ,
__func__ , flag ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
iowrite8 ( PCH_UART_FCR_FIFOE | priv - > fcr , priv - > membase + UART_FCR ) ;
iowrite8 ( PCH_UART_FCR_FIFOE | priv - > fcr | flag ,
priv - > membase + UART_FCR ) ;
iowrite8 ( priv - > fcr , priv - > membase + UART_FCR ) ;
return 0 ;
}
static int pch_uart_hal_set_fifo ( struct eg20t_port * priv ,
unsigned int dmamode ,
unsigned int fifo_size , unsigned int trigger )
{
u8 fcr ;
if ( dmamode & ~ PCH_UART_FCR_DMS ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s:Invalid DMA Mode(0x%x) \n " ,
__func__ , dmamode ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
if ( fifo_size & ~ ( PCH_UART_FCR_FIFOE | PCH_UART_FCR_FIFO256 ) ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s:Invalid FIFO SIZE(0x%x) \n " ,
__func__ , fifo_size ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
if ( trigger & ~ PCH_UART_FCR_RFTL ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s:Invalid TRIGGER(0x%x) \n " ,
__func__ , trigger ) ;
2010-11-17 03:55:54 +03:00
return - EINVAL ;
}
switch ( priv - > fifo_size ) {
case 256 :
priv - > trigger_level =
trigger_level_256 [ trigger > > PCH_UART_FCR_RFTL_SHIFT ] ;
break ;
case 64 :
priv - > trigger_level =
trigger_level_64 [ trigger > > PCH_UART_FCR_RFTL_SHIFT ] ;
break ;
case 16 :
priv - > trigger_level =
trigger_level_16 [ trigger > > PCH_UART_FCR_RFTL_SHIFT ] ;
break ;
default :
priv - > trigger_level =
trigger_level_1 [ trigger > > PCH_UART_FCR_RFTL_SHIFT ] ;
break ;
}
fcr =
dmamode | fifo_size | trigger | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR ;
iowrite8 ( PCH_UART_FCR_FIFOE , priv - > membase + UART_FCR ) ;
iowrite8 ( PCH_UART_FCR_FIFOE | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR ,
priv - > membase + UART_FCR ) ;
iowrite8 ( fcr , priv - > membase + UART_FCR ) ;
priv - > fcr = fcr ;
return 0 ;
}
static u8 pch_uart_hal_get_modem ( struct eg20t_port * priv )
{
2012-02-06 13:24:44 +04:00
unsigned int msr = ioread8 ( priv - > membase + UART_MSR ) ;
priv - > dmsr = msr & PCH_UART_MSR_DELTA ;
return ( u8 ) msr ;
2010-11-17 03:55:54 +03:00
}
2011-02-23 04:03:14 +03:00
static void pch_uart_hal_write ( struct eg20t_port * priv ,
2010-11-17 03:55:54 +03:00
const unsigned char * buf , int tx_size )
{
int i ;
unsigned int thr ;
for ( i = 0 ; i < tx_size ; ) {
thr = buf [ i + + ] ;
iowrite8 ( thr , priv - > membase + PCH_UART_THR ) ;
}
}
static int pch_uart_hal_read ( struct eg20t_port * priv , unsigned char * buf ,
int rx_size )
{
int i ;
u8 rbr , lsr ;
2013-01-19 13:52:11 +04:00
struct uart_port * port = & priv - > port ;
2010-11-17 03:55:54 +03:00
lsr = ioread8 ( priv - > membase + UART_LSR ) ;
for ( i = 0 , lsr = ioread8 ( priv - > membase + UART_LSR ) ;
2013-01-19 13:52:11 +04:00
i < rx_size & & lsr & ( UART_LSR_DR | UART_LSR_BI ) ;
2010-11-17 03:55:54 +03:00
lsr = ioread8 ( priv - > membase + UART_LSR ) ) {
rbr = ioread8 ( priv - > membase + PCH_UART_RBR ) ;
2013-01-19 13:52:11 +04:00
if ( lsr & UART_LSR_BI ) {
port - > icount . brk + + ;
if ( uart_handle_break ( port ) )
continue ;
}
2013-01-24 08:31:27 +04:00
# ifdef SUPPORT_SYSRQ
2013-01-19 13:52:11 +04:00
if ( port - > sysrq ) {
if ( uart_handle_sysrq_char ( port , rbr ) )
continue ;
}
2013-01-24 08:31:27 +04:00
# endif
2013-01-19 13:52:11 +04:00
2010-11-17 03:55:54 +03:00
buf [ i + + ] = rbr ;
}
return i ;
}
2012-03-26 09:43:01 +04:00
static unsigned char pch_uart_hal_get_iid ( struct eg20t_port * priv )
2010-11-17 03:55:54 +03:00
{
2012-03-26 09:43:01 +04:00
return ioread8 ( priv - > membase + UART_IIR ) & \
( PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP ) ;
2010-11-17 03:55:54 +03:00
}
static u8 pch_uart_hal_get_line_status ( struct eg20t_port * priv )
{
return ioread8 ( priv - > membase + UART_LSR ) ;
}
static void pch_uart_hal_set_break ( struct eg20t_port * priv , int on )
{
unsigned int lcr ;
lcr = ioread8 ( priv - > membase + UART_LCR ) ;
if ( on )
lcr | = PCH_UART_LCR_SB ;
else
lcr & = ~ PCH_UART_LCR_SB ;
iowrite8 ( lcr , priv - > membase + UART_LCR ) ;
}
static int push_rx ( struct eg20t_port * priv , const unsigned char * buf ,
int size )
{
2013-01-03 18:53:04 +04:00
struct uart_port * port = & priv - > port ;
struct tty_port * tport = & port - > state - > port ;
2010-11-17 03:55:54 +03:00
2013-01-03 18:53:04 +04:00
tty_insert_flip_string ( tport , buf , size ) ;
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( tport ) ;
2010-11-17 03:55:54 +03:00
return 0 ;
}
static int pop_tx_x ( struct eg20t_port * priv , unsigned char * buf )
{
2012-02-06 13:24:44 +04:00
int ret = 0 ;
2010-11-17 03:55:54 +03:00
struct uart_port * port = & priv - > port ;
if ( port - > x_char ) {
2011-02-23 04:03:15 +03:00
dev_dbg ( priv - > port . dev , " %s:X character send %02x (%lu) \n " ,
__func__ , port - > x_char , jiffies ) ;
2010-11-17 03:55:54 +03:00
buf [ 0 ] = port - > x_char ;
port - > x_char = 0 ;
ret = 1 ;
}
return ret ;
}
static int dma_push_rx ( struct eg20t_port * priv , int size )
{
int room ;
struct uart_port * port = & priv - > port ;
2013-01-03 18:53:01 +04:00
struct tty_port * tport = & port - > state - > port ;
2010-11-17 03:55:54 +03:00
2013-01-03 18:53:01 +04:00
room = tty_buffer_request_room ( tport , size ) ;
2010-11-17 03:55:54 +03:00
if ( room < size )
dev_warn ( port - > dev , " Rx overrun: dropping %u bytes \n " ,
size - room ) ;
if ( ! room )
2013-09-10 14:50:51 +04:00
return 0 ;
2010-11-17 03:55:54 +03:00
2013-01-03 18:53:04 +04:00
tty_insert_flip_string ( tport , sg_virt ( & priv - > sg_rx ) , size ) ;
2010-11-17 03:55:54 +03:00
port - > icount . rx + = room ;
return room ;
}
static void pch_free_dma ( struct uart_port * port )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
if ( priv - > chan_tx ) {
dma_release_channel ( priv - > chan_tx ) ;
priv - > chan_tx = NULL ;
}
if ( priv - > chan_rx ) {
dma_release_channel ( priv - > chan_rx ) ;
priv - > chan_rx = NULL ;
}
2012-03-26 09:43:06 +04:00
if ( priv - > rx_buf_dma ) {
dma_free_coherent ( port - > dev , port - > fifosize , priv - > rx_buf_virt ,
priv - > rx_buf_dma ) ;
priv - > rx_buf_virt = NULL ;
priv - > rx_buf_dma = 0 ;
}
2010-11-17 03:55:54 +03:00
return ;
}
static bool filter ( struct dma_chan * chan , void * slave )
{
struct pch_dma_slave * param = slave ;
if ( ( chan - > chan_id = = param - > chan_id ) & & ( param - > dma_dev = =
chan - > device - > dev ) ) {
chan - > private = param ;
return true ;
} else {
return false ;
}
}
static void pch_request_dma ( struct uart_port * port )
{
dma_cap_mask_t mask ;
struct dma_chan * chan ;
struct pci_dev * dma_dev ;
struct pch_dma_slave * param ;
struct eg20t_port * priv =
container_of ( port , struct eg20t_port , port ) ;
dma_cap_zero ( mask ) ;
dma_cap_set ( DMA_SLAVE , mask ) ;
2014-07-30 19:59:52 +04:00
/* Get DMA's dev information */
dma_dev = pci_get_slot ( priv - > pdev - > bus ,
PCI_DEVFN ( PCI_SLOT ( priv - > pdev - > devfn ) , 0 ) ) ;
2010-11-17 03:55:54 +03:00
/* Set Tx DMA */
param = & priv - > param_tx ;
param - > dma_dev = & dma_dev - > dev ;
2011-02-23 04:03:19 +03:00
param - > chan_id = priv - > port . line * 2 ; /* Tx = 0, 2, 4, ... */
2010-11-17 03:55:54 +03:00
param - > tx_reg = port - > mapbase + UART_TX ;
chan = dma_request_channel ( mask , filter , param ) ;
if ( ! chan ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s:dma_request_channel FAILS(Tx) \n " ,
__func__ ) ;
2010-11-17 03:55:54 +03:00
return ;
}
priv - > chan_tx = chan ;
/* Set Rx DMA */
param = & priv - > param_rx ;
param - > dma_dev = & dma_dev - > dev ;
2011-02-23 04:03:19 +03:00
param - > chan_id = priv - > port . line * 2 + 1 ; /* Rx = Tx + 1 */
2010-11-17 03:55:54 +03:00
param - > rx_reg = port - > mapbase + UART_RX ;
chan = dma_request_channel ( mask , filter , param ) ;
if ( ! chan ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s:dma_request_channel FAILS(Rx) \n " ,
__func__ ) ;
2010-11-17 03:55:54 +03:00
dma_release_channel ( priv - > chan_tx ) ;
pch_uart: Fix DMA resource leak issue
Changing UART mode PIO->DMA->PIO->DMA like below, pch_uart driver can't get
DMA channel resource.
setserial /dev/ttyPCH0 ^low_latency
setserial /dev/ttyPCH0 low_latency
CAUSE:
Changing mode using setserial command, ".startup" function which gets DMA
channel is called before ".verify_port" function which sets
dma-flag(use_dma/use_dma_flag) as 1.
PIO->DMA
.startup: Since dma-flag is 0, DMA channel is not requested.
.verify_port: dma-flag is set as 1.
.shutdown: N/A
DMA->PIO
.startup: Since dma-flag is 1, DMA channel is requested.
.verify_port: dma-flag is set as 0.
.shutdown: Since dma-flag is 0, DMA channel is not released.
This means DMA channel resource leak occurs.
Next time, this driver can't get DMA channel resource forever.
MODIFICATION:
Currently, when release DMA channel resource, this driver checks dma-flag.
However, this specification occurs the above issue.
This driver must check whether dma_request_channel is executed or not.
The values are saved in private data variable "chan_tx/chan_tx".
These variables mean if the value is NULL, DMA channel is not requested,
if not NULL, DMA channel is requested.
This patch fixes the issue.
Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
Acked-by: Alan Cox <alan@linux.intel.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-11-11 05:55:27 +04:00
priv - > chan_tx = NULL ;
2010-11-17 03:55:54 +03:00
return ;
}
/* Get Consistent memory for DMA */
priv - > rx_buf_virt = dma_alloc_coherent ( port - > dev , port - > fifosize ,
& priv - > rx_buf_dma , GFP_KERNEL ) ;
priv - > chan_rx = chan ;
}
static void pch_dma_rx_complete ( void * arg )
{
struct eg20t_port * priv = arg ;
struct uart_port * port = & priv - > port ;
2011-02-23 04:03:12 +03:00
int count ;
2010-11-17 03:55:54 +03:00
2011-02-23 04:03:12 +03:00
dma_sync_sg_for_cpu ( port - > dev , & priv - > sg_rx , 1 , DMA_FROM_DEVICE ) ;
count = dma_push_rx ( priv , priv - > trigger_level ) ;
if ( count )
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( & port - > state - > port ) ;
2011-02-23 04:03:12 +03:00
async_tx_ack ( priv - > desc_rx ) ;
2012-07-06 12:19:42 +04:00
pch_uart_hal_enable_interrupt ( priv , PCH_UART_HAL_RX_INT |
PCH_UART_HAL_RX_ERR_INT ) ;
2010-11-17 03:55:54 +03:00
}
static void pch_dma_tx_complete ( void * arg )
{
struct eg20t_port * priv = arg ;
struct uart_port * port = & priv - > port ;
struct circ_buf * xmit = & port - > state - > xmit ;
2011-02-23 04:03:12 +03:00
struct scatterlist * sg = priv - > sg_tx_p ;
int i ;
2010-11-17 03:55:54 +03:00
2011-02-23 04:03:12 +03:00
for ( i = 0 ; i < priv - > nent ; i + + , sg + + ) {
xmit - > tail + = sg_dma_len ( sg ) ;
port - > icount . tx + = sg_dma_len ( sg ) ;
}
2010-11-17 03:55:54 +03:00
xmit - > tail & = UART_XMIT_SIZE - 1 ;
async_tx_ack ( priv - > desc_tx ) ;
2011-02-23 04:03:12 +03:00
dma_unmap_sg ( port - > dev , sg , priv - > nent , DMA_TO_DEVICE ) ;
2010-11-17 03:55:54 +03:00
priv - > tx_dma_use = 0 ;
2011-02-23 04:03:12 +03:00
priv - > nent = 0 ;
kfree ( priv - > sg_tx_p ) ;
2011-02-23 04:03:18 +03:00
pch_uart_hal_enable_interrupt ( priv , PCH_UART_HAL_TX_INT ) ;
2010-11-17 03:55:54 +03:00
}
2011-02-23 04:03:14 +03:00
static int pop_tx ( struct eg20t_port * priv , int size )
2010-11-17 03:55:54 +03:00
{
int count = 0 ;
struct uart_port * port = & priv - > port ;
struct circ_buf * xmit = & port - > state - > xmit ;
if ( uart_tx_stopped ( port ) | | uart_circ_empty ( xmit ) | | count > = size )
goto pop_tx_end ;
do {
int cnt_to_end =
CIRC_CNT_TO_END ( xmit - > head , xmit - > tail , UART_XMIT_SIZE ) ;
int sz = min ( size - count , cnt_to_end ) ;
2011-02-23 04:03:14 +03:00
pch_uart_hal_write ( priv , & xmit - > buf [ xmit - > tail ] , sz ) ;
2010-11-17 03:55:54 +03:00
xmit - > tail = ( xmit - > tail + sz ) & ( UART_XMIT_SIZE - 1 ) ;
count + = sz ;
} while ( ! uart_circ_empty ( xmit ) & & count < size ) ;
pop_tx_end :
2011-02-23 04:03:15 +03:00
dev_dbg ( priv - > port . dev , " %d characters. Remained %d characters.(%lu) \n " ,
2010-11-17 03:55:54 +03:00
count , size - count , jiffies ) ;
return count ;
}
static int handle_rx_to ( struct eg20t_port * priv )
{
struct pch_uart_buffer * buf ;
int rx_size ;
int ret ;
if ( ! priv - > start_rx ) {
2012-07-06 12:19:42 +04:00
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_RX_INT |
PCH_UART_HAL_RX_ERR_INT ) ;
2010-11-17 03:55:54 +03:00
return 0 ;
}
buf = & priv - > rxbuf ;
do {
rx_size = pch_uart_hal_read ( priv , buf - > buf , buf - > size ) ;
ret = push_rx ( priv , buf - > buf , rx_size ) ;
if ( ret )
return 0 ;
} while ( rx_size = = buf - > size ) ;
return PCH_UART_HANDLED_RX_INT ;
}
static int handle_rx ( struct eg20t_port * priv )
{
return handle_rx_to ( priv ) ;
}
static int dma_handle_rx ( struct eg20t_port * priv )
{
struct uart_port * port = & priv - > port ;
struct dma_async_tx_descriptor * desc ;
struct scatterlist * sg ;
priv = container_of ( port , struct eg20t_port , port ) ;
sg = & priv - > sg_rx ;
sg_init_table ( & priv - > sg_rx , 1 ) ; /* Initialize SG table */
2011-02-23 04:03:12 +03:00
sg_dma_len ( sg ) = priv - > trigger_level ;
2010-11-17 03:55:54 +03:00
sg_set_page ( & priv - > sg_rx , virt_to_page ( priv - > rx_buf_virt ) ,
2010-12-16 10:13:29 +03:00
sg_dma_len ( sg ) , ( unsigned long ) priv - > rx_buf_virt &
~ PAGE_MASK ) ;
2010-11-17 03:55:54 +03:00
sg_dma_address ( sg ) = priv - > rx_buf_dma ;
2012-03-09 01:11:18 +04:00
desc = dmaengine_prep_slave_sg ( priv - > chan_rx ,
2011-10-14 09:17:38 +04:00
sg , 1 , DMA_DEV_TO_MEM ,
2011-02-23 04:03:12 +03:00
DMA_PREP_INTERRUPT | DMA_CTRL_ACK ) ;
2010-11-17 03:55:54 +03:00
if ( ! desc )
return 0 ;
priv - > desc_rx = desc ;
desc - > callback = pch_dma_rx_complete ;
desc - > callback_param = priv ;
desc - > tx_submit ( desc ) ;
dma_async_issue_pending ( priv - > chan_rx ) ;
return PCH_UART_HANDLED_RX_INT ;
}
static unsigned int handle_tx ( struct eg20t_port * priv )
{
struct uart_port * port = & priv - > port ;
struct circ_buf * xmit = & port - > state - > xmit ;
int fifo_size ;
int tx_size ;
int size ;
int tx_empty ;
if ( ! priv - > start_tx ) {
2011-02-23 04:03:15 +03:00
dev_info ( priv - > port . dev , " %s:Tx isn't started. (%lu) \n " ,
__func__ , jiffies ) ;
2010-11-17 03:55:54 +03:00
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_TX_INT ) ;
priv - > tx_empty = 1 ;
return 0 ;
}
fifo_size = max ( priv - > fifo_size , 1 ) ;
tx_empty = 1 ;
if ( pop_tx_x ( priv , xmit - > buf ) ) {
pch_uart_hal_write ( priv , xmit - > buf , 1 ) ;
port - > icount . tx + + ;
tx_empty = 0 ;
fifo_size - - ;
}
size = min ( xmit - > head - xmit - > tail , fifo_size ) ;
2011-02-23 04:03:12 +03:00
if ( size < 0 )
size = fifo_size ;
2011-02-23 04:03:14 +03:00
tx_size = pop_tx ( priv , size ) ;
2010-11-17 03:55:54 +03:00
if ( tx_size > 0 ) {
2011-02-23 04:03:14 +03:00
port - > icount . tx + = tx_size ;
2010-11-17 03:55:54 +03:00
tx_empty = 0 ;
}
priv - > tx_empty = tx_empty ;
2011-02-23 04:03:12 +03:00
if ( tx_empty ) {
2010-11-17 03:55:54 +03:00
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_TX_INT ) ;
2011-02-23 04:03:12 +03:00
uart_write_wakeup ( port ) ;
}
2010-11-17 03:55:54 +03:00
return PCH_UART_HANDLED_TX_INT ;
}
static unsigned int dma_handle_tx ( struct eg20t_port * priv )
{
struct uart_port * port = & priv - > port ;
struct circ_buf * xmit = & port - > state - > xmit ;
2011-02-23 04:03:12 +03:00
struct scatterlist * sg ;
2010-11-17 03:55:54 +03:00
int nent ;
int fifo_size ;
int tx_empty ;
struct dma_async_tx_descriptor * desc ;
2011-02-23 04:03:12 +03:00
int num ;
int i ;
int bytes ;
int size ;
int rem ;
2010-11-17 03:55:54 +03:00
if ( ! priv - > start_tx ) {
2011-02-23 04:03:15 +03:00
dev_info ( priv - > port . dev , " %s:Tx isn't started. (%lu) \n " ,
__func__ , jiffies ) ;
2010-11-17 03:55:54 +03:00
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_TX_INT ) ;
priv - > tx_empty = 1 ;
return 0 ;
}
2011-02-23 04:03:18 +03:00
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 ;
}
2010-11-17 03:55:54 +03:00
fifo_size = max ( priv - > fifo_size , 1 ) ;
tx_empty = 1 ;
if ( pop_tx_x ( priv , xmit - > buf ) ) {
pch_uart_hal_write ( priv , xmit - > buf , 1 ) ;
port - > icount . tx + + ;
tx_empty = 0 ;
fifo_size - - ;
}
2011-02-23 04:03:12 +03:00
bytes = min ( ( int ) CIRC_CNT ( xmit - > head , xmit - > tail ,
UART_XMIT_SIZE ) , CIRC_CNT_TO_END ( xmit - > head ,
xmit - > tail , UART_XMIT_SIZE ) ) ;
if ( ! bytes ) {
2011-02-23 04:03:15 +03:00
dev_dbg ( priv - > port . dev , " %s 0 bytes return \n " , __func__ ) ;
2011-02-23 04:03:12 +03:00
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_TX_INT ) ;
uart_write_wakeup ( port ) ;
return 0 ;
}
if ( bytes > fifo_size ) {
num = bytes / fifo_size + 1 ;
size = fifo_size ;
rem = bytes % fifo_size ;
} else {
num = 1 ;
size = bytes ;
rem = bytes ;
}
2010-11-17 03:55:54 +03:00
2011-02-23 04:03:15 +03:00
dev_dbg ( priv - > port . dev , " %s num=%d size=%d rem=%d \n " ,
__func__ , num , size , rem ) ;
2010-11-17 03:55:54 +03:00
priv - > tx_dma_use = 1 ;
2011-02-23 04:03:12 +03:00
priv - > sg_tx_p = kzalloc ( sizeof ( struct scatterlist ) * num , GFP_ATOMIC ) ;
2012-07-28 16:43:57 +04:00
if ( ! priv - > sg_tx_p ) {
dev_err ( priv - > port . dev , " %s:kzalloc Failed \n " , __func__ ) ;
return 0 ;
}
2010-11-17 03:55:54 +03:00
2011-02-23 04:03:12 +03:00
sg_init_table ( priv - > sg_tx_p , num ) ; /* Initialize SG table */
sg = priv - > sg_tx_p ;
2010-11-17 03:55:54 +03:00
2011-02-23 04:03:12 +03:00
for ( i = 0 ; i < num ; i + + , sg + + ) {
if ( i = = ( num - 1 ) )
sg_set_page ( sg , virt_to_page ( xmit - > buf ) ,
rem , fifo_size * i ) ;
else
sg_set_page ( sg , virt_to_page ( xmit - > buf ) ,
size , fifo_size * i ) ;
}
sg = priv - > sg_tx_p ;
nent = dma_map_sg ( port - > dev , sg , num , DMA_TO_DEVICE ) ;
2010-11-17 03:55:54 +03:00
if ( ! nent ) {
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s:dma_map_sg Failed \n " , __func__ ) ;
2010-11-17 03:55:54 +03:00
return 0 ;
}
2011-02-23 04:03:12 +03:00
priv - > nent = nent ;
for ( i = 0 ; i < nent ; i + + , sg + + ) {
sg - > offset = ( xmit - > tail & ( UART_XMIT_SIZE - 1 ) ) +
fifo_size * i ;
sg_dma_address ( sg ) = ( sg_dma_address ( sg ) &
~ ( UART_XMIT_SIZE - 1 ) ) + sg - > offset ;
if ( i = = ( nent - 1 ) )
sg_dma_len ( sg ) = rem ;
else
sg_dma_len ( sg ) = size ;
}
2010-11-17 03:55:54 +03:00
2012-03-09 01:11:18 +04:00
desc = dmaengine_prep_slave_sg ( priv - > chan_tx ,
2011-10-14 09:17:38 +04:00
priv - > sg_tx_p , nent , DMA_MEM_TO_DEV ,
2011-02-23 04:03:12 +03:00
DMA_PREP_INTERRUPT | DMA_CTRL_ACK ) ;
2010-11-17 03:55:54 +03:00
if ( ! desc ) {
2014-07-11 20:13:26 +04:00
dev_err ( priv - > port . dev , " %s:dmaengine_prep_slave_sg Failed \n " ,
2011-02-23 04:03:15 +03:00
__func__ ) ;
2010-11-17 03:55:54 +03:00
return 0 ;
}
2011-02-23 04:03:12 +03:00
dma_sync_sg_for_device ( port - > dev , priv - > sg_tx_p , nent , DMA_TO_DEVICE ) ;
2010-11-17 03:55:54 +03:00
priv - > desc_tx = desc ;
desc - > callback = pch_dma_tx_complete ;
desc - > callback_param = priv ;
desc - > tx_submit ( desc ) ;
dma_async_issue_pending ( priv - > chan_tx ) ;
return PCH_UART_HANDLED_TX_INT ;
}
static void pch_uart_err_ir ( struct eg20t_port * priv , unsigned int lsr )
{
2013-01-19 13:52:10 +04:00
struct uart_port * port = & priv - > port ;
struct tty_struct * tty = tty_port_tty_get ( & port - > state - > port ) ;
char * error_msg [ 5 ] = { } ;
int i = 0 ;
2010-11-17 03:55:54 +03:00
if ( lsr & PCH_UART_LSR_ERR )
2013-01-19 13:52:10 +04:00
error_msg [ i + + ] = " Error data in FIFO \n " ;
if ( lsr & UART_LSR_FE ) {
port - > icount . frame + + ;
error_msg [ i + + ] = " Framing Error \n " ;
}
2010-11-17 03:55:54 +03:00
2013-01-19 13:52:10 +04:00
if ( lsr & UART_LSR_PE ) {
port - > icount . parity + + ;
error_msg [ i + + ] = " Parity Error \n " ;
}
2010-11-17 03:55:54 +03:00
2013-01-19 13:52:10 +04:00
if ( lsr & UART_LSR_OE ) {
port - > icount . overrun + + ;
error_msg [ i + + ] = " Overrun Error \n " ;
}
2010-11-17 03:55:54 +03:00
2013-01-19 13:52:10 +04:00
if ( tty = = NULL ) {
for ( i = 0 ; error_msg [ i ] ! = NULL ; i + + )
dev_err ( & priv - > pdev - > dev , error_msg [ i ] ) ;
2013-09-10 14:50:49 +04:00
} else {
tty_kref_put ( tty ) ;
2013-01-19 13:52:10 +04:00
}
2010-11-17 03:55:54 +03:00
}
static irqreturn_t pch_uart_interrupt ( int irq , void * dev_id )
{
struct eg20t_port * priv = dev_id ;
unsigned int handled ;
u8 lsr ;
int ret = 0 ;
2012-03-26 09:43:01 +04:00
unsigned char iid ;
2010-11-17 03:55:54 +03:00
unsigned long flags ;
2012-03-26 09:43:03 +04:00
int next = 1 ;
u8 msr ;
2010-11-17 03:55:54 +03:00
2012-06-20 01:00:18 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2010-11-17 03:55:54 +03:00
handled = 0 ;
2012-03-26 09:43:03 +04:00
while ( next ) {
iid = pch_uart_hal_get_iid ( priv ) ;
if ( iid & PCH_UART_IIR_IP ) /* No Interrupt */
break ;
2010-11-17 03:55:54 +03:00
switch ( iid ) {
case PCH_UART_IID_RLS : /* Receiver Line Status */
lsr = pch_uart_hal_get_line_status ( priv ) ;
if ( lsr & ( PCH_UART_LSR_ERR | UART_LSR_FE |
UART_LSR_PE | UART_LSR_OE ) ) {
pch_uart_err_ir ( priv , lsr ) ;
ret = PCH_UART_HANDLED_RX_ERR_INT ;
2012-03-26 09:43:05 +04:00
} else {
ret = PCH_UART_HANDLED_LS_INT ;
2010-11-17 03:55:54 +03:00
}
break ;
case PCH_UART_IID_RDR : /* Received Data Ready */
2011-02-23 04:03:12 +03:00
if ( priv - > use_dma ) {
pch_uart_hal_disable_interrupt ( priv ,
2012-07-06 12:19:42 +04:00
PCH_UART_HAL_RX_INT |
PCH_UART_HAL_RX_ERR_INT ) ;
2010-11-17 03:55:54 +03:00
ret = dma_handle_rx ( priv ) ;
2011-02-23 04:03:12 +03:00
if ( ! ret )
pch_uart_hal_enable_interrupt ( priv ,
2012-07-06 12:19:42 +04:00
PCH_UART_HAL_RX_INT |
PCH_UART_HAL_RX_ERR_INT ) ;
2011-02-23 04:03:12 +03:00
} else {
2010-11-17 03:55:54 +03:00
ret = handle_rx ( priv ) ;
2011-02-23 04:03:12 +03:00
}
2010-11-17 03:55:54 +03:00
break ;
case PCH_UART_IID_RDR_TO : /* Received Data Ready
( FIFO Timeout ) */
ret = handle_rx_to ( priv ) ;
break ;
case PCH_UART_IID_THRE : /* Transmitter Holding Register
Empty */
if ( priv - > use_dma )
ret = dma_handle_tx ( priv ) ;
else
ret = handle_tx ( priv ) ;
break ;
case PCH_UART_IID_MS : /* Modem Status */
2012-03-26 09:43:03 +04:00
msr = pch_uart_hal_get_modem ( priv ) ;
next = 0 ; /* MS ir prioirty is the lowest. So, MS ir
means final interrupt */
if ( ( msr & UART_MSR_ANY_DELTA ) = = 0 )
break ;
ret | = PCH_UART_HANDLED_MS_INT ;
2010-11-17 03:55:54 +03:00
break ;
default : /* Never junp to this label */
2012-03-26 09:43:02 +04:00
dev_err ( priv - > port . dev , " %s:iid=%02x (%lu) \n " , __func__ ,
2011-02-23 04:03:15 +03:00
iid , jiffies ) ;
2010-11-17 03:55:54 +03:00
ret = - 1 ;
2012-03-26 09:43:03 +04:00
next = 0 ;
2010-11-17 03:55:54 +03:00
break ;
}
handled | = ( unsigned int ) ret ;
}
2012-06-20 01:00:18 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-17 03:55:54 +03:00
return IRQ_RETVAL ( handled ) ;
}
/* This function tests whether the transmitter fifo and shifter for the port
described by ' port ' is empty . */
static unsigned int pch_uart_tx_empty ( struct uart_port * port )
{
struct eg20t_port * priv ;
2012-02-06 13:24:44 +04:00
2010-11-17 03:55:54 +03:00
priv = container_of ( port , struct eg20t_port , port ) ;
if ( priv - > tx_empty )
2012-02-06 13:24:44 +04:00
return TIOCSER_TEMT ;
2010-11-17 03:55:54 +03:00
else
2012-02-06 13:24:44 +04:00
return 0 ;
2010-11-17 03:55:54 +03:00
}
/* Returns the current state of modem control inputs. */
static unsigned int pch_uart_get_mctrl ( struct uart_port * port )
{
struct eg20t_port * priv ;
u8 modem ;
unsigned int ret = 0 ;
priv = container_of ( port , struct eg20t_port , port ) ;
modem = pch_uart_hal_get_modem ( priv ) ;
if ( modem & UART_MSR_DCD )
ret | = TIOCM_CAR ;
if ( modem & UART_MSR_RI )
ret | = TIOCM_RNG ;
if ( modem & UART_MSR_DSR )
ret | = TIOCM_DSR ;
if ( modem & UART_MSR_CTS )
ret | = TIOCM_CTS ;
return ret ;
}
static void pch_uart_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
u32 mcr = 0 ;
struct eg20t_port * priv = container_of ( port , struct eg20t_port , port ) ;
if ( mctrl & TIOCM_DTR )
mcr | = UART_MCR_DTR ;
if ( mctrl & TIOCM_RTS )
mcr | = UART_MCR_RTS ;
if ( mctrl & TIOCM_LOOP )
mcr | = UART_MCR_LOOP ;
2011-02-23 04:03:17 +03:00
if ( priv - > mcr & UART_MCR_AFE )
mcr | = UART_MCR_AFE ;
if ( mctrl )
iowrite8 ( mcr , priv - > membase + UART_MCR ) ;
2010-11-17 03:55:54 +03:00
}
static void pch_uart_stop_tx ( struct uart_port * port )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
priv - > start_tx = 0 ;
priv - > tx_dma_use = 0 ;
}
static void pch_uart_start_tx ( struct uart_port * port )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
2011-02-23 04:03:15 +03:00
if ( priv - > use_dma ) {
if ( priv - > tx_dma_use ) {
dev_dbg ( priv - > port . dev , " %s : Tx DMA is NOT empty. \n " ,
__func__ ) ;
2010-11-17 03:55:54 +03:00
return ;
2011-02-23 04:03:15 +03:00
}
}
2010-11-17 03:55:54 +03:00
priv - > start_tx = 1 ;
pch_uart_hal_enable_interrupt ( priv , PCH_UART_HAL_TX_INT ) ;
}
static void pch_uart_stop_rx ( struct uart_port * port )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
priv - > start_rx = 0 ;
2012-07-06 12:19:42 +04:00
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_RX_INT |
PCH_UART_HAL_RX_ERR_INT ) ;
2010-11-17 03:55:54 +03:00
}
/* Enable the modem status interrupts. */
static void pch_uart_enable_ms ( struct uart_port * port )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
pch_uart_hal_enable_interrupt ( priv , PCH_UART_HAL_MS_INT ) ;
}
/* Control the transmission of a break signal. */
static void pch_uart_break_ctl ( struct uart_port * port , int ctl )
{
struct eg20t_port * priv ;
unsigned long flags ;
priv = container_of ( port , struct eg20t_port , port ) ;
2012-06-20 01:00:18 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2010-11-17 03:55:54 +03:00
pch_uart_hal_set_break ( priv , ctl ) ;
2012-06-20 01:00:18 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-17 03:55:54 +03:00
}
/* Grab any interrupt resources and initialise any low level driver state. */
static int pch_uart_startup ( struct uart_port * port )
{
struct eg20t_port * priv ;
int ret ;
int fifo_size ;
int trigger_level ;
priv = container_of ( port , struct eg20t_port , port ) ;
priv - > tx_empty = 1 ;
2011-02-23 04:03:16 +03:00
if ( port - > uartclk )
2012-03-09 21:51:48 +04:00
priv - > uartclk = port - > uartclk ;
2011-02-23 04:03:16 +03:00
else
2012-03-09 21:51:48 +04:00
port - > uartclk = priv - > uartclk ;
2011-02-23 04:03:16 +03:00
2010-11-17 03:55:54 +03:00
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 ,
PCH_UART_HAL_STB1 ) ;
if ( ret )
return ret ;
switch ( priv - > fifo_size ) {
case 256 :
fifo_size = PCH_UART_HAL_FIFO256 ;
break ;
case 64 :
fifo_size = PCH_UART_HAL_FIFO64 ;
break ;
case 16 :
fifo_size = PCH_UART_HAL_FIFO16 ;
2012-07-02 21:51:38 +04:00
break ;
2010-11-17 03:55:54 +03:00
case 1 :
default :
fifo_size = PCH_UART_HAL_FIFO_DIS ;
break ;
}
switch ( priv - > trigger ) {
case PCH_UART_HAL_TRIGGER1 :
trigger_level = 1 ;
break ;
case PCH_UART_HAL_TRIGGER_L :
trigger_level = priv - > fifo_size / 4 ;
break ;
case PCH_UART_HAL_TRIGGER_M :
trigger_level = priv - > fifo_size / 2 ;
break ;
case PCH_UART_HAL_TRIGGER_H :
default :
trigger_level = priv - > fifo_size - ( priv - > fifo_size / 8 ) ;
break ;
}
priv - > trigger_level = trigger_level ;
ret = pch_uart_hal_set_fifo ( priv , PCH_UART_HAL_DMA_MODE0 ,
fifo_size , priv - > trigger ) ;
if ( ret < 0 )
return ret ;
ret = request_irq ( priv - > port . irq , pch_uart_interrupt , IRQF_SHARED ,
2014-03-25 17:05:08 +04:00
priv - > irq_name , priv ) ;
2010-11-17 03:55:54 +03:00
if ( ret < 0 )
return ret ;
if ( priv - > use_dma )
pch_request_dma ( port ) ;
priv - > start_rx = 1 ;
2012-07-06 12:19:42 +04:00
pch_uart_hal_enable_interrupt ( priv , PCH_UART_HAL_RX_INT |
PCH_UART_HAL_RX_ERR_INT ) ;
2010-11-17 03:55:54 +03:00
uart_update_timeout ( port , CS8 , default_baud ) ;
return 0 ;
}
static void pch_uart_shutdown ( struct uart_port * port )
{
struct eg20t_port * priv ;
int ret ;
priv = container_of ( port , struct eg20t_port , port ) ;
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_ALL_INT ) ;
pch_uart_hal_fifo_reset ( priv , PCH_UART_HAL_CLR_ALL_FIFO ) ;
ret = pch_uart_hal_set_fifo ( priv , PCH_UART_HAL_DMA_MODE0 ,
PCH_UART_HAL_FIFO_DIS , PCH_UART_HAL_TRIGGER1 ) ;
if ( ret )
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev ,
" pch_uart_hal_set_fifo Failed(ret=%d) \n " , ret ) ;
2010-11-17 03:55:54 +03:00
pch_uart: Fix DMA resource leak issue
Changing UART mode PIO->DMA->PIO->DMA like below, pch_uart driver can't get
DMA channel resource.
setserial /dev/ttyPCH0 ^low_latency
setserial /dev/ttyPCH0 low_latency
CAUSE:
Changing mode using setserial command, ".startup" function which gets DMA
channel is called before ".verify_port" function which sets
dma-flag(use_dma/use_dma_flag) as 1.
PIO->DMA
.startup: Since dma-flag is 0, DMA channel is not requested.
.verify_port: dma-flag is set as 1.
.shutdown: N/A
DMA->PIO
.startup: Since dma-flag is 1, DMA channel is requested.
.verify_port: dma-flag is set as 0.
.shutdown: Since dma-flag is 0, DMA channel is not released.
This means DMA channel resource leak occurs.
Next time, this driver can't get DMA channel resource forever.
MODIFICATION:
Currently, when release DMA channel resource, this driver checks dma-flag.
However, this specification occurs the above issue.
This driver must check whether dma_request_channel is executed or not.
The values are saved in private data variable "chan_tx/chan_tx".
These variables mean if the value is NULL, DMA channel is not requested,
if not NULL, DMA channel is requested.
This patch fixes the issue.
Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
Acked-by: Alan Cox <alan@linux.intel.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-11-11 05:55:27 +04:00
pch_free_dma ( port ) ;
2010-11-17 03:55:54 +03:00
free_irq ( priv - > port . irq , priv ) ;
}
/* Change the port parameters, including word length, parity, stop
* bits . Update read_status_mask and ignore_status_mask to indicate
* the types of events we are interested in receiving . */
static void pch_uart_set_termios ( struct uart_port * port ,
struct ktermios * termios , struct ktermios * old )
{
int rtn ;
2013-07-30 02:15:07 +04:00
unsigned int baud , parity , bits , stb ;
2010-11-17 03:55:54 +03:00
struct eg20t_port * priv ;
unsigned long flags ;
priv = container_of ( port , struct eg20t_port , port ) ;
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
bits = PCH_UART_HAL_5BIT ;
break ;
case CS6 :
bits = PCH_UART_HAL_6BIT ;
break ;
case CS7 :
bits = PCH_UART_HAL_7BIT ;
break ;
default : /* CS8 */
bits = PCH_UART_HAL_8BIT ;
break ;
}
if ( termios - > c_cflag & CSTOPB )
stb = PCH_UART_HAL_STB2 ;
else
stb = PCH_UART_HAL_STB1 ;
if ( termios - > c_cflag & PARENB ) {
2012-07-06 12:19:43 +04:00
if ( termios - > c_cflag & PARODD )
2010-11-17 03:55:54 +03:00
parity = PCH_UART_HAL_PARITY_ODD ;
else
parity = PCH_UART_HAL_PARITY_EVEN ;
2012-02-06 13:24:44 +04:00
} else
2010-11-17 03:55:54 +03:00
parity = PCH_UART_HAL_PARITY_NONE ;
2011-02-23 04:03:17 +03:00
/* 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 ;
2010-11-17 03:55:54 +03:00
termios - > c_cflag & = ~ CMSPAR ; /* Mark/Space parity is not supported */
baud = uart_get_baud_rate ( port , termios , old , 0 , port - > uartclk / 16 ) ;
2012-06-20 01:00:18 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
spin_lock ( & port - > lock ) ;
2010-11-17 03:55:54 +03:00
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
rtn = pch_uart_hal_set_line ( priv , baud , parity , bits , stb ) ;
if ( rtn )
goto out ;
2011-10-27 10:45:18 +04:00
pch_uart_set_mctrl ( & priv - > port , priv - > port . mctrl ) ;
2010-11-17 03:55:54 +03:00
/* Don't rewrite B0 */
if ( tty_termios_baud_rate ( termios ) )
tty_termios_encode_baud_rate ( termios , baud , baud ) ;
out :
2012-06-20 01:00:18 +04:00
spin_unlock ( & port - > lock ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-17 03:55:54 +03:00
}
static const char * pch_uart_type ( struct uart_port * port )
{
return KBUILD_MODNAME ;
}
static void pch_uart_release_port ( struct uart_port * port )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
pci_iounmap ( priv - > pdev , priv - > membase ) ;
pci_release_regions ( priv - > pdev ) ;
}
static int pch_uart_request_port ( struct uart_port * port )
{
struct eg20t_port * priv ;
int ret ;
void __iomem * membase ;
priv = container_of ( port , struct eg20t_port , port ) ;
ret = pci_request_regions ( priv - > pdev , KBUILD_MODNAME ) ;
if ( ret < 0 )
return - EBUSY ;
membase = pci_iomap ( priv - > pdev , 1 , 0 ) ;
if ( ! membase ) {
pci_release_regions ( priv - > pdev ) ;
return - EBUSY ;
}
priv - > membase = port - > membase = membase ;
return 0 ;
}
static void pch_uart_config_port ( struct uart_port * port , int type )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
if ( type & UART_CONFIG_TYPE ) {
port - > type = priv - > port_type ;
pch_uart_request_port ( port ) ;
}
}
static int pch_uart_verify_port ( struct uart_port * port ,
struct serial_struct * serinfo )
{
struct eg20t_port * priv ;
priv = container_of ( port , struct eg20t_port , port ) ;
if ( serinfo - > flags & UPF_LOW_LATENCY ) {
2011-02-23 04:03:15 +03:00
dev_info ( priv - > port . dev ,
" PCH UART : Use PIO Mode (without DMA) \n " ) ;
2010-11-17 03:55:54 +03:00
priv - > use_dma = 0 ;
serinfo - > flags & = ~ UPF_LOW_LATENCY ;
} else {
# ifndef CONFIG_PCH_DMA
2011-02-23 04:03:15 +03:00
dev_err ( priv - > port . dev , " %s : PCH DMA is not Loaded. \n " ,
__func__ ) ;
2010-11-17 03:55:54 +03:00
return - EOPNOTSUPP ;
# endif
2013-12-04 23:52:49 +04:00
if ( ! priv - > use_dma ) {
2012-04-12 05:47:50 +04:00
pch_request_dma ( port ) ;
2013-12-04 23:52:49 +04:00
if ( priv - > chan_rx )
priv - > use_dma = 1 ;
}
dev_info ( priv - > port . dev , " PCH UART: %s \n " ,
priv - > use_dma ?
" Use DMA Mode " : " No DMA " ) ;
2010-11-17 03:55:54 +03:00
}
return 0 ;
}
2013-08-15 02:18:37 +04:00
# if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_PCH_UART_CONSOLE)
2011-11-16 03:04:07 +04:00
/*
* Wait for transmitter & holding register to empty
*/
static void wait_for_xmitr ( struct eg20t_port * up , int bits )
{
unsigned int status , tmout = 10000 ;
/* Wait up to 10ms for the character(s) to be sent. */
for ( ; ; ) {
status = ioread8 ( up - > membase + UART_LSR ) ;
if ( ( status & bits ) = = bits )
break ;
if ( - - tmout = = 0 )
break ;
udelay ( 1 ) ;
}
/* Wait up to 1s for flow control if necessary */
if ( up - > port . flags & UPF_CONS_FLOW ) {
unsigned int tmout ;
for ( tmout = 1000000 ; tmout ; tmout - - ) {
unsigned int msr = ioread8 ( up - > membase + UART_MSR ) ;
if ( msr & UART_MSR_CTS )
break ;
udelay ( 1 ) ;
touch_nmi_watchdog ( ) ;
}
}
}
2013-08-15 02:18:37 +04:00
# endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_PCH_UART_CONSOLE */
2011-11-16 03:04:07 +04:00
2013-03-05 18:30:38 +04:00
# ifdef CONFIG_CONSOLE_POLL
/*
* Console polling routines for communicate via uart while
* in an interrupt or debug context .
*/
static int pch_uart_get_poll_char ( struct uart_port * port )
{
struct eg20t_port * priv =
container_of ( port , struct eg20t_port , port ) ;
u8 lsr = ioread8 ( priv - > membase + UART_LSR ) ;
if ( ! ( lsr & UART_LSR_DR ) )
return NO_POLL_CHAR ;
return ioread8 ( priv - > membase + PCH_UART_RBR ) ;
}
static void pch_uart_put_poll_char ( struct uart_port * port ,
unsigned char c )
{
unsigned int ier ;
struct eg20t_port * priv =
container_of ( port , struct eg20t_port , port ) ;
/*
* First save the IER then disable the interrupts
*/
ier = ioread8 ( priv - > membase + UART_IER ) ;
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_ALL_INT ) ;
wait_for_xmitr ( priv , UART_LSR_THRE ) ;
/*
* Send the character out .
*/
iowrite8 ( c , priv - > membase + PCH_UART_THR ) ;
/*
* Finally , wait for transmitter to become empty
* and restore the IER
*/
wait_for_xmitr ( priv , BOTH_EMPTY ) ;
iowrite8 ( ier , priv - > membase + UART_IER ) ;
}
# endif /* CONFIG_CONSOLE_POLL */
static struct uart_ops pch_uart_ops = {
. tx_empty = pch_uart_tx_empty ,
. set_mctrl = pch_uart_set_mctrl ,
. get_mctrl = pch_uart_get_mctrl ,
. stop_tx = pch_uart_stop_tx ,
. start_tx = pch_uart_start_tx ,
. stop_rx = pch_uart_stop_rx ,
. enable_ms = pch_uart_enable_ms ,
. break_ctl = pch_uart_break_ctl ,
. startup = pch_uart_startup ,
. shutdown = pch_uart_shutdown ,
. set_termios = pch_uart_set_termios ,
/* .pm = pch_uart_pm, Not supported yet */
. type = pch_uart_type ,
. release_port = pch_uart_release_port ,
. request_port = pch_uart_request_port ,
. config_port = pch_uart_config_port ,
. verify_port = pch_uart_verify_port ,
# ifdef CONFIG_CONSOLE_POLL
. poll_get_char = pch_uart_get_poll_char ,
. poll_put_char = pch_uart_put_poll_char ,
# endif
} ;
# ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
2011-11-16 03:04:07 +04:00
static void pch_console_putchar ( struct uart_port * port , int ch )
{
struct eg20t_port * priv =
container_of ( port , struct eg20t_port , port ) ;
wait_for_xmitr ( priv , UART_LSR_THRE ) ;
iowrite8 ( ch , priv - > membase + PCH_UART_THR ) ;
}
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port . . .
*
* The console_lock must be held when we get here .
*/
static void
pch_console_write ( struct console * co , const char * s , unsigned int count )
{
struct eg20t_port * priv ;
unsigned long flags ;
2012-06-20 01:00:18 +04:00
int priv_locked = 1 ;
int port_locked = 1 ;
2011-11-16 03:04:07 +04:00
u8 ier ;
priv = pch_uart_ports [ co - > index ] ;
touch_nmi_watchdog ( ) ;
local_irq_save ( flags ) ;
if ( priv - > port . sysrq ) {
2013-01-19 13:52:11 +04:00
/* call to uart_handle_sysrq_char already took the priv lock */
priv_locked = 0 ;
2012-06-20 01:00:18 +04:00
/* serial8250_handle_port() already took the port lock */
port_locked = 0 ;
2011-11-16 03:04:07 +04:00
} else if ( oops_in_progress ) {
2012-06-20 01:00:18 +04:00
priv_locked = spin_trylock ( & priv - > lock ) ;
port_locked = spin_trylock ( & priv - > port . lock ) ;
} else {
spin_lock ( & priv - > lock ) ;
2011-11-16 03:04:07 +04:00
spin_lock ( & priv - > port . lock ) ;
2012-06-20 01:00:18 +04:00
}
2011-11-16 03:04:07 +04:00
/*
* First save the IER then disable the interrupts
*/
ier = ioread8 ( priv - > membase + UART_IER ) ;
pch_uart_hal_disable_interrupt ( priv , PCH_UART_HAL_ALL_INT ) ;
uart_console_write ( & priv - > port , s , count , pch_console_putchar ) ;
/*
* Finally , wait for transmitter to become empty
* and restore the IER
*/
wait_for_xmitr ( priv , BOTH_EMPTY ) ;
iowrite8 ( ier , priv - > membase + UART_IER ) ;
2012-06-20 01:00:18 +04:00
if ( port_locked )
2011-11-16 03:04:07 +04:00
spin_unlock ( & priv - > port . lock ) ;
2012-06-20 01:00:18 +04:00
if ( priv_locked )
spin_unlock ( & priv - > lock ) ;
2011-11-16 03:04:07 +04:00
local_irq_restore ( flags ) ;
}
static int __init pch_console_setup ( struct console * co , char * options )
{
struct uart_port * port ;
2012-03-09 21:51:51 +04:00
int baud = default_baud ;
2011-11-16 03:04:07 +04:00
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
/*
* Check whether an invalid uart number has been specified , and
* if so , search for the first available port that does have
* console support .
*/
if ( co - > index > = PCH_UART_NR )
co - > index = 0 ;
port = & pch_uart_ports [ co - > index ] - > port ;
if ( ! port | | ( ! port - > iobase & & ! port - > membase ) )
return - ENODEV ;
2012-03-09 21:51:49 +04:00
port - > uartclk = pch_uart_get_uartclk ( ) ;
2011-11-16 03:04:07 +04:00
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
return uart_set_options ( port , co , baud , parity , bits , flow ) ;
}
static struct uart_driver pch_uart_driver ;
static struct console pch_console = {
. name = PCH_UART_DRIVER_DEVICE ,
. write = pch_console_write ,
. device = uart_console_device ,
. setup = pch_console_setup ,
. flags = CON_PRINTBUFFER | CON_ANYTIME ,
. index = - 1 ,
. data = & pch_uart_driver ,
} ;
# define PCH_CONSOLE (&pch_console)
# else
# define PCH_CONSOLE NULL
2013-03-05 18:30:38 +04:00
# endif /* CONFIG_SERIAL_PCH_UART_CONSOLE */
2011-11-16 03:04:07 +04:00
2010-11-17 03:55:54 +03:00
static struct uart_driver pch_uart_driver = {
. owner = THIS_MODULE ,
. driver_name = KBUILD_MODNAME ,
. dev_name = PCH_UART_DRIVER_DEVICE ,
. major = 0 ,
. minor = 0 ,
. nr = PCH_UART_NR ,
2011-11-16 03:04:07 +04:00
. cons = PCH_CONSOLE ,
2010-11-17 03:55:54 +03:00
} ;
static struct eg20t_port * pch_uart_init_port ( struct pci_dev * pdev ,
2011-01-28 12:00:01 +03:00
const struct pci_device_id * id )
2010-11-17 03:55:54 +03:00
{
struct eg20t_port * priv ;
int ret ;
unsigned int iobase ;
unsigned int mapbase ;
2010-12-16 10:13:29 +03:00
unsigned char * rxbuf ;
2012-03-09 21:51:49 +04:00
int fifosize ;
2011-02-23 04:03:19 +03:00
int port_type ;
struct pch_uart_driver_data * board ;
2014-02-05 04:58:02 +04:00
# ifdef CONFIG_DEBUG_FS
2012-02-06 13:24:43 +04:00
char name [ 32 ] ; /* for debugfs file name */
2014-02-05 04:58:02 +04:00
# endif
2011-02-23 04:03:19 +03:00
board = & drv_dat [ id - > driver_data ] ;
port_type = board - > port_type ;
2010-11-17 03:55:54 +03:00
priv = kzalloc ( sizeof ( struct eg20t_port ) , GFP_KERNEL ) ;
if ( priv = = NULL )
goto init_port_alloc_err ;
2010-12-16 10:13:29 +03:00
rxbuf = ( unsigned char * ) __get_free_page ( GFP_KERNEL ) ;
2010-11-17 03:55:54 +03:00
if ( ! rxbuf )
goto init_port_free_txbuf ;
switch ( port_type ) {
case PORT_UNKNOWN :
2011-01-28 12:00:01 +03:00
fifosize = 256 ; /* EG20T/ML7213: UART0 */
2010-11-17 03:55:54 +03:00
break ;
case PORT_8250 :
2011-01-28 12:00:01 +03:00
fifosize = 64 ; /* EG20T:UART1~3 ML7213: UART1~2*/
2010-11-17 03:55:54 +03:00
break ;
default :
dev_err ( & pdev - > dev , " Invalid Port Type(=%d) \n " , port_type ) ;
goto init_port_hal_free ;
}
2011-07-04 10:58:31 +04:00
pci_enable_msi ( pdev ) ;
2012-04-02 09:36:22 +04:00
pci_set_master ( pdev ) ;
2011-07-04 10:58:31 +04:00
2012-06-20 01:00:18 +04:00
spin_lock_init ( & priv - > lock ) ;
2010-11-17 03:55:54 +03:00
iobase = pci_resource_start ( pdev , 0 ) ;
mapbase = pci_resource_start ( pdev , 1 ) ;
priv - > mapbase = mapbase ;
priv - > iobase = iobase ;
priv - > pdev = pdev ;
priv - > tx_empty = 1 ;
2010-12-16 10:13:29 +03:00
priv - > rxbuf . buf = rxbuf ;
2010-11-17 03:55:54 +03:00
priv - > rxbuf . size = PAGE_SIZE ;
priv - > fifo_size = fifosize ;
2012-03-09 21:51:49 +04:00
priv - > uartclk = pch_uart_get_uartclk ( ) ;
2010-11-17 03:55:54 +03:00
priv - > port_type = PORT_MAX_8250 + port_type + 1 ;
priv - > port . dev = & pdev - > dev ;
priv - > port . iobase = iobase ;
priv - > port . membase = NULL ;
priv - > port . mapbase = mapbase ;
priv - > port . irq = pdev - > irq ;
priv - > port . iotype = UPIO_PORT ;
priv - > port . ops = & pch_uart_ops ;
priv - > port . flags = UPF_BOOT_AUTOCONF ;
priv - > port . fifosize = fifosize ;
2011-02-23 04:03:19 +03:00
priv - > port . line = board - > line_no ;
2010-11-17 03:55:54 +03:00
priv - > trigger = PCH_UART_HAL_TRIGGER_M ;
2014-03-25 17:05:08 +04:00
snprintf ( priv - > irq_name , IRQ_NAME_SIZE ,
KBUILD_MODNAME " : " PCH_UART_DRIVER_DEVICE " %d " ,
priv - > port . line ) ;
2011-02-23 04:03:13 +03:00
spin_lock_init ( & priv - > port . lock ) ;
2010-11-17 03:55:54 +03:00
pci_set_drvdata ( pdev , priv ) ;
2012-02-06 13:24:45 +04:00
priv - > trigger_level = 1 ;
priv - > fcr = 0 ;
2011-01-28 12:00:01 +03:00
2011-11-16 03:04:07 +04:00
# ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
pch_uart_ports [ board - > line_no ] = priv ;
# endif
2010-11-17 03:55:54 +03:00
ret = uart_add_one_port ( & pch_uart_driver , & priv - > port ) ;
if ( ret < 0 )
goto init_port_hal_free ;
2012-02-06 13:24:43 +04:00
# ifdef CONFIG_DEBUG_FS
snprintf ( name , sizeof ( name ) , " uart%d_regs " , board - > line_no ) ;
priv - > debugfs = debugfs_create_file ( name , S_IFREG | S_IRUGO ,
NULL , priv , & port_regs_ops ) ;
# endif
2010-11-17 03:55:54 +03:00
return priv ;
init_port_hal_free :
2011-11-16 03:04:07 +04:00
# ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
pch_uart_ports [ board - > line_no ] = NULL ;
# endif
2010-12-16 10:13:29 +03:00
free_page ( ( unsigned long ) rxbuf ) ;
2010-11-17 03:55:54 +03:00
init_port_free_txbuf :
kfree ( priv ) ;
init_port_alloc_err :
return NULL ;
}
static void pch_uart_exit_port ( struct eg20t_port * priv )
{
2012-02-06 13:24:43 +04:00
# ifdef CONFIG_DEBUG_FS
if ( priv - > debugfs )
debugfs_remove ( priv - > debugfs ) ;
# endif
2010-11-17 03:55:54 +03:00
uart_remove_one_port ( & pch_uart_driver , & priv - > port ) ;
2010-12-16 10:13:29 +03:00
free_page ( ( unsigned long ) priv - > rxbuf . buf ) ;
2010-11-17 03:55:54 +03:00
}
static void pch_uart_pci_remove ( struct pci_dev * pdev )
{
2012-02-06 13:24:45 +04:00
struct eg20t_port * priv = pci_get_drvdata ( pdev ) ;
2011-07-04 10:58:31 +04:00
pci_disable_msi ( pdev ) ;
2011-11-16 03:04:07 +04:00
# ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
pch_uart_ports [ priv - > port . line ] = NULL ;
# endif
2010-11-17 03:55:54 +03:00
pch_uart_exit_port ( priv ) ;
pci_disable_device ( pdev ) ;
kfree ( priv ) ;
return ;
}
# ifdef CONFIG_PM
static int pch_uart_pci_suspend ( struct pci_dev * pdev , pm_message_t state )
{
struct eg20t_port * priv = pci_get_drvdata ( pdev ) ;
uart_suspend_port ( & pch_uart_driver , & priv - > port ) ;
pci_save_state ( pdev ) ;
pci_set_power_state ( pdev , pci_choose_state ( pdev , state ) ) ;
return 0 ;
}
static int pch_uart_pci_resume ( struct pci_dev * pdev )
{
struct eg20t_port * priv = pci_get_drvdata ( pdev ) ;
int ret ;
pci_set_power_state ( pdev , PCI_D0 ) ;
pci_restore_state ( pdev ) ;
ret = pci_enable_device ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" %s-pci_enable_device failed(ret=%d) " , __func__ , ret ) ;
return ret ;
}
uart_resume_port ( & pch_uart_driver , & priv - > port ) ;
return 0 ;
}
# else
# define pch_uart_pci_suspend NULL
# define pch_uart_pci_resume NULL
# endif
2013-12-03 03:26:37 +04:00
static const struct pci_device_id pch_uart_pci_id [ ] = {
2010-11-17 03:55:54 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , 0x8811 ) ,
2011-02-23 04:03:19 +03:00
. driver_data = pch_et20t_uart0 } ,
2010-11-17 03:55:54 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , 0x8812 ) ,
2011-02-23 04:03:19 +03:00
. driver_data = pch_et20t_uart1 } ,
2010-11-17 03:55:54 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , 0x8813 ) ,
2011-02-23 04:03:19 +03:00
. driver_data = pch_et20t_uart2 } ,
2010-11-17 03:55:54 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , 0x8814 ) ,
2011-02-23 04:03:19 +03:00
. driver_data = pch_et20t_uart3 } ,
2011-01-28 12:00:01 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_ROHM , 0x8027 ) ,
2011-02-23 04:03:19 +03:00
. driver_data = pch_ml7213_uart0 } ,
2011-01-28 12:00:01 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_ROHM , 0x8028 ) ,
2011-02-23 04:03:19 +03:00
. driver_data = pch_ml7213_uart1 } ,
2011-01-28 12:00:01 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_ROHM , 0x8029 ) ,
2011-02-23 04:03:19 +03:00
. driver_data = pch_ml7213_uart2 } ,
2011-05-09 12:25:20 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_ROHM , 0x800C ) ,
. driver_data = pch_ml7223_uart0 } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ROHM , 0x800D ) ,
. driver_data = pch_ml7223_uart1 } ,
2011-10-28 04:38:49 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_ROHM , 0x8811 ) ,
. driver_data = pch_ml7831_uart0 } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ROHM , 0x8812 ) ,
. driver_data = pch_ml7831_uart1 } ,
2010-11-17 03:55:54 +03:00
{ 0 , } ,
} ;
2012-11-19 22:21:50 +04:00
static int pch_uart_pci_probe ( struct pci_dev * pdev ,
2010-11-17 03:55:54 +03:00
const struct pci_device_id * id )
{
int ret ;
struct eg20t_port * priv ;
ret = pci_enable_device ( pdev ) ;
if ( ret < 0 )
goto probe_error ;
2011-01-28 12:00:01 +03:00
priv = pch_uart_init_port ( pdev , id ) ;
2010-11-17 03:55:54 +03:00
if ( ! priv ) {
ret = - EBUSY ;
goto probe_disable_device ;
}
pci_set_drvdata ( pdev , priv ) ;
return ret ;
probe_disable_device :
2011-07-04 10:58:31 +04:00
pci_disable_msi ( pdev ) ;
2010-11-17 03:55:54 +03:00
pci_disable_device ( pdev ) ;
probe_error :
return ret ;
}
static struct pci_driver pch_uart_pci_driver = {
. name = " pch_uart " ,
. id_table = pch_uart_pci_id ,
. probe = pch_uart_pci_probe ,
2012-11-19 22:21:34 +04:00
. remove = pch_uart_pci_remove ,
2010-11-17 03:55:54 +03:00
. suspend = pch_uart_pci_suspend ,
. resume = pch_uart_pci_resume ,
} ;
static int __init pch_uart_module_init ( void )
{
int ret ;
/* register as UART driver */
ret = uart_register_driver ( & pch_uart_driver ) ;
if ( ret < 0 )
return ret ;
/* register as PCI driver */
ret = pci_register_driver ( & pch_uart_pci_driver ) ;
if ( ret < 0 )
uart_unregister_driver ( & pch_uart_driver ) ;
return ret ;
}
module_init ( pch_uart_module_init ) ;
static void __exit pch_uart_module_exit ( void )
{
pci_unregister_driver ( & pch_uart_pci_driver ) ;
uart_unregister_driver ( & pch_uart_driver ) ;
}
module_exit ( pch_uart_module_exit ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Intel EG20T PCH UART PCI Driver " ) ;
2013-09-01 22:26:37 +04:00
MODULE_DEVICE_TABLE ( pci , pch_uart_pci_id ) ;
2010-11-17 03:55:54 +03:00
module_param ( default_baud , uint , S_IRUGO ) ;
2012-03-09 21:51:52 +04:00
MODULE_PARM_DESC ( default_baud ,
" Default BAUD for initial driver state and console (default 9600) " ) ;
2012-03-09 21:51:50 +04:00
module_param ( user_uartclk , uint , S_IRUGO ) ;
2012-03-09 21:51:52 +04:00
MODULE_PARM_DESC ( user_uartclk ,
" Override UART default or board specific UART clock " ) ;