2017-11-06 18:11:51 +01:00
// SPDX-License-Identifier: GPL-2.0
2015-10-13 13:29:02 +03:00
/*
* 8250 _mid . c - Driver for UART on Intel Penwell and various other Intel SOCs
*
* Copyright ( C ) 2015 Intel Corporation
* Author : Heikki Krogerus < heikki . krogerus @ linux . intel . com >
*/
2016-04-04 17:35:11 +03:00
# include <linux/bitops.h>
2015-10-13 13:29:02 +03:00
# include <linux/module.h>
# include <linux/pci.h>
2016-04-04 17:35:11 +03:00
# include <linux/rational.h>
2015-10-13 13:29:02 +03:00
# include <linux/dma/hsu.h>
2016-04-04 17:35:09 +03:00
# include <linux/8250_pci.h>
2015-10-13 13:29:02 +03:00
# include "8250.h"
# define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b
# define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c
# define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d
# define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191
2017-09-22 15:11:56 +03:00
# define PCI_DEVICE_ID_INTEL_CDF_UART 0x18d8
2015-10-13 13:29:06 +03:00
# define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8
2015-10-13 13:29:02 +03:00
/* Intel MID Specific registers */
2017-09-22 15:11:56 +03:00
# define INTEL_MID_UART_FISR 0x08
2015-10-13 13:29:02 +03:00
# define INTEL_MID_UART_PS 0x30
# define INTEL_MID_UART_MUL 0x34
# define INTEL_MID_UART_DIV 0x38
struct mid8250 ;
struct mid8250_board {
2016-04-04 17:35:09 +03:00
unsigned int flags ;
2015-10-13 13:29:02 +03:00
unsigned long freq ;
unsigned int base_baud ;
int ( * setup ) ( struct mid8250 * , struct uart_port * p ) ;
2015-10-13 13:29:06 +03:00
void ( * exit ) ( struct mid8250 * ) ;
2015-10-13 13:29:02 +03:00
} ;
struct mid8250 {
int line ;
int dma_index ;
struct pci_dev * dma_dev ;
struct uart_8250_dma dma ;
struct mid8250_board * board ;
2015-10-13 13:29:06 +03:00
struct hsu_dma_chip dma_chip ;
2015-10-13 13:29:02 +03:00
} ;
/*****************************************************************************/
static int pnw_setup ( struct mid8250 * mid , struct uart_port * p )
{
struct pci_dev * pdev = to_pci_dev ( p - > dev ) ;
switch ( pdev - > device ) {
case PCI_DEVICE_ID_INTEL_PNW_UART1 :
mid - > dma_index = 0 ;
break ;
case PCI_DEVICE_ID_INTEL_PNW_UART2 :
mid - > dma_index = 1 ;
break ;
case PCI_DEVICE_ID_INTEL_PNW_UART3 :
mid - > dma_index = 2 ;
break ;
default :
return - EINVAL ;
}
mid - > dma_dev = pci_get_slot ( pdev - > bus ,
PCI_DEVFN ( PCI_SLOT ( pdev - > devfn ) , 3 ) ) ;
return 0 ;
}
2017-01-11 16:31:35 +02:00
static int tng_handle_irq ( struct uart_port * p )
{
struct mid8250 * mid = p - > private_data ;
struct uart_8250_port * up = up_to_u8250p ( p ) ;
struct hsu_dma_chip * chip ;
u32 status ;
int ret = 0 ;
int err ;
chip = pci_get_drvdata ( mid - > dma_dev ) ;
/* Rx DMA */
err = hsu_dma_get_status ( chip , mid - > dma_index * 2 + 1 , & status ) ;
if ( err > 0 ) {
serial8250_rx_dma_flush ( up ) ;
ret | = 1 ;
} else if ( err = = 0 )
ret | = hsu_dma_do_irq ( chip , mid - > dma_index * 2 + 1 , status ) ;
/* Tx DMA */
err = hsu_dma_get_status ( chip , mid - > dma_index * 2 , & status ) ;
if ( err > 0 )
ret | = 1 ;
else if ( err = = 0 )
ret | = hsu_dma_do_irq ( chip , mid - > dma_index * 2 , status ) ;
/* UART */
ret | = serial8250_handle_irq ( p , serial_port_in ( p , UART_IIR ) ) ;
return IRQ_RETVAL ( ret ) ;
}
2015-10-13 13:29:02 +03:00
static int tng_setup ( struct mid8250 * mid , struct uart_port * p )
{
struct pci_dev * pdev = to_pci_dev ( p - > dev ) ;
int index = PCI_FUNC ( pdev - > devfn ) ;
2016-04-04 17:35:13 +03:00
/*
* Device 0000 : 00 : 04.0 is not a real HSU port . It provides a global
* register set for all HSU ports , although it has the same PCI ID .
* Skip it here .
*/
2015-10-13 13:29:02 +03:00
if ( index - - = = 0 )
return - ENODEV ;
mid - > dma_index = index ;
mid - > dma_dev = pci_get_slot ( pdev - > bus , PCI_DEVFN ( 5 , 0 ) ) ;
2017-01-11 16:31:35 +02:00
p - > handle_irq = tng_handle_irq ;
2015-10-13 13:29:02 +03:00
return 0 ;
}
2015-10-13 13:29:06 +03:00
static int dnv_handle_irq ( struct uart_port * p )
{
struct mid8250 * mid = p - > private_data ;
2016-06-15 13:44:13 +08:00
struct uart_8250_port * up = up_to_u8250p ( p ) ;
2017-09-22 15:11:56 +03:00
unsigned int fisr = serial_port_in ( p , INTEL_MID_UART_FISR ) ;
2016-06-15 13:44:11 +08:00
u32 status ;
2016-08-23 16:09:40 +03:00
int ret = 0 ;
2016-06-15 13:44:11 +08:00
int err ;
if ( fisr & BIT ( 2 ) ) {
err = hsu_dma_get_status ( & mid - > dma_chip , 1 , & status ) ;
2016-06-15 13:44:13 +08:00
if ( err > 0 ) {
serial8250_rx_dma_flush ( up ) ;
2016-08-23 16:09:40 +03:00
ret | = 1 ;
2016-06-15 13:44:13 +08:00
} else if ( err = = 0 )
2016-06-15 13:44:11 +08:00
ret | = hsu_dma_do_irq ( & mid - > dma_chip , 1 , status ) ;
}
if ( fisr & BIT ( 1 ) ) {
err = hsu_dma_get_status ( & mid - > dma_chip , 0 , & status ) ;
if ( err > 0 )
2016-08-23 16:09:40 +03:00
ret | = 1 ;
2016-06-15 13:44:11 +08:00
else if ( err = = 0 )
ret | = hsu_dma_do_irq ( & mid - > dma_chip , 0 , status ) ;
}
2016-04-04 17:35:10 +03:00
if ( fisr & BIT ( 0 ) )
ret | = serial8250_handle_irq ( p , serial_port_in ( p , UART_IIR ) ) ;
2016-08-23 16:09:40 +03:00
return IRQ_RETVAL ( ret ) ;
2015-10-13 13:29:06 +03:00
}
# define DNV_DMA_CHAN_OFFSET 0x80
static int dnv_setup ( struct mid8250 * mid , struct uart_port * p )
{
struct hsu_dma_chip * chip = & mid - > dma_chip ;
struct pci_dev * pdev = to_pci_dev ( p - > dev ) ;
2016-04-04 17:35:09 +03:00
unsigned int bar = FL_GET_BASE ( mid - > board - > flags ) ;
2015-10-13 13:29:06 +03:00
int ret ;
2017-01-11 16:31:37 +02:00
pci_set_master ( pdev ) ;
2017-01-11 16:31:36 +02:00
ret = pci_alloc_irq_vectors ( pdev , 1 , 1 , PCI_IRQ_ALL_TYPES ) ;
if ( ret < 0 )
return ret ;
p - > irq = pci_irq_vector ( pdev , 0 ) ;
2015-10-13 13:29:06 +03:00
chip - > dev = & pdev - > dev ;
2017-01-11 16:31:36 +02:00
chip - > irq = pci_irq_vector ( pdev , 0 ) ;
2015-10-13 13:29:06 +03:00
chip - > regs = p - > membase ;
2016-04-04 17:35:09 +03:00
chip - > length = pci_resource_len ( pdev , bar ) ;
2015-10-13 13:29:06 +03:00
chip - > offset = DNV_DMA_CHAN_OFFSET ;
/* Falling back to PIO mode if DMA probing fails */
ret = hsu_dma_probe ( chip ) ;
if ( ret )
return 0 ;
mid - > dma_dev = pdev ;
p - > handle_irq = dnv_handle_irq ;
return 0 ;
}
static void dnv_exit ( struct mid8250 * mid )
{
if ( ! mid - > dma_dev )
return ;
hsu_dma_remove ( & mid - > dma_chip ) ;
}
2015-10-13 13:29:02 +03:00
/*****************************************************************************/
static void mid8250_set_termios ( struct uart_port * p ,
struct ktermios * termios ,
struct ktermios * old )
{
unsigned int baud = tty_termios_baud_rate ( termios ) ;
struct mid8250 * mid = p - > private_data ;
unsigned short ps = 16 ;
unsigned long fuart = baud * ps ;
unsigned long w = BIT ( 24 ) - 1 ;
unsigned long mul , div ;
2016-07-01 17:21:49 +03:00
/* Gracefully handle the B0 case: fall back to B9600 */
fuart = fuart ? fuart : 9600 * 16 ;
2015-10-13 13:29:02 +03:00
if ( mid - > board - > freq < fuart ) {
/* Find prescaler value that satisfies Fuart < Fref */
if ( mid - > board - > freq > baud )
ps = mid - > board - > freq / baud ; /* baud rate too high */
else
ps = 1 ; /* PLL case */
fuart = baud * ps ;
} else {
/* Get Fuart closer to Fref */
fuart * = rounddown_pow_of_two ( mid - > board - > freq / fuart ) ;
}
rational_best_approximation ( fuart , mid - > board - > freq , w , w , & mul , & div ) ;
p - > uartclk = fuart * 16 / ps ; /* core uses ps = 16 always */
writel ( ps , p - > membase + INTEL_MID_UART_PS ) ; /* set PS */
writel ( mul , p - > membase + INTEL_MID_UART_MUL ) ; /* set MUL */
writel ( div , p - > membase + INTEL_MID_UART_DIV ) ;
serial8250_do_set_termios ( p , termios , old ) ;
}
static bool mid8250_dma_filter ( struct dma_chan * chan , void * param )
{
struct hsu_dma_slave * s = param ;
if ( s - > dma_dev ! = chan - > device - > dev | | s - > chan_id ! = chan - > chan_id )
return false ;
chan - > private = s ;
return true ;
}
static int mid8250_dma_setup ( struct mid8250 * mid , struct uart_8250_port * port )
{
struct uart_8250_dma * dma = & mid - > dma ;
struct device * dev = port - > port . dev ;
struct hsu_dma_slave * rx_param ;
struct hsu_dma_slave * tx_param ;
2015-10-13 13:29:06 +03:00
if ( ! mid - > dma_dev )
return 0 ;
2015-10-13 13:29:02 +03:00
rx_param = devm_kzalloc ( dev , sizeof ( * rx_param ) , GFP_KERNEL ) ;
if ( ! rx_param )
return - ENOMEM ;
tx_param = devm_kzalloc ( dev , sizeof ( * tx_param ) , GFP_KERNEL ) ;
if ( ! tx_param )
return - ENOMEM ;
rx_param - > chan_id = mid - > dma_index * 2 + 1 ;
tx_param - > chan_id = mid - > dma_index * 2 ;
dma - > rxconf . src_maxburst = 64 ;
dma - > txconf . dst_maxburst = 64 ;
rx_param - > dma_dev = & mid - > dma_dev - > dev ;
tx_param - > dma_dev = & mid - > dma_dev - > dev ;
dma - > fn = mid8250_dma_filter ;
dma - > rx_param = rx_param ;
dma - > tx_param = tx_param ;
port - > dma = dma ;
return 0 ;
}
static int mid8250_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
struct uart_8250_port uart ;
struct mid8250 * mid ;
2016-04-04 17:35:09 +03:00
unsigned int bar ;
2015-10-13 13:29:02 +03:00
int ret ;
ret = pcim_enable_device ( pdev ) ;
if ( ret )
return ret ;
mid = devm_kzalloc ( & pdev - > dev , sizeof ( * mid ) , GFP_KERNEL ) ;
if ( ! mid )
return - ENOMEM ;
mid - > board = ( struct mid8250_board * ) id - > driver_data ;
2016-04-04 17:35:09 +03:00
bar = FL_GET_BASE ( mid - > board - > flags ) ;
2015-10-13 13:29:02 +03:00
memset ( & uart , 0 , sizeof ( struct uart_8250_port ) ) ;
uart . port . dev = & pdev - > dev ;
uart . port . irq = pdev - > irq ;
uart . port . private_data = mid ;
uart . port . type = PORT_16750 ;
uart . port . iotype = UPIO_MEM ;
uart . port . uartclk = mid - > board - > base_baud * 16 ;
uart . port . flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE ;
uart . port . set_termios = mid8250_set_termios ;
2016-04-04 17:35:09 +03:00
uart . port . mapbase = pci_resource_start ( pdev , bar ) ;
uart . port . membase = pcim_iomap ( pdev , bar , 0 ) ;
2015-10-13 13:29:02 +03:00
if ( ! uart . port . membase )
return - ENOMEM ;
if ( mid - > board - > setup ) {
ret = mid - > board - > setup ( mid , & uart . port ) ;
if ( ret )
return ret ;
}
ret = mid8250_dma_setup ( mid , & uart ) ;
if ( ret )
2015-10-13 13:29:06 +03:00
goto err ;
2015-10-13 13:29:02 +03:00
ret = serial8250_register_8250_port ( & uart ) ;
if ( ret < 0 )
2015-10-13 13:29:06 +03:00
goto err ;
2015-10-13 13:29:02 +03:00
mid - > line = ret ;
pci_set_drvdata ( pdev , mid ) ;
return 0 ;
2015-10-13 13:29:06 +03:00
err :
if ( mid - > board - > exit )
mid - > board - > exit ( mid ) ;
return ret ;
2015-10-13 13:29:02 +03:00
}
static void mid8250_remove ( struct pci_dev * pdev )
{
struct mid8250 * mid = pci_get_drvdata ( pdev ) ;
2016-11-30 01:25:25 -05:00
serial8250_unregister_port ( mid - > line ) ;
2015-10-13 13:29:06 +03:00
if ( mid - > board - > exit )
mid - > board - > exit ( mid ) ;
2015-10-13 13:29:02 +03:00
}
static const struct mid8250_board pnw_board = {
2016-04-04 17:35:09 +03:00
. flags = FL_BASE0 ,
2015-10-13 13:29:02 +03:00
. freq = 50000000 ,
. base_baud = 115200 ,
. setup = pnw_setup ,
} ;
static const struct mid8250_board tng_board = {
2016-04-04 17:35:09 +03:00
. flags = FL_BASE0 ,
2015-10-13 13:29:02 +03:00
. freq = 38400000 ,
. base_baud = 1843200 ,
. setup = tng_setup ,
} ;
2015-10-13 13:29:06 +03:00
static const struct mid8250_board dnv_board = {
2016-04-04 17:35:09 +03:00
. flags = FL_BASE1 ,
2015-10-13 13:29:06 +03:00
. freq = 133333333 ,
. base_baud = 115200 ,
. setup = dnv_setup ,
. exit = dnv_exit ,
} ;
2015-10-13 13:29:02 +03:00
# define MID_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board }
static const struct pci_device_id pci_ids [ ] = {
MID_DEVICE ( PCI_DEVICE_ID_INTEL_PNW_UART1 , pnw_board ) ,
MID_DEVICE ( PCI_DEVICE_ID_INTEL_PNW_UART2 , pnw_board ) ,
MID_DEVICE ( PCI_DEVICE_ID_INTEL_PNW_UART3 , pnw_board ) ,
MID_DEVICE ( PCI_DEVICE_ID_INTEL_TNG_UART , tng_board ) ,
2017-09-22 15:11:56 +03:00
MID_DEVICE ( PCI_DEVICE_ID_INTEL_CDF_UART , dnv_board ) ,
2015-10-13 13:29:06 +03:00
MID_DEVICE ( PCI_DEVICE_ID_INTEL_DNV_UART , dnv_board ) ,
2015-10-13 13:29:02 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( pci , pci_ids ) ;
static struct pci_driver mid8250_pci_driver = {
. name = " 8250_mid " ,
. id_table = pci_ids ,
. probe = mid8250_probe ,
. remove = mid8250_remove ,
} ;
module_pci_driver ( mid8250_pci_driver ) ;
MODULE_AUTHOR ( " Intel Corporation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Intel MID UART driver " ) ;