2017-11-06 18:11:51 +01:00
// SPDX-License-Identifier: GPL-2.0
2016-01-17 12:15:30 +00:00
/*
* Serial port driver for BCM2835AUX UART
*
* Copyright ( C ) 2016 Martin Sperl < kernel @ martin . sperl . org >
*
* Based on 8250 _lpc18xx . c :
* Copyright ( C ) 2015 Joachim Eastwood < manabian @ gmail . com >
2020-02-28 14:31:07 +01:00
*
* The bcm2835aux is capable of RTS auto flow - control , but this driver doesn ' t
* take advantage of it yet . When adding support , be sure not to enable it
* simultaneously to rs485 .
2016-01-17 12:15:30 +00:00
*/
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
2022-02-07 18:21:29 -05:00
# include <linux/property.h>
2016-01-17 12:15:30 +00:00
# include "8250.h"
2020-02-28 14:31:07 +01:00
# define BCM2835_AUX_UART_CNTL 8
# define BCM2835_AUX_UART_CNTL_RXEN 0x01 /* Receiver enable */
# define BCM2835_AUX_UART_CNTL_TXEN 0x02 /* Transmitter enable */
# define BCM2835_AUX_UART_CNTL_AUTORTS 0x04 /* RTS set by RX fill level */
# define BCM2835_AUX_UART_CNTL_AUTOCTS 0x08 /* CTS stops transmitter */
# define BCM2835_AUX_UART_CNTL_RTS3 0x00 /* RTS set until 3 chars left */
# define BCM2835_AUX_UART_CNTL_RTS2 0x10 /* RTS set until 2 chars left */
# define BCM2835_AUX_UART_CNTL_RTS1 0x20 /* RTS set until 1 chars left */
# define BCM2835_AUX_UART_CNTL_RTS4 0x30 /* RTS set until 4 chars left */
# define BCM2835_AUX_UART_CNTL_RTSINV 0x40 /* Invert auto RTS polarity */
# define BCM2835_AUX_UART_CNTL_CTSINV 0x80 /* Invert auto CTS polarity */
2020-01-16 13:14:06 +01:00
/**
* struct bcm2835aux_data - driver private data of BCM2835 auxiliary UART
* @ clk : clock producer of the port ' s uartclk
* @ line : index of the port ' s serial8250_ports [ ] entry
2020-02-28 14:31:07 +01:00
* @ cntl : cached copy of CNTL register
2020-01-16 13:14:06 +01:00
*/
2016-01-17 12:15:30 +00:00
struct bcm2835aux_data {
struct clk * clk ;
int line ;
2020-02-28 14:31:07 +01:00
u32 cntl ;
2016-01-17 12:15:30 +00:00
} ;
2020-02-28 14:31:07 +01:00
static void bcm2835aux_rs485_start_tx ( struct uart_8250_port * up )
{
if ( ! ( up - > port . rs485 . flags & SER_RS485_RX_DURING_TX ) ) {
struct bcm2835aux_data * data = dev_get_drvdata ( up - > port . dev ) ;
data - > cntl & = ~ BCM2835_AUX_UART_CNTL_RXEN ;
serial_out ( up , BCM2835_AUX_UART_CNTL , data - > cntl ) ;
}
/*
* On the bcm2835aux , the MCR register contains no other
* flags besides RTS . So no need for a read - modify - write .
*/
if ( up - > port . rs485 . flags & SER_RS485_RTS_ON_SEND )
serial8250_out_MCR ( up , 0 ) ;
else
serial8250_out_MCR ( up , UART_MCR_RTS ) ;
}
static void bcm2835aux_rs485_stop_tx ( struct uart_8250_port * up )
{
if ( up - > port . rs485 . flags & SER_RS485_RTS_AFTER_SEND )
serial8250_out_MCR ( up , 0 ) ;
else
serial8250_out_MCR ( up , UART_MCR_RTS ) ;
if ( ! ( up - > port . rs485 . flags & SER_RS485_RX_DURING_TX ) ) {
struct bcm2835aux_data * data = dev_get_drvdata ( up - > port . dev ) ;
data - > cntl | = BCM2835_AUX_UART_CNTL_RXEN ;
serial_out ( up , BCM2835_AUX_UART_CNTL , data - > cntl ) ;
}
}
2016-01-17 12:15:30 +00:00
static int bcm2835aux_serial_probe ( struct platform_device * pdev )
{
2024-03-04 14:27:06 +02:00
const struct software_node * bcm2835_swnode ;
2020-01-16 13:14:04 +01:00
struct uart_8250_port up = { } ;
2016-01-17 12:15:30 +00:00
struct bcm2835aux_data * data ;
struct resource * res ;
2022-02-07 18:21:29 -05:00
unsigned int uartclk ;
2016-01-17 12:15:30 +00:00
int ret ;
/* allocate the custom structure */
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
/* initialize data */
2020-01-16 13:14:04 +01:00
up . capabilities = UART_CAP_FIFO | UART_CAP_MINI ;
up . port . dev = & pdev - > dev ;
up . port . type = PORT_16550 ;
2024-03-04 14:27:06 +02:00
up . port . flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST | UPF_IOREMAP ;
2020-02-28 14:31:07 +01:00
up . port . rs485_config = serial8250_em485_config ;
2022-07-04 12:45:14 +03:00
up . port . rs485_supported = serial8250_em485_supported ;
2020-02-28 14:31:07 +01:00
up . rs485_start_tx = bcm2835aux_rs485_start_tx ;
up . rs485_stop_tx = bcm2835aux_rs485_stop_tx ;
/* initialize cached copy with power-on reset value */
data - > cntl = BCM2835_AUX_UART_CNTL_RXEN | BCM2835_AUX_UART_CNTL_TXEN ;
platform_set_drvdata ( pdev , data ) ;
2016-01-17 12:15:30 +00:00
/* get the clock - this also enables the HW */
2022-02-07 18:21:29 -05:00
data - > clk = devm_clk_get_optional ( & pdev - > dev , NULL ) ;
2023-12-20 12:43:34 +01:00
if ( IS_ERR ( data - > clk ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( data - > clk ) , " could not get clk \n " ) ;
2016-01-17 12:15:30 +00:00
/* map the main registers */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " memory resource not found " ) ;
return - EINVAL ;
}
2022-02-07 18:21:29 -05:00
2024-03-04 14:27:06 +02:00
up . port . mapbase = res - > start ;
up . port . mapsize = resource_size ( res ) ;
bcm2835_swnode = device_get_match_data ( & pdev - > dev ) ;
if ( bcm2835_swnode ) {
ret = device_add_software_node ( & pdev - > dev , bcm2835_swnode ) ;
if ( ret )
return ret ;
}
2022-02-07 18:21:29 -05:00
2024-03-04 14:27:06 +02:00
ret = uart_read_port_properties ( & up . port ) ;
if ( ret )
goto rm_swnode ;
2016-01-17 12:15:30 +00:00
2024-03-04 14:27:06 +02:00
up . port . regshift = 2 ;
up . port . fifosize = 8 ;
2016-01-17 12:15:30 +00:00
/* enable the clock as a last step */
ret = clk_prepare_enable ( data - > clk ) ;
if ( ret ) {
2024-03-04 14:27:06 +02:00
dev_err_probe ( & pdev - > dev , ret , " unable to enable uart clock \n " ) ;
goto rm_swnode ;
2016-01-17 12:15:30 +00:00
}
2022-02-07 18:21:29 -05:00
uartclk = clk_get_rate ( data - > clk ) ;
2024-03-04 14:27:06 +02:00
if ( uartclk )
up . port . uartclk = uartclk ;
2022-02-07 18:21:29 -05:00
2016-01-17 12:15:30 +00:00
/* the HW-clock divider for bcm2835aux is 8,
* but 8250 expects a divider of 16 ,
* so we have to multiply the actual clock by 2
* to get identical baudrates .
*/
2024-03-04 14:27:06 +02:00
up . port . uartclk * = 2 ;
2016-01-17 12:15:30 +00:00
/* register the port */
2020-01-16 13:14:04 +01:00
ret = serial8250_register_8250_port ( & up ) ;
2016-01-17 12:15:30 +00:00
if ( ret < 0 ) {
2020-09-01 17:30:59 +02:00
dev_err_probe ( & pdev - > dev , ret , " unable to register 8250 port \n " ) ;
2016-01-17 12:15:30 +00:00
goto dis_clk ;
}
data - > line = ret ;
return 0 ;
dis_clk :
clk_disable_unprepare ( data - > clk ) ;
2024-03-04 14:27:06 +02:00
rm_swnode :
device_remove_software_node ( & pdev - > dev ) ;
2016-01-17 12:15:30 +00:00
return ret ;
}
2023-11-10 16:29:31 +01:00
static void bcm2835aux_serial_remove ( struct platform_device * pdev )
2016-01-17 12:15:30 +00:00
{
struct bcm2835aux_data * data = platform_get_drvdata ( pdev ) ;
2020-01-16 13:14:01 +01:00
serial8250_unregister_port ( data - > line ) ;
2016-01-17 12:15:30 +00:00
clk_disable_unprepare ( data - > clk ) ;
2024-03-04 14:27:06 +02:00
device_remove_software_node ( & pdev - > dev ) ;
2016-01-17 12:15:30 +00:00
}
2024-03-04 14:27:06 +02:00
/*
* Some UEFI implementations ( e . g . tianocore / edk2 for the Raspberry Pi )
* describe the miniuart with a base address that encompasses the auxiliary
* registers shared between the miniuart and spi .
*
* This is due to historical reasons , see discussion here :
* https : //edk2.groups.io/g/devel/topic/87501357#84349
*
* We need to add the offset between the miniuart and auxiliary registers
* to get the real miniuart base address .
*/
static const struct property_entry bcm2835_acpi_properties [ ] = {
PROPERTY_ENTRY_U32 ( " reg-offset " , 0x40 ) ,
{ }
} ;
static const struct software_node bcm2835_acpi_node = {
. properties = bcm2835_acpi_properties ,
2022-02-07 18:21:29 -05:00
} ;
2016-01-17 12:15:30 +00:00
static const struct of_device_id bcm2835aux_serial_match [ ] = {
{ . compatible = " brcm,bcm2835-aux-uart " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , bcm2835aux_serial_match ) ;
2022-02-07 18:21:29 -05:00
static const struct acpi_device_id bcm2835aux_serial_acpi_match [ ] = {
2024-03-04 14:27:06 +02:00
{ " BCM2836 " , ( kernel_ulong_t ) & bcm2835_acpi_node } ,
2022-02-07 18:21:29 -05:00
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , bcm2835aux_serial_acpi_match ) ;
2016-01-17 12:15:30 +00:00
static struct platform_driver bcm2835aux_serial_driver = {
. driver = {
. name = " bcm2835-aux-uart " ,
. of_match_table = bcm2835aux_serial_match ,
2022-02-07 18:21:29 -05:00
. acpi_match_table = bcm2835aux_serial_acpi_match ,
2016-01-17 12:15:30 +00:00
} ,
. probe = bcm2835aux_serial_probe ,
2023-11-10 16:29:31 +01:00
. remove_new = bcm2835aux_serial_remove ,
2016-01-17 12:15:30 +00:00
} ;
module_platform_driver ( bcm2835aux_serial_driver ) ;
2020-01-26 13:33:14 +01:00
# ifdef CONFIG_SERIAL_8250_CONSOLE
static int __init early_bcm2835aux_setup ( struct earlycon_device * device ,
const char * options )
{
if ( ! device - > port . membase )
return - ENODEV ;
device - > port . iotype = UPIO_MEM32 ;
device - > port . regshift = 2 ;
return early_serial8250_setup ( device , NULL ) ;
}
OF_EARLYCON_DECLARE ( bcm2835aux , " brcm,bcm2835-aux-uart " ,
early_bcm2835aux_setup ) ;
# endif
2016-01-17 12:15:30 +00:00
MODULE_DESCRIPTION ( " BCM2835 auxiliar UART driver " ) ;
MODULE_AUTHOR ( " Martin Sperl <kernel@martin.sperl.org> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;