From ed467a695a9892dd631138b78d0a3d3ca88fed9f Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 26 Apr 2017 09:56:39 -0500 Subject: [PATCH] serial: mcux: Shim driver for LPSCI UART on KL25Z Adds a shim layer around the mcux lpsci driver to adapt it to the Zephyr serial interface. Change-Id: I024f1605e3194f34bb57e8a121900e05b3085a82 Signed-off-by: Kumar Gala --- arch/arm/soc/nxp_kinetis/Kconfig | 6 + drivers/serial/Kconfig | 2 + drivers/serial/Kconfig.mcux_lpsci | 32 +++ drivers/serial/Makefile | 1 + drivers/serial/uart_mcux_lpsci.c | 308 ++++++++++++++++++++++++++++ dts/arm/yaml/nxp,kinetis-lpsci.yaml | 31 +++ ext/hal/nxp/mcux/drivers/Makefile | 1 + 7 files changed, 381 insertions(+) create mode 100644 drivers/serial/Kconfig.mcux_lpsci create mode 100644 drivers/serial/uart_mcux_lpsci.c create mode 100644 dts/arm/yaml/nxp,kinetis-lpsci.yaml diff --git a/arch/arm/soc/nxp_kinetis/Kconfig b/arch/arm/soc/nxp_kinetis/Kconfig index 4bb558eb9a6..ceb58e7d9ef 100644 --- a/arch/arm/soc/nxp_kinetis/Kconfig +++ b/arch/arm/soc/nxp_kinetis/Kconfig @@ -58,6 +58,12 @@ config HAS_LPUART help Set if the low power uart (LPUART) module is present in the SoC. +config HAS_LPSCI + bool + default n + help + Set if the low power uart (LPSCI) module is present in the SoC. + if HAS_OSC choice diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 2f14d814e4d..bafa2d96141 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -59,6 +59,8 @@ source "drivers/serial/Kconfig.ns16550" source "drivers/serial/Kconfig.mcux" +source "drivers/serial/Kconfig.mcux_lpsci" + source "drivers/serial/Kconfig.mcux_lpuart" source "drivers/serial/Kconfig.stellaris" diff --git a/drivers/serial/Kconfig.mcux_lpsci b/drivers/serial/Kconfig.mcux_lpsci new file mode 100644 index 00000000000..d20a8780796 --- /dev/null +++ b/drivers/serial/Kconfig.mcux_lpsci @@ -0,0 +1,32 @@ +# Kconfig - MCUXpresso SDK LPSCI +# +# Copyright (c) 2017, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig UART_MCUX_LPSCI + bool "MCUX LPSCI driver" + depends on HAS_MCUX && HAS_LPSCI + default n + select SERIAL_HAS_DRIVER + help + Enable the MCUX LPSCI driver. + +if UART_MCUX_LPSCI + +menuconfig UART_MCUX_LPSCI_0 + bool "UART 0" + default n + help + Enable UART 0. + +if UART_MCUX_LPSCI_0 + +config UART_MCUX_LPSCI_0_NAME + string "UART 0 driver name" + default "UART_0" + +endif # UART_MCUX_LPSCI_0 + +endif # UART_MCUX_LPSCI diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 1fa9fd5c3be..2e4b9728dfc 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -3,6 +3,7 @@ ccflags-y +=-I$(srctree)/drivers obj-$(CONFIG_UART_NS16550) += uart_ns16550.o obj-$(CONFIG_UART_MCUX) += uart_mcux.o +obj-$(CONFIG_UART_MCUX_LPSCI) += uart_mcux_lpsci.o obj-$(CONFIG_UART_MCUX_LPUART) += uart_mcux_lpuart.o obj-$(CONFIG_UART_STELLARIS) += uart_stellaris.o obj-$(CONFIG_UART_NSIM) += uart_nsim.o diff --git a/drivers/serial/uart_mcux_lpsci.c b/drivers/serial/uart_mcux_lpsci.c new file mode 100644 index 00000000000..5d6c08d6d95 --- /dev/null +++ b/drivers/serial/uart_mcux_lpsci.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2017, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +struct mcux_lpsci_config { + UART0_Type *base; + clock_name_t clock_source; + u32_t baud_rate; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + void (*irq_config_func)(struct device *dev); +#endif +}; + +struct mcux_lpsci_data { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_t callback; +#endif +}; + +static int mcux_lpsci_poll_in(struct device *dev, unsigned char *c) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t flags = LPSCI_GetStatusFlags(config->base); + int ret = -1; + + if (flags & kLPSCI_RxDataRegFullFlag) { + *c = LPSCI_ReadByte(config->base); + ret = 0; + } + + return ret; +} + +static unsigned char mcux_lpsci_poll_out(struct device *dev, unsigned char c) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + + while (!(LPSCI_GetStatusFlags(config->base) + & kLPSCI_TxDataRegEmptyFlag)) + ; + + LPSCI_WriteByte(config->base, c); + + return c; +} + +static int mcux_lpsci_err_check(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t flags = LPSCI_GetStatusFlags(config->base); + int err = 0; + + if (flags & kLPSCI_RxOverrunFlag) { + err |= UART_ERROR_OVERRUN; + } + + if (flags & kLPSCI_ParityErrorFlag) { + err |= UART_ERROR_PARITY; + } + + if (flags & kLPSCI_FramingErrorFlag) { + err |= UART_ERROR_FRAMING; + } + + LPSCI_ClearStatusFlags(config->base, kLPSCI_RxOverrunFlag | + kLPSCI_ParityErrorFlag | + kLPSCI_FramingErrorFlag); + + return err; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int mcux_lpsci_fifo_fill(struct device *dev, const u8_t *tx_data, + int len) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u8_t num_tx = 0; + + while ((len - num_tx > 0) && + (LPSCI_GetStatusFlags(config->base) + & kLPSCI_TxDataRegEmptyFlag)) { + + LPSCI_WriteByte(config->base, tx_data[num_tx++]); + } + + return num_tx; +} + +static int mcux_lpsci_fifo_read(struct device *dev, u8_t *rx_data, + const int len) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u8_t num_rx = 0; + + while ((len - num_rx > 0) && + (LPSCI_GetStatusFlags(config->base) + & kLPSCI_RxDataRegFullFlag)) { + + rx_data[num_rx++] = LPSCI_ReadByte(config->base); + } + + return num_rx; +} + +static void mcux_lpsci_irq_tx_enable(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_TxDataRegEmptyInterruptEnable; + + LPSCI_EnableInterrupts(config->base, mask); +} + +static void mcux_lpsci_irq_tx_disable(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_TxDataRegEmptyInterruptEnable; + + LPSCI_DisableInterrupts(config->base, mask); +} + +static int mcux_lpsci_irq_tx_empty(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t flags = LPSCI_GetStatusFlags(config->base); + + return (flags & kLPSCI_TxDataRegEmptyFlag) != 0; +} + +static int mcux_lpsci_irq_tx_ready(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_TxDataRegEmptyInterruptEnable; + + return (LPSCI_GetEnabledInterrupts(config->base) & mask) + && mcux_lpsci_irq_tx_empty(dev); +} + +static void mcux_lpsci_irq_rx_enable(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_RxDataRegFullInterruptEnable; + + LPSCI_EnableInterrupts(config->base, mask); +} + +static void mcux_lpsci_irq_rx_disable(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_RxDataRegFullInterruptEnable; + + LPSCI_DisableInterrupts(config->base, mask); +} + +static int mcux_lpsci_irq_rx_full(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t flags = LPSCI_GetStatusFlags(config->base); + + return (flags & kLPSCI_RxDataRegFullFlag) != 0; +} + +static int mcux_lpsci_irq_rx_ready(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_RxDataRegFullInterruptEnable; + + return (LPSCI_GetEnabledInterrupts(config->base) & mask) + && mcux_lpsci_irq_rx_full(dev); +} + +static void mcux_lpsci_irq_err_enable(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_NoiseErrorInterruptEnable | + kLPSCI_FramingErrorInterruptEnable | + kLPSCI_ParityErrorInterruptEnable; + + LPSCI_EnableInterrupts(config->base, mask); +} + +static void mcux_lpsci_irq_err_disable(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + u32_t mask = kLPSCI_NoiseErrorInterruptEnable | + kLPSCI_FramingErrorInterruptEnable | + kLPSCI_ParityErrorInterruptEnable; + + LPSCI_DisableInterrupts(config->base, mask); +} + +static int mcux_lpsci_irq_is_pending(struct device *dev) +{ + return (mcux_lpsci_irq_tx_ready(dev) + || mcux_lpsci_irq_rx_ready(dev)); +} + +static int mcux_lpsci_irq_update(struct device *dev) +{ + return 1; +} + +static void mcux_lpsci_irq_callback_set(struct device *dev, + uart_irq_callback_t cb) +{ + struct mcux_lpsci_data *data = dev->driver_data; + + data->callback = cb; +} + +static void mcux_lpsci_isr(void *arg) +{ + struct device *dev = arg; + struct mcux_lpsci_data *data = dev->driver_data; + + if (data->callback) { + data->callback(dev); + } +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static int mcux_lpsci_init(struct device *dev) +{ + const struct mcux_lpsci_config *config = dev->config->config_info; + lpsci_config_t uart_config; + u32_t clock_freq; + + clock_freq = CLOCK_GetFreq(config->clock_source); + + LPSCI_GetDefaultConfig(&uart_config); + uart_config.enableTx = true; + uart_config.enableRx = true; + uart_config.baudRate_Bps = config->baud_rate; + + LPSCI_Init(config->base, &uart_config, clock_freq); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + config->irq_config_func(dev); +#endif + + return 0; +} + +static const struct uart_driver_api mcux_lpsci_driver_api = { + .poll_in = mcux_lpsci_poll_in, + .poll_out = mcux_lpsci_poll_out, + .err_check = mcux_lpsci_err_check, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = mcux_lpsci_fifo_fill, + .fifo_read = mcux_lpsci_fifo_read, + .irq_tx_enable = mcux_lpsci_irq_tx_enable, + .irq_tx_disable = mcux_lpsci_irq_tx_disable, + .irq_tx_empty = mcux_lpsci_irq_tx_empty, + .irq_tx_ready = mcux_lpsci_irq_tx_ready, + .irq_rx_enable = mcux_lpsci_irq_rx_enable, + .irq_rx_disable = mcux_lpsci_irq_rx_disable, + .irq_rx_ready = mcux_lpsci_irq_rx_ready, + .irq_err_enable = mcux_lpsci_irq_err_enable, + .irq_err_disable = mcux_lpsci_irq_err_disable, + .irq_is_pending = mcux_lpsci_irq_is_pending, + .irq_update = mcux_lpsci_irq_update, + .irq_callback_set = mcux_lpsci_irq_callback_set, +#endif +}; + +#ifdef CONFIG_UART_MCUX_LPSCI_0 + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void mcux_lpsci_config_func_0(struct device *dev); +#endif + +static const struct mcux_lpsci_config mcux_lpsci_0_config = { + .base = UART0, + .clock_source = UART0_CLK_SRC, + .baud_rate = NXP_KINETIS_LPSCI_4006A000_CURRENT_SPEED, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .irq_config_func = mcux_lpsci_config_func_0, +#endif +}; + +static struct mcux_lpsci_data mcux_lpsci_0_data; + +DEVICE_AND_API_INIT(uart_0, CONFIG_UART_MCUX_LPSCI_0_NAME, + &mcux_lpsci_init, + &mcux_lpsci_0_data, &mcux_lpsci_0_config, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &mcux_lpsci_driver_api); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void mcux_lpsci_config_func_0(struct device *dev) +{ + IRQ_CONNECT(NXP_KINETIS_LPSCI_4006A000_IRQ_0, + NXP_KINETIS_LPSCI_4006A000_IRQ_0_PRIORITY, + mcux_lpsci_isr, DEVICE_GET(uart_0), 0); + + irq_enable(NXP_KINETIS_LPSCI_4006A000_IRQ_0); +} +#endif + +#endif /* CONFIG_UART_MCUX_LPSCI_0 */ diff --git a/dts/arm/yaml/nxp,kinetis-lpsci.yaml b/dts/arm/yaml/nxp,kinetis-lpsci.yaml new file mode 100644 index 00000000000..205afc705df --- /dev/null +++ b/dts/arm/yaml/nxp,kinetis-lpsci.yaml @@ -0,0 +1,31 @@ +--- +title: Kinetis LPSCI UART +id: nxp,kinetis-lpsci +version: 0.1 + +description: > + This binding gives a base representation of the LPSCI UART + +inherits: + - !include uart.yaml + +properties: + - compatible: + type: string + category: required + description: compatible strings + constraint: "nxp,kinetis-lpsci" + + - reg: + type: array + description: mmio register space + generation: define + category: required + + - interrupts: + type: array + category: required + description: required interrupts + generation: define + +... diff --git a/ext/hal/nxp/mcux/drivers/Makefile b/ext/hal/nxp/mcux/drivers/Makefile index b6f07931ee1..305d76e4ebb 100644 --- a/ext/hal/nxp/mcux/drivers/Makefile +++ b/ext/hal/nxp/mcux/drivers/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_RANDOM_MCUX_TRNG) += fsl_trng.o obj-$(CONFIG_SOC_FLASH_MCUX) += fsl_flash.o obj-$(CONFIG_SPI_MCUX_DSPI) += fsl_dspi.o obj-$(CONFIG_UART_MCUX) += fsl_uart.o +obj-$(CONFIG_UART_MCUX_LPSCI) += fsl_lpsci.o obj-$(CONFIG_UART_MCUX_LPUART) += fsl_lpuart.o