2011-08-26 19:04:50 +01:00
/*
* Synopsys DesignWare 8250 driver .
*
* Copyright 2011 Picochip , Jamie Iles .
2013-01-10 11:25:10 +02:00
* Copyright 2013 Intel Corporation
2011-08-26 19:04:50 +01: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 ; either version 2 of the License , or
* ( at your option ) any later version .
*
* The Synopsys DesignWare 8250 has an extra feature whereby it detects if the
* LCR is written whilst busy . If it is , then a busy detect interrupt is
* raised , the LCR needs to be rewritten and the uart status register read .
*/
# include <linux/device.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/serial_8250.h>
# include <linux/serial_core.h>
# include <linux/serial_reg.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
2013-01-10 11:25:10 +02:00
# include <linux/acpi.h>
2013-03-29 00:15:49 +01:00
# include <linux/clk.h>
2013-04-10 16:58:28 +03:00
# include <linux/pm_runtime.h>
2011-08-26 19:04:50 +01:00
2013-01-10 11:25:12 +02:00
# include "8250.h"
2013-01-10 11:25:09 +02:00
/* Offsets for the DesignWare specific registers */
# define DW_UART_USR 0x1f /* UART Status Register */
# define DW_UART_CPR 0xf4 /* Component Parameter Register */
# define DW_UART_UCV 0xf8 /* UART Component Version */
/* Component Parameter Register bits */
# define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
# define DW_UART_CPR_AFCE_MODE (1 << 4)
# define DW_UART_CPR_THRE_MODE (1 << 5)
# define DW_UART_CPR_SIR_MODE (1 << 6)
# define DW_UART_CPR_SIR_LP_MODE (1 << 7)
# define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8)
# define DW_UART_CPR_FIFO_ACCESS (1 << 9)
# define DW_UART_CPR_FIFO_STAT (1 << 10)
# define DW_UART_CPR_SHADOW (1 << 11)
# define DW_UART_CPR_ENCODED_PARMS (1 << 12)
# define DW_UART_CPR_DMA_EXTRA (1 << 13)
# define DW_UART_CPR_FIFO_MODE (0xff << 16)
/* Helper for fifo size calculation */
# define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16)
2011-08-26 19:04:50 +01:00
struct dw8250_data {
2013-03-29 00:15:49 +01:00
int last_lcr ;
int line ;
struct clk * clk ;
2011-08-26 19:04:50 +01:00
} ;
static void dw8250_serial_out ( struct uart_port * p , int offset , int value )
{
struct dw8250_data * d = p - > private_data ;
if ( offset = = UART_LCR )
d - > last_lcr = value ;
offset < < = p - > regshift ;
writeb ( value , p - > membase + offset ) ;
}
static unsigned int dw8250_serial_in ( struct uart_port * p , int offset )
{
offset < < = p - > regshift ;
return readb ( p - > membase + offset ) ;
}
static void dw8250_serial_out32 ( struct uart_port * p , int offset , int value )
{
struct dw8250_data * d = p - > private_data ;
if ( offset = = UART_LCR )
d - > last_lcr = value ;
offset < < = p - > regshift ;
writel ( value , p - > membase + offset ) ;
}
static unsigned int dw8250_serial_in32 ( struct uart_port * p , int offset )
{
offset < < = p - > regshift ;
return readl ( p - > membase + offset ) ;
}
static int dw8250_handle_irq ( struct uart_port * p )
{
struct dw8250_data * d = p - > private_data ;
unsigned int iir = p - > serial_in ( p , UART_IIR ) ;
if ( serial8250_handle_irq ( p , iir ) ) {
return 1 ;
} else if ( ( iir & UART_IIR_BUSY ) = = UART_IIR_BUSY ) {
/* Clear the USR and write the LCR again. */
2013-01-10 11:25:09 +02:00
( void ) p - > serial_in ( p , DW_UART_USR ) ;
2013-01-14 20:09:26 +01:00
p - > serial_out ( p , UART_LCR , d - > last_lcr ) ;
2011-08-26 19:04:50 +01:00
return 1 ;
}
return 0 ;
}
2013-04-10 16:58:28 +03:00
static void
dw8250_do_pm ( struct uart_port * port , unsigned int state , unsigned int old )
{
if ( ! state )
pm_runtime_get_sync ( port - > dev ) ;
serial8250_do_pm ( port , state , old ) ;
if ( state )
pm_runtime_put_sync_suspend ( port - > dev ) ;
}
2013-01-10 11:25:08 +02:00
static int dw8250_probe_of ( struct uart_port * p )
{
struct device_node * np = p - > dev - > of_node ;
u32 val ;
if ( ! of_property_read_u32 ( np , " reg-io-width " , & val ) ) {
switch ( val ) {
case 1 :
break ;
case 4 :
p - > iotype = UPIO_MEM32 ;
p - > serial_in = dw8250_serial_in32 ;
p - > serial_out = dw8250_serial_out32 ;
break ;
default :
dev_err ( p - > dev , " unsupported reg-io-width (%u) \n " , val ) ;
return - EINVAL ;
}
}
if ( ! of_property_read_u32 ( np , " reg-shift " , & val ) )
p - > regshift = val ;
2013-03-29 00:15:49 +01:00
/* clock got configured through clk api, all done */
if ( p - > uartclk )
return 0 ;
/* try to find out clock frequency from DT as fallback */
2013-01-10 11:25:08 +02:00
if ( of_property_read_u32 ( np , " clock-frequency " , & val ) ) {
2013-03-29 00:15:49 +01:00
dev_err ( p - > dev , " clk or clock-frequency not defined \n " ) ;
2013-01-10 11:25:08 +02:00
return - EINVAL ;
}
p - > uartclk = val ;
return 0 ;
}
2013-01-16 14:08:15 +02:00
# ifdef CONFIG_ACPI
2013-04-10 16:58:30 +03:00
static int dw8250_probe_acpi ( struct uart_8250_port * up )
2013-01-10 11:25:10 +02:00
{
const struct acpi_device_id * id ;
2013-04-10 16:58:30 +03:00
struct uart_port * p = & up - > port ;
2013-01-10 11:25:10 +02:00
id = acpi_match_device ( p - > dev - > driver - > acpi_match_table , p - > dev ) ;
if ( ! id )
return - ENODEV ;
p - > iotype = UPIO_MEM32 ;
p - > serial_in = dw8250_serial_in32 ;
p - > serial_out = dw8250_serial_out32 ;
p - > regshift = 2 ;
2013-04-10 16:58:29 +03:00
if ( ! p - > uartclk )
p - > uartclk = ( unsigned int ) id - > driver_data ;
2013-01-10 11:25:10 +02:00
2013-04-10 16:58:30 +03:00
up - > dma = devm_kzalloc ( p - > dev , sizeof ( * up - > dma ) , GFP_KERNEL ) ;
if ( ! up - > dma )
return - ENOMEM ;
up - > dma - > rxconf . src_maxburst = p - > fifosize / 4 ;
up - > dma - > txconf . dst_maxburst = p - > fifosize / 4 ;
2013-01-10 11:25:12 +02:00
2013-01-10 11:25:10 +02:00
return 0 ;
}
2013-01-16 14:08:15 +02:00
# else
2013-04-12 12:44:27 +03:00
static inline int dw8250_probe_acpi ( struct uart_8250_port * up )
2013-01-16 14:08:15 +02:00
{
return - ENODEV ;
}
# endif /* CONFIG_ACPI */
2013-01-10 11:25:10 +02:00
2013-01-10 11:25:09 +02:00
static void dw8250_setup_port ( struct uart_8250_port * up )
{
struct uart_port * p = & up - > port ;
u32 reg = readl ( p - > membase + DW_UART_UCV ) ;
/*
* If the Component Version Register returns zero , we know that
* ADDITIONAL_FEATURES are not enabled . No need to go any further .
*/
if ( ! reg )
return ;
dev_dbg_ratelimited ( p - > dev , " Designware UART version %c.%c%c \n " ,
( reg > > 24 ) & 0xff , ( reg > > 16 ) & 0xff , ( reg > > 8 ) & 0xff ) ;
reg = readl ( p - > membase + DW_UART_CPR ) ;
if ( ! reg )
return ;
/* Select the type based on fifo */
if ( reg & DW_UART_CPR_FIFO_MODE ) {
p - > type = PORT_16550A ;
p - > flags | = UPF_FIXED_TYPE ;
p - > fifosize = DW_UART_CPR_FIFO_SIZE ( reg ) ;
up - > tx_loadsz = p - > fifosize ;
2013-04-10 16:58:31 +03:00
up - > capabilities = UART_CAP_FIFO ;
2013-01-10 11:25:09 +02:00
}
2013-04-10 16:58:31 +03:00
if ( reg & DW_UART_CPR_AFCE_MODE )
up - > capabilities | = UART_CAP_AFE ;
2013-01-10 11:25:09 +02:00
}
2012-11-19 13:21:50 -05:00
static int dw8250_probe ( struct platform_device * pdev )
2011-08-26 19:04:50 +01:00
{
2012-07-12 12:59:50 +01:00
struct uart_8250_port uart = { } ;
2011-08-26 19:04:50 +01:00
struct resource * regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
struct resource * irq = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
struct dw8250_data * data ;
2013-01-10 11:25:08 +02:00
int err ;
2011-08-26 19:04:50 +01:00
if ( ! regs | | ! irq ) {
dev_err ( & pdev - > dev , " no registers/irq defined \n " ) ;
return - EINVAL ;
}
2012-07-12 12:59:50 +01:00
spin_lock_init ( & uart . port . lock ) ;
uart . port . mapbase = regs - > start ;
uart . port . irq = irq - > start ;
uart . port . handle_irq = dw8250_handle_irq ;
2013-04-10 16:58:28 +03:00
uart . port . pm = dw8250_do_pm ;
2012-07-12 12:59:50 +01:00
uart . port . type = PORT_8250 ;
2013-01-10 11:25:07 +02:00
uart . port . flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT ;
2012-07-12 12:59:50 +01:00
uart . port . dev = & pdev - > dev ;
2011-08-26 19:04:50 +01:00
2013-04-11 15:43:21 +03:00
uart . port . membase = devm_ioremap ( & pdev - > dev , regs - > start ,
resource_size ( regs ) ) ;
2013-01-10 11:25:07 +02:00
if ( ! uart . port . membase )
return - ENOMEM ;
2013-03-29 00:15:49 +01:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( ! IS_ERR ( data - > clk ) ) {
clk_prepare_enable ( data - > clk ) ;
uart . port . uartclk = clk_get_rate ( data - > clk ) ;
}
2012-07-12 12:59:50 +01:00
uart . port . iotype = UPIO_MEM ;
uart . port . serial_in = dw8250_serial_in ;
uart . port . serial_out = dw8250_serial_out ;
2013-03-29 00:15:49 +01:00
uart . port . private_data = data ;
2013-01-10 11:25:08 +02:00
2013-01-16 14:08:16 +02:00
dw8250_setup_port ( & uart ) ;
2013-01-10 11:25:08 +02:00
if ( pdev - > dev . of_node ) {
err = dw8250_probe_of ( & uart . port ) ;
if ( err )
return err ;
2013-01-10 11:25:10 +02:00
} else if ( ACPI_HANDLE ( & pdev - > dev ) ) {
2013-04-10 16:58:30 +03:00
err = dw8250_probe_acpi ( & uart ) ;
2013-01-10 11:25:10 +02:00
if ( err )
return err ;
2013-01-10 11:25:08 +02:00
} else {
return - ENODEV ;
2011-08-26 19:04:50 +01:00
}
2012-07-12 12:59:50 +01:00
data - > line = serial8250_register_8250_port ( & uart ) ;
2011-08-26 19:04:50 +01:00
if ( data - > line < 0 )
return data - > line ;
platform_set_drvdata ( pdev , data ) ;
2013-04-10 16:58:28 +03:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2011-08-26 19:04:50 +01:00
return 0 ;
}
2012-11-19 13:26:18 -05:00
static int dw8250_remove ( struct platform_device * pdev )
2011-08-26 19:04:50 +01:00
{
struct dw8250_data * data = platform_get_drvdata ( pdev ) ;
2013-04-10 16:58:28 +03:00
pm_runtime_get_sync ( & pdev - > dev ) ;
2011-08-26 19:04:50 +01:00
serial8250_unregister_port ( data - > line ) ;
2013-03-29 00:15:49 +01:00
if ( ! IS_ERR ( data - > clk ) )
clk_disable_unprepare ( data - > clk ) ;
2013-04-10 16:58:28 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_put_noidle ( & pdev - > dev ) ;
2011-08-26 19:04:50 +01:00
return 0 ;
}
2012-10-15 10:25:58 +01:00
# ifdef CONFIG_PM
2013-04-10 16:58:28 +03:00
static int dw8250_suspend ( struct device * dev )
2012-10-15 10:25:58 +01:00
{
2013-04-10 16:58:28 +03:00
struct dw8250_data * data = dev_get_drvdata ( dev ) ;
2012-10-15 10:25:58 +01:00
serial8250_suspend_port ( data - > line ) ;
return 0 ;
}
2013-04-10 16:58:28 +03:00
static int dw8250_resume ( struct device * dev )
2012-10-15 10:25:58 +01:00
{
2013-04-10 16:58:28 +03:00
struct dw8250_data * data = dev_get_drvdata ( dev ) ;
2012-10-15 10:25:58 +01:00
serial8250_resume_port ( data - > line ) ;
return 0 ;
}
# endif /* CONFIG_PM */
2013-04-10 16:58:28 +03:00
# ifdef CONFIG_PM_RUNTIME
static int dw8250_runtime_suspend ( struct device * dev )
{
struct dw8250_data * data = dev_get_drvdata ( dev ) ;
clk_disable_unprepare ( data - > clk ) ;
return 0 ;
}
static int dw8250_runtime_resume ( struct device * dev )
{
struct dw8250_data * data = dev_get_drvdata ( dev ) ;
clk_prepare_enable ( data - > clk ) ;
return 0 ;
}
# endif
static const struct dev_pm_ops dw8250_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( dw8250_suspend , dw8250_resume )
SET_RUNTIME_PM_OPS ( dw8250_runtime_suspend , dw8250_runtime_resume , NULL )
} ;
2013-01-10 11:25:08 +02:00
static const struct of_device_id dw8250_of_match [ ] = {
2011-08-26 19:04:50 +01:00
{ . compatible = " snps,dw-apb-uart " } ,
{ /* Sentinel */ }
} ;
2013-01-10 11:25:08 +02:00
MODULE_DEVICE_TABLE ( of , dw8250_of_match ) ;
2011-08-26 19:04:50 +01:00
2013-01-10 11:25:10 +02:00
static const struct acpi_device_id dw8250_acpi_match [ ] = {
2013-04-10 16:58:29 +03:00
{ " INT33C4 " , 0 } ,
{ " INT33C5 " , 0 } ,
2013-01-10 11:25:10 +02:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , dw8250_acpi_match ) ;
2011-08-26 19:04:50 +01:00
static struct platform_driver dw8250_platform_driver = {
. driver = {
. name = " dw-apb-uart " ,
. owner = THIS_MODULE ,
2013-04-10 16:58:28 +03:00
. pm = & dw8250_pm_ops ,
2013-01-10 11:25:08 +02:00
. of_match_table = dw8250_of_match ,
2013-01-10 11:25:10 +02:00
. acpi_match_table = ACPI_PTR ( dw8250_acpi_match ) ,
2011-08-26 19:04:50 +01:00
} ,
. probe = dw8250_probe ,
2012-11-19 13:21:34 -05:00
. remove = dw8250_remove ,
2011-08-26 19:04:50 +01:00
} ;
2011-11-28 19:22:15 +08:00
module_platform_driver ( dw8250_platform_driver ) ;
2011-08-26 19:04:50 +01:00
MODULE_AUTHOR ( " Jamie Iles " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Synopsys DesignWare 8250 serial port driver " ) ;