[PATCH v2 1/4] serial: ns16550: Support run-time configuration

Simon Goldschmidt simon.k.r.goldschmidt at gmail.com
Mon Dec 9 22:07:14 CET 2019


Am 09.12.2019 um 17:59 schrieb Simon Glass:
> At present this driver uses an assortment of CONFIG options to control
> how it accesses the hardware. This is painful for platforms that are
> supposed to be controlled by a device tree or a previous-stage bootloader.
> 
> Add a new CONFIG option to enable fully dynamic configuration. This
> controls register spacing, size, offset and endianness.
> 
> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
> 
> Changes in v2:
> - runtime -> run-time
> - Enable run-time config for slimbootloader too
> - Improve Kconfig help based on Bin's comments
> - Use ns16550 in patch subject
> 
>   drivers/serial/Kconfig   | 21 +++++++++++++++
>   drivers/serial/ns16550.c | 57 ++++++++++++++++++++++++++++++++++------
>   include/ns16550.h        | 13 +++++++++
>   3 files changed, 83 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index ece7d87d4c..472a9f0929 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
> @@ -598,6 +598,27 @@ config SYS_NS16550
>   	  be used. It can be a constant or a function to get clock, eg,
>   	  get_serial_clock().
>   
> +config NS16550_DYNAMIC
> +	bool "Allow NS16550 to be configured at runtime"
> +	default y if SYS_COREBOOT || SYS_SLIMBOOTLOADER
> +	help
> +	  Enable this option to allow device-tree control of the driver.
> +
> +	  Normally this driver is controlled by the following options:
> +
> +	  CONFIG_SYS_NS16550_PORT_MAPPED - indicates that port I/O is used for
> +	     access. If not enabled, then the UART is memory-mapped.
> +	  CONFIG_SYS_NS16550_MEM32 - if memory-mapped, indicates that 32-bit
> +	     access should be used (instead of 8-bit)
> +	  CONFIG_SYS_NS16550_REG_SIZE - indicates register width and also
> +	     endianness. If positive, big-endian access is used. If negative,
> +	     little-endian is used.
> +
> +	  It is not a good practice for a driver to be statically configured,
> +	  since it prevents the same driver being used for different types of
> +	  UARTs in a system. This option avoids this problem at the cost of a
> +	  slightly increased code size.
> +
>   config INTEL_MID_SERIAL
>   	bool "Intel MID platform UART support"
>   	depends on DM_SERIAL && OF_CONTROL
> diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
> index 754b6e9921..96c4471efd 100644
> --- a/drivers/serial/ns16550.c
> +++ b/drivers/serial/ns16550.c
> @@ -92,19 +92,57 @@ static inline int serial_in_shift(void *addr, int shift)
>   #define CONFIG_SYS_NS16550_CLK  0
>   #endif
>   
> +static void serial_out_dynamic(struct ns16550_platdata *plat, u8 *addr,
> +			       int value)
> +{
> +	if (plat->flags & NS16550_FLAG_BE) {
> +		if (plat->reg_width == 1)
> +			writeb(value, addr + (1 << plat->reg_shift) - 1);
> +		else if (plat->flags & NS16550_FLAG_IO)
> +			out_be32(addr, value);
> +		else
> +			writel(value, addr);
> +	} else {
> +		if (plat->reg_width == 1)
> +			writeb(value, addr);
> +		else if (plat->flags & NS16550_FLAG_IO)
> +			out_le32(addr, value);
> +		else
> +			writel(value, addr);
> +	}
> +}
> +
> +static int serial_in_dynamic(struct ns16550_platdata *plat, u8 *addr)
> +{
> +	if (plat->flags & NS16550_FLAG_BE) {
> +		if (plat->reg_width == 1)
> +			return readb(addr + (1 << plat->reg_shift) - 1);
> +		else if (plat->flags & NS16550_FLAG_IO)
> +			return in_be32(addr);
> +		else
> +			return readl(addr);
> +	} else {
> +		if (plat->reg_width == 1)
> +			return readb(addr);
> +		else if (plat->flags & NS16550_FLAG_IO)
> +			return in_le32(addr);
> +		else
> +			return readl(addr);
> +	}
> +}
> +
>   static void ns16550_writeb(NS16550_t port, int offset, int value)
>   {
>   	struct ns16550_platdata *plat = port->plat;
>   	unsigned char *addr;
>   
>   	offset *= 1 << plat->reg_shift;
> -	addr = (unsigned char *)plat->base + offset;
> +	addr = (unsigned char *)plat->base + offset + plat->reg_offset;
>   
> -	/*
> -	 * As far as we know it doesn't make sense to support selection of
> -	 * these options at run-time, so use the existing CONFIG options.
> -	 */
> -	serial_out_shift(addr + plat->reg_offset, plat->reg_shift, value);
> +	if (IS_ENABLED(CONFIG_NS16550_DYNAMIC))
> +		serial_out_dynamic(plat, addr, value);
> +	else
> +		serial_out_shift(addr, plat->reg_shift, value);
>   }
>   
>   static int ns16550_readb(NS16550_t port, int offset)
> @@ -113,9 +151,12 @@ static int ns16550_readb(NS16550_t port, int offset)
>   	unsigned char *addr;
>   
>   	offset *= 1 << plat->reg_shift;
> -	addr = (unsigned char *)plat->base + offset;
> +	addr = (unsigned char *)plat->base + offset + plat->reg_offset;
>   
> -	return serial_in_shift(addr + plat->reg_offset, plat->reg_shift);
> +	if (IS_ENABLED(CONFIG_NS16550_DYNAMIC))
> +		return serial_in_dynamic(plat, addr);
> +	else
> +		return serial_in_shift(addr, plat->reg_shift);
>   }
>   
>   static u32 ns16550_getfcr(NS16550_t port)
> diff --git a/include/ns16550.h b/include/ns16550.h
> index 701efeea85..ba9b71962d 100644
> --- a/include/ns16550.h
> +++ b/include/ns16550.h
> @@ -31,6 +31,9 @@
>   #define CONFIG_SYS_NS16550_REG_SIZE (-1)
>   #endif
>   
> +#ifdef CONFIG_NS16550_DYNAMIC
> +#define UART_REG(x)	unsigned char x
> +#else
>   #if !defined(CONFIG_SYS_NS16550_REG_SIZE) || (CONFIG_SYS_NS16550_REG_SIZE == 0)
>   #error "Please define NS16550 registers size."
>   #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_DM_SERIAL)
> @@ -44,6 +47,12 @@
>   	unsigned char x;						\
>   	unsigned char postpad_##x[-CONFIG_SYS_NS16550_REG_SIZE - 1];
>   #endif
> +#endif /* CONFIG_NS16550_DYNAMIC */
> +
> +enum ns16550_flags {
> +	NS16550_FLAG_IO	= 1 << 0, /* Use I/O access (else mem-mapped) */
> +	NS16550_FLAG_BE	= 1 << 1, /* Use big-endian access (else little) */
> +};
>   
>   /**
>    * struct ns16550_platdata - information about a NS16550 port
> @@ -51,7 +60,10 @@
>    * @base:		Base register address
>    * @reg_width:		IO accesses size of registers (in bytes)
>    * @reg_shift:		Shift size of registers (0=byte, 1=16bit, 2=32bit...)
> + * @reg_offset:		Offset to start of registers
>    * @clock:		UART base clock speed in Hz
> + * @fcr:		Offset of FCR register (normally UART_FCR_DEFVAL)
> + * @flags:		A few flags (enum ns16550_flags)
>    * @bdf:		PCI slot/function (pci_dev_t)
>    */
>   struct ns16550_platdata {
> @@ -61,6 +73,7 @@ struct ns16550_platdata {
>   	int reg_offset;
>   	int clock;
>   	u32 fcr;
> +	int flags;

I'm not too fond of adding 4 bytes here for nothing on most platforms, 
but unless you come up with a better idea than cluttering this file with 
ifdefs:

Reviewed-by: Simon Goldschmidt <simon.k.r.goldschmidt at gmail.com>

>   #if defined(CONFIG_PCI) && defined(CONFIG_SPL)
>   	int bdf;
>   #endif
> 



More information about the U-Boot mailing list