2017-11-06 18:11:51 +01:00
// SPDX-License-Identifier: GPL-2.0+
2015-05-24 16:11:44 +01:00
/*
* Copyright ( C ) 2010 Lars - Peter Clausen < lars @ metafoo . de >
* Copyright ( C ) 2015 Imagination Technologies
*
* Ingenic SoC UART support
*/
# include <linux/clk.h>
# include <linux/console.h>
# include <linux/io.h>
# include <linux/libfdt.h>
2016-02-17 16:02:32 +01:00
# include <linux/module.h>
2015-05-24 16:11:44 +01:00
# include <linux/of.h>
# include <linux/of_fdt.h>
2015-09-08 16:55:35 +01:00
# include <linux/of_device.h>
2015-05-24 16:11:44 +01:00
# include <linux/platform_device.h>
# include <linux/serial_8250.h>
# include <linux/serial_core.h>
# include <linux/serial_reg.h>
2015-09-08 16:55:35 +01:00
# include "8250.h"
/** ingenic_uart_config: SOC specific config data. */
struct ingenic_uart_config {
int tx_loadsz ;
int fifosize ;
} ;
2015-05-24 16:11:44 +01:00
struct ingenic_uart_data {
struct clk * clk_module ;
struct clk * clk_baud ;
int line ;
} ;
2015-09-08 16:55:35 +01:00
static const struct of_device_id of_match [ ] ;
2015-05-24 16:11:44 +01:00
# define UART_FCR_UME BIT(4)
2015-09-08 16:55:34 +01:00
# define UART_MCR_MDCE BIT(7)
# define UART_MCR_FCM BIT(6)
2015-05-24 16:11:44 +01:00
static struct earlycon_device * early_device ;
2017-07-18 14:02:57 +08:00
static uint8_t early_in ( struct uart_port * port , int offset )
2015-05-24 16:11:44 +01:00
{
return readl ( port - > membase + ( offset < < 2 ) ) ;
}
2017-07-18 14:02:57 +08:00
static void early_out ( struct uart_port * port , int offset , uint8_t value )
2015-05-24 16:11:44 +01:00
{
writel ( value , port - > membase + ( offset < < 2 ) ) ;
}
2017-07-18 14:02:57 +08:00
static void ingenic_early_console_putc ( struct uart_port * port , int c )
2015-05-24 16:11:44 +01:00
{
uint8_t lsr ;
do {
lsr = early_in ( port , UART_LSR ) ;
} while ( ( lsr & UART_LSR_TEMT ) = = 0 ) ;
early_out ( port , UART_TX , c ) ;
}
2017-07-18 14:02:57 +08:00
static void ingenic_early_console_write ( struct console * console ,
2015-05-24 16:11:44 +01:00
const char * s , unsigned int count )
{
uart_console_write ( & early_device - > port , s , count ,
ingenic_early_console_putc ) ;
}
static void __init ingenic_early_console_setup_clock ( struct earlycon_device * dev )
{
void * fdt = initial_boot_params ;
const __be32 * prop ;
int offset ;
offset = fdt_path_offset ( fdt , " /ext " ) ;
if ( offset < 0 )
return ;
prop = fdt_getprop ( fdt , offset , " clock-frequency " , NULL ) ;
if ( ! prop )
return ;
dev - > port . uartclk = be32_to_cpup ( prop ) ;
}
static int __init ingenic_early_console_setup ( struct earlycon_device * dev ,
const char * opt )
{
struct uart_port * port = & dev - > port ;
serial: 8250_ingenic: Parse earlycon options
In the devicetree, it is possible to specify the baudrate, parity,
bits, flow of the early console, by passing a configuration string like
this:
aliases {
serial0 = &uart0;
};
chosen {
stdout-path = "serial0:57600n8";
};
This, for instance, will configure the early console for a baudrate of
57600 bps, no parity, and 8 bits per baud.
This patches implements parsing of this configuration string in the
8250_ingenic driver, which previously just ignored it.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-28 14:07:09 +01:00
unsigned int divisor ;
int baud = 115200 ;
2015-05-24 16:11:44 +01:00
if ( ! dev - > port . membase )
return - ENODEV ;
serial: 8250_ingenic: Parse earlycon options
In the devicetree, it is possible to specify the baudrate, parity,
bits, flow of the early console, by passing a configuration string like
this:
aliases {
serial0 = &uart0;
};
chosen {
stdout-path = "serial0:57600n8";
};
This, for instance, will configure the early console for a baudrate of
57600 bps, no parity, and 8 bits per baud.
This patches implements parsing of this configuration string in the
8250_ingenic driver, which previously just ignored it.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-28 14:07:09 +01:00
if ( opt ) {
unsigned int parity , bits , flow ; /* unused for now */
uart_parse_options ( opt , & baud , & parity , & bits , & flow ) ;
}
2015-05-24 16:11:44 +01:00
ingenic_early_console_setup_clock ( dev ) ;
serial: 8250_ingenic: Parse earlycon options
In the devicetree, it is possible to specify the baudrate, parity,
bits, flow of the early console, by passing a configuration string like
this:
aliases {
serial0 = &uart0;
};
chosen {
stdout-path = "serial0:57600n8";
};
This, for instance, will configure the early console for a baudrate of
57600 bps, no parity, and 8 bits per baud.
This patches implements parsing of this configuration string in the
8250_ingenic driver, which previously just ignored it.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-28 14:07:09 +01:00
if ( dev - > baud )
baud = dev - > baud ;
2015-05-24 16:11:44 +01:00
divisor = DIV_ROUND_CLOSEST ( port - > uartclk , 16 * baud ) ;
early_out ( port , UART_IER , 0 ) ;
early_out ( port , UART_LCR , UART_LCR_DLAB | UART_LCR_WLEN8 ) ;
early_out ( port , UART_DLL , 0 ) ;
early_out ( port , UART_DLM , 0 ) ;
early_out ( port , UART_LCR , UART_LCR_WLEN8 ) ;
early_out ( port , UART_FCR , UART_FCR_UME | UART_FCR_CLEAR_XMIT |
UART_FCR_CLEAR_RCVR | UART_FCR_ENABLE_FIFO ) ;
early_out ( port , UART_MCR , UART_MCR_RTS | UART_MCR_DTR ) ;
early_out ( port , UART_LCR , UART_LCR_DLAB | UART_LCR_WLEN8 ) ;
early_out ( port , UART_DLL , divisor & 0xff ) ;
early_out ( port , UART_DLM , ( divisor > > 8 ) & 0xff ) ;
early_out ( port , UART_LCR , UART_LCR_WLEN8 ) ;
early_device = dev ;
dev - > con - > write = ingenic_early_console_write ;
return 0 ;
}
OF_EARLYCON_DECLARE ( jz4740_uart , " ingenic,jz4740-uart " ,
ingenic_early_console_setup ) ;
2017-12-28 14:07:08 +01:00
OF_EARLYCON_DECLARE ( jz4770_uart , " ingenic,jz4770-uart " ,
ingenic_early_console_setup ) ;
2015-05-24 16:11:44 +01:00
OF_EARLYCON_DECLARE ( jz4775_uart , " ingenic,jz4775-uart " ,
ingenic_early_console_setup ) ;
OF_EARLYCON_DECLARE ( jz4780_uart , " ingenic,jz4780-uart " ,
ingenic_early_console_setup ) ;
2019-01-29 01:03:48 +08:00
OF_EARLYCON_DECLARE ( x1000_uart , " ingenic,x1000-uart " ,
ingenic_early_console_setup ) ;
2015-05-24 16:11:44 +01:00
static void ingenic_uart_serial_out ( struct uart_port * p , int offset , int value )
{
2015-09-08 16:55:34 +01:00
int ier ;
2015-05-24 16:11:44 +01:00
switch ( offset ) {
case UART_FCR :
/* UART module enable */
value | = UART_FCR_UME ;
break ;
case UART_IER :
2016-01-14 16:08:13 +01:00
/*
* Enable receive timeout interrupt with the receive line
* status interrupt .
*/
2015-05-24 16:11:44 +01:00
value | = ( value & 0x4 ) < < 2 ;
break ;
2015-09-08 16:55:34 +01:00
case UART_MCR :
2016-01-14 16:08:13 +01:00
/*
* If we have enabled modem status IRQs we should enable
* modem mode .
*/
2015-09-08 16:55:34 +01:00
ier = p - > serial_in ( p , UART_IER ) ;
if ( ier & UART_IER_MSI )
value | = UART_MCR_MDCE | UART_MCR_FCM ;
else
value & = ~ ( UART_MCR_MDCE | UART_MCR_FCM ) ;
break ;
2015-05-24 16:11:44 +01:00
default :
break ;
}
writeb ( value , p - > membase + ( offset < < p - > regshift ) ) ;
}
2015-09-08 16:55:34 +01:00
static unsigned int ingenic_uart_serial_in ( struct uart_port * p , int offset )
{
unsigned int value ;
value = readb ( p - > membase + ( offset < < p - > regshift ) ) ;
/* Hide non-16550 compliant bits from higher levels */
switch ( offset ) {
case UART_FCR :
value & = ~ UART_FCR_UME ;
break ;
case UART_MCR :
value & = ~ ( UART_MCR_MDCE | UART_MCR_FCM ) ;
break ;
default :
break ;
}
return value ;
}
2015-05-24 16:11:44 +01:00
static int ingenic_uart_probe ( struct platform_device * pdev )
{
struct uart_8250_port uart = { } ;
struct resource * regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
struct resource * irq = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
struct ingenic_uart_data * data ;
2015-09-08 16:55:35 +01:00
const struct ingenic_uart_config * cdata ;
const struct of_device_id * match ;
2015-05-24 16:11:44 +01:00
int err , line ;
2015-09-08 16:55:35 +01:00
match = of_match_device ( of_match , & pdev - > dev ) ;
if ( ! match ) {
dev_err ( & pdev - > dev , " Error: No device match found \n " ) ;
return - ENODEV ;
}
cdata = match - > data ;
2015-05-24 16:11:44 +01:00
if ( ! regs | | ! irq ) {
dev_err ( & pdev - > dev , " no registers/irq defined \n " ) ;
return - EINVAL ;
}
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
spin_lock_init ( & uart . port . lock ) ;
2015-09-08 16:55:35 +01:00
uart . port . type = PORT_16550A ;
2015-05-24 16:11:44 +01:00
uart . port . flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE ;
uart . port . iotype = UPIO_MEM ;
uart . port . mapbase = regs - > start ;
uart . port . regshift = 2 ;
uart . port . serial_out = ingenic_uart_serial_out ;
2015-09-08 16:55:34 +01:00
uart . port . serial_in = ingenic_uart_serial_in ;
2015-05-24 16:11:44 +01:00
uart . port . irq = irq - > start ;
uart . port . dev = & pdev - > dev ;
2015-09-08 16:55:35 +01:00
uart . port . fifosize = cdata - > fifosize ;
uart . tx_loadsz = cdata - > tx_loadsz ;
uart . capabilities = UART_CAP_FIFO | UART_CAP_RTOIE ;
2015-05-24 16:11:44 +01:00
/* Check for a fixed line number */
line = of_alias_get_id ( pdev - > dev . of_node , " serial " ) ;
if ( line > = 0 )
uart . port . line = line ;
uart . port . membase = devm_ioremap ( & pdev - > dev , regs - > start ,
resource_size ( regs ) ) ;
if ( ! uart . port . membase )
return - ENOMEM ;
data - > clk_module = devm_clk_get ( & pdev - > dev , " module " ) ;
if ( IS_ERR ( data - > clk_module ) ) {
err = PTR_ERR ( data - > clk_module ) ;
if ( err ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev ,
" unable to get module clock: %d \n " , err ) ;
return err ;
}
data - > clk_baud = devm_clk_get ( & pdev - > dev , " baud " ) ;
if ( IS_ERR ( data - > clk_baud ) ) {
err = PTR_ERR ( data - > clk_baud ) ;
if ( err ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev ,
" unable to get baud clock: %d \n " , err ) ;
return err ;
}
err = clk_prepare_enable ( data - > clk_module ) ;
if ( err ) {
dev_err ( & pdev - > dev , " could not enable module clock: %d \n " , err ) ;
goto out ;
}
err = clk_prepare_enable ( data - > clk_baud ) ;
if ( err ) {
dev_err ( & pdev - > dev , " could not enable baud clock: %d \n " , err ) ;
goto out_disable_moduleclk ;
}
uart . port . uartclk = clk_get_rate ( data - > clk_baud ) ;
data - > line = serial8250_register_8250_port ( & uart ) ;
if ( data - > line < 0 ) {
err = data - > line ;
goto out_disable_baudclk ;
}
platform_set_drvdata ( pdev , data ) ;
return 0 ;
out_disable_baudclk :
clk_disable_unprepare ( data - > clk_baud ) ;
out_disable_moduleclk :
clk_disable_unprepare ( data - > clk_module ) ;
out :
return err ;
}
2016-02-17 16:02:32 +01:00
static int ingenic_uart_remove ( struct platform_device * pdev )
{
struct ingenic_uart_data * data = platform_get_drvdata ( pdev ) ;
serial8250_unregister_port ( data - > line ) ;
clk_disable_unprepare ( data - > clk_module ) ;
clk_disable_unprepare ( data - > clk_baud ) ;
return 0 ;
}
2015-09-08 16:55:35 +01:00
static const struct ingenic_uart_config jz4740_uart_config = {
. tx_loadsz = 8 ,
. fifosize = 16 ,
} ;
static const struct ingenic_uart_config jz4760_uart_config = {
. tx_loadsz = 16 ,
. fifosize = 32 ,
} ;
static const struct ingenic_uart_config jz4780_uart_config = {
. tx_loadsz = 32 ,
. fifosize = 64 ,
} ;
2019-01-29 01:03:48 +08:00
static const struct ingenic_uart_config x1000_uart_config = {
. tx_loadsz = 32 ,
. fifosize = 64 ,
} ;
2015-05-24 16:11:44 +01:00
static const struct of_device_id of_match [ ] = {
2015-09-08 16:55:35 +01:00
{ . compatible = " ingenic,jz4740-uart " , . data = & jz4740_uart_config } ,
{ . compatible = " ingenic,jz4760-uart " , . data = & jz4760_uart_config } ,
2017-12-28 14:07:08 +01:00
{ . compatible = " ingenic,jz4770-uart " , . data = & jz4760_uart_config } ,
2015-09-08 16:55:35 +01:00
{ . compatible = " ingenic,jz4775-uart " , . data = & jz4760_uart_config } ,
{ . compatible = " ingenic,jz4780-uart " , . data = & jz4780_uart_config } ,
2019-01-29 01:03:48 +08:00
{ . compatible = " ingenic,x1000-uart " , . data = & x1000_uart_config } ,
2015-05-24 16:11:44 +01:00
{ /* sentinel */ }
} ;
2016-02-17 16:02:32 +01:00
MODULE_DEVICE_TABLE ( of , of_match ) ;
2015-05-24 16:11:44 +01:00
static struct platform_driver ingenic_uart_platform_driver = {
. driver = {
2016-02-17 16:02:32 +01:00
. name = " ingenic-uart " ,
. of_match_table = of_match ,
2015-05-24 16:11:44 +01:00
} ,
. probe = ingenic_uart_probe ,
2016-02-17 16:02:32 +01:00
. remove = ingenic_uart_remove ,
2015-05-24 16:11:44 +01:00
} ;
2016-02-17 16:02:32 +01:00
module_platform_driver ( ingenic_uart_platform_driver ) ;
MODULE_AUTHOR ( " Paul Burton " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Ingenic SoC UART driver " ) ;