diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/Kconfig | 21 | ||||
-rw-r--r-- | drivers/serial/ns16550.c | 79 |
2 files changed, 92 insertions, 8 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index bd95f70b61c..1147654082e 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -601,6 +601,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 9851663dc54..31f6cfe421c 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -93,19 +93,79 @@ static inline int serial_in_shift(void *addr, int shift) #define CONFIG_SYS_NS16550_CLK 0 #endif +/* + * Use this #ifdef for now since many platforms don't define in(), out(), + * out_le32(), etc. but we don't have #defines to indicate this. + * + * TODO(sjg@chromium.org): Add CONFIG options to indicate what I/O is available + * on a platform + */ +#ifdef CONFIG_NS16550_DYNAMIC +static void serial_out_dynamic(struct ns16550_platdata *plat, u8 *addr, + int value) +{ + if (plat->flags & NS16550_FLAG_IO) { + outb(value, addr); + } else if (plat->reg_width == 4) { + if (plat->flags & NS16550_FLAG_ENDIAN) { + if (plat->flags & NS16550_FLAG_BE) + out_be32(addr, value); + else + out_le32(addr, value); + } else { + writel(value, addr); + } + } else if (plat->flags & NS16550_FLAG_BE) { + writeb(value, addr + (1 << plat->reg_shift) - 1); + } else { + writeb(value, addr); + } +} + +static int serial_in_dynamic(struct ns16550_platdata *plat, u8 *addr) +{ + if (plat->flags & NS16550_FLAG_IO) { + return inb(addr); + } else if (plat->reg_width == 4) { + if (plat->flags & NS16550_FLAG_ENDIAN) { + if (plat->flags & NS16550_FLAG_BE) + return in_be32(addr); + else + return in_le32(addr); + } else { + return readl(addr); + } + } else if (plat->flags & NS16550_FLAG_BE) { + return readb(addr + (1 << plat->reg_shift) - 1); + } else { + return readb(addr); + } +} +#else +static inline void serial_out_dynamic(struct ns16550_platdata *plat, u8 *addr, + int value) +{ +} + +static inline int serial_in_dynamic(struct ns16550_platdata *plat, u8 *addr) +{ + return 0; +} + +#endif /* CONFIG_NS16550_DYNAMIC */ + 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) @@ -114,9 +174,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) |