diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 8b3fecd5109..ac28e6e5464 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -101,4 +101,6 @@ source "drivers/serial/Kconfig.esp32" source "drivers/serial/Kconfig.gecko" +source "drivers/serial/Kconfig.msp432p4xx" + endif diff --git a/drivers/serial/Kconfig.msp432p4xx b/drivers/serial/Kconfig.msp432p4xx new file mode 100644 index 00000000000..b729f1ace1d --- /dev/null +++ b/drivers/serial/Kconfig.msp432p4xx @@ -0,0 +1,9 @@ +menuconfig UART_MSP432P4XX + depends on SOC_SERIES_MSP432P4XX + bool "MSP432P4XX UART driver" + default n + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + depends on SOC_FAMILY_TISIMPLELINK + help + This option enables the MSP432P4XX UART driver, for UART_0. diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 923e8e89e2e..1c0225d27d3 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_UART_RISCV_QEMU) += uart_riscv_qemu.o obj-$(CONFIG_UART_FE310) += uart_fe310.o obj-$(CONFIG_UART_ESP32) += uart_esp32.o obj-$(CONFIG_UART_GECKO) += uart_gecko.o +obj-$(CONFIG_UART_MSP432P4XX) += uart_msp432p4xx.o diff --git a/drivers/serial/uart_msp432p4xx.c b/drivers/serial/uart_msp432p4xx.c new file mode 100644 index 00000000000..23fee10f31d --- /dev/null +++ b/drivers/serial/uart_msp432p4xx.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2017, Linaro Ltd + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* See www.ti.com/lit/pdf/slau356f, Chapter 22, for MSP432P4XX UART info. */ + +/* include driverlib/gpio.h (from the msp432p4xx SDK) before Z's uart.h so + * that the definition of BIT is not overriden */ +#include + +#include + +/* Driverlib includes */ +#include +#include +#include + +struct uart_msp432p4xx_dev_data_t { + /* UART config structure */ + eUSCI_UART_Config uartConfig; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_t cb; /**< Callback function pointer */ +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +#define DEV_CFG(dev) \ + ((const struct uart_device_config * const)(dev)->config->config_info) +#define DEV_DATA(dev) \ + ((struct uart_msp432p4xx_dev_data_t * const)(dev)->driver_data) + +static struct device DEVICE_NAME_GET(uart_msp432p4xx_0); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void uart_msp432p4xx_isr(void *arg); +#endif + +static const struct uart_device_config uart_msp432p4xx_dev_cfg_0 = { + .base = (void *)CONFIG_UART_MSP432P4XX_BASE_ADDRESS, + .sys_clk_freq = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC, +}; + +static struct uart_msp432p4xx_dev_data_t uart_msp432p4xx_dev_data_0 = { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .cb = NULL, +#endif +}; + +static int baudrate_set(eUSCI_UART_Config *config, uint32_t baudrate) +{ + u16_t prescalar; + u8_t first_mod_reg, second_mod_reg; + + switch (baudrate) { + case 1200: + prescalar = 2500; + first_mod_reg = 0; + second_mod_reg = 0; + break; + case 2400: + prescalar = 1250; + first_mod_reg = 0; + second_mod_reg = 0; + break; + case 4800: + prescalar = 625; + first_mod_reg = 0; + second_mod_reg = 0; + break; + case 9600: + prescalar = 312; + first_mod_reg = 8; + second_mod_reg = 0; + break; + case 19200: + prescalar = 156; + first_mod_reg = 4; + second_mod_reg = 0; + break; + case 38400: + prescalar = 78; + first_mod_reg = 2; + second_mod_reg = 0; + break; + case 57600: + prescalar = 52; + first_mod_reg = 1; + second_mod_reg = 37; + break; + case 115200: + prescalar = 26; + first_mod_reg = 0; + second_mod_reg = 111; + break; + case 230400: + prescalar = 13; + first_mod_reg = 0; + second_mod_reg = 37; + break; + case 460800: + prescalar = 6; + first_mod_reg = 8; + second_mod_reg = 32; + break; + default: + return -EINVAL; + } + + config->clockPrescalar = prescalar; + config->firstModReg = first_mod_reg; + config->secondModReg = second_mod_reg; + + return 0; +} + +static int uart_msp432p4xx_init(struct device *dev) +{ + int err; + const struct uart_device_config *config = DEV_CFG(dev); + eUSCI_UART_Config UartConfig; + + /* Select P1.2 and P1.3 in UART mode */ + MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, + (GPIO_PIN2 | GPIO_PIN3), GPIO_PRIMARY_MODULE_FUNCTION); + + UartConfig.selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK; + UartConfig.parity = EUSCI_A_UART_NO_PARITY; + UartConfig.msborLsbFirst = EUSCI_A_UART_LSB_FIRST; + UartConfig.numberofStopBits = EUSCI_A_UART_ONE_STOP_BIT; + UartConfig.uartMode = EUSCI_A_UART_MODE; + UartConfig.overSampling = EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION; + + /* Baud rate settings calculated for 48MHz */ + err = baudrate_set(&UartConfig, CONFIG_UART_MSP432P4XX_BAUD_RATE); + if (err) { + return err; + } + /* Configure UART Module */ + MAP_UART_initModule((unsigned long)config->base, &UartConfig); + + /* Enable UART module */ + MAP_UART_enableModule((unsigned long)config->base); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + IRQ_CONNECT(TI_MSP432P4XX_UART_40001000_IRQ_0, + TI_MSP432P4XX_UART_40001000_IRQ_0_PRIORITY, + uart_msp432p4xx_isr, DEVICE_GET(uart_msp432p4xx_0), + 0); + irq_enable(TI_MSP432P4XX_UART_40001000_IRQ_0); + +#endif + return 0; +} + +static int uart_msp432p4xx_poll_in(struct device *dev, unsigned char *c) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + *c = MAP_UART_receiveData((unsigned long)config->base); + + return 0; +} + +static unsigned char uart_msp432p4xx_poll_out(struct device *dev, + unsigned char c) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UART_transmitData((unsigned long)config->base, c); + + return c; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int uart_msp432p4xx_fifo_fill(struct device *dev, + const u8_t *tx_data, int size) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int num_tx = 0; + + while ((size - num_tx) > 0) { + MAP_UART_transmitData((unsigned long)config->base, + tx_data[num_tx]); + if (MAP_UART_getInterruptStatus((unsigned long)config->base, + EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG)) { + num_tx++; + } else { + break; + } + } + + return (int)num_tx; +} + +static int uart_msp432p4xx_fifo_read(struct device *dev, u8_t *rx_data, + const int size) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int num_rx = 0; + + while (((size - num_rx) > 0) && + MAP_UART_getInterruptStatus((unsigned long)config->base, + EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG)) { + + rx_data[num_rx++] = + MAP_UART_receiveData((unsigned long)config->base); + } + + return num_rx; +} + +static void uart_msp432p4xx_irq_tx_enable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UART_enableInterrupt((unsigned long)config->base, + EUSCI_A_UART_TRANSMIT_INTERRUPT); +} + +static void uart_msp432p4xx_irq_tx_disable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UART_disableInterrupt((unsigned long)config->base, + EUSCI_A_UART_TRANSMIT_INTERRUPT); +} + +static int uart_msp432p4xx_irq_tx_ready(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int int_status; + + int_status = MAP_UART_getInterruptStatus((unsigned long)config->base, + EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG); + + return (int_status & EUSCI_A_IE_TXIE); +} + +static void uart_msp432p4xx_irq_rx_enable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UART_enableInterrupt((unsigned long)config->base, + EUSCI_A_UART_RECEIVE_INTERRUPT); +} + +static void uart_msp432p4xx_irq_rx_disable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UART_disableInterrupt((unsigned long)config->base, + EUSCI_A_UART_RECEIVE_INTERRUPT); +} + +static int uart_msp432p4xx_irq_tx_complete(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + return MAP_UART_getInterruptStatus((unsigned long)config->base, + EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG); +} + +static int uart_msp432p4xx_irq_rx_ready(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int int_status; + + int_status = MAP_UART_getInterruptStatus((unsigned long)config->base, + EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG); + + return (int_status & EUSCI_A_IE_RXIE); +} + +static void uart_msp432p4xx_irq_err_enable(struct device *dev) +{ + /* Not yet used in zephyr */ +} + +static void uart_msp432p4xx_irq_err_disable(struct device *dev) +{ + /* Not yet used in zephyr */ +} + +static int uart_msp432p4xx_irq_is_pending(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int int_status; + + int_status = MAP_UART_getEnabledInterruptStatus( + (unsigned long)config->base); + + return (int_status & (EUSCI_A_IE_TXIE | EUSCI_A_IE_RXIE)); +} + +static int uart_msp432p4xx_irq_update(struct device *dev) +{ + return 1; +} + +static void uart_msp432p4xx_irq_callback_set(struct device *dev, + uart_irq_callback_t cb) +{ + struct uart_msp432p4xx_dev_data_t * const dev_data = DEV_DATA(dev); + + dev_data->cb = cb; +} + +/** + * @brief Interrupt service routine. + * + * This simply calls the callback function, if one exists. + * + * @param arg Argument to ISR. + * + * @return N/A + */ +static void uart_msp432p4xx_isr(void *arg) +{ + struct device *dev = arg; + const struct uart_device_config *config = DEV_CFG(dev); + struct uart_msp432p4xx_dev_data_t * const dev_data = DEV_DATA(dev); + unsigned int int_status; + + int_status = MAP_UART_getEnabledInterruptStatus( + (unsigned long)config->base); + + if (dev_data->cb) { + dev_data->cb(dev); + } + /* + * Clear interrupts only after cb called, as Zephyr UART clients expect + * to check interrupt status during the callback. + */ + MAP_UART_disableInterrupt((unsigned long)config->base, int_status); +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static const struct uart_driver_api uart_msp432p4xx_driver_api = { + .poll_in = uart_msp432p4xx_poll_in, + .poll_out = uart_msp432p4xx_poll_out, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_msp432p4xx_fifo_fill, + .fifo_read = uart_msp432p4xx_fifo_read, + .irq_tx_enable = uart_msp432p4xx_irq_tx_enable, + .irq_tx_disable = uart_msp432p4xx_irq_tx_disable, + .irq_tx_ready = uart_msp432p4xx_irq_tx_ready, + .irq_rx_enable = uart_msp432p4xx_irq_rx_enable, + .irq_rx_disable = uart_msp432p4xx_irq_rx_disable, + .irq_tx_complete = uart_msp432p4xx_irq_tx_complete, + .irq_rx_ready = uart_msp432p4xx_irq_rx_ready, + .irq_err_enable = uart_msp432p4xx_irq_err_enable, + .irq_err_disable = uart_msp432p4xx_irq_err_disable, + .irq_is_pending = uart_msp432p4xx_irq_is_pending, + .irq_update = uart_msp432p4xx_irq_update, + .irq_callback_set = uart_msp432p4xx_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +DEVICE_AND_API_INIT(uart_msp432p4xx_0, CONFIG_UART_MSP432P4XX_NAME, + uart_msp432p4xx_init, &uart_msp432p4xx_dev_data_0, + &uart_msp432p4xx_dev_cfg_0, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + (void *)&uart_msp432p4xx_driver_api); diff --git a/dts/arm/yaml/ti,msp432p4xx-uart.yaml b/dts/arm/yaml/ti,msp432p4xx-uart.yaml new file mode 100644 index 00000000000..721d302f25e --- /dev/null +++ b/dts/arm/yaml/ti,msp432p4xx-uart.yaml @@ -0,0 +1,30 @@ +--- +title: TI MSP432P4XX UART +id: ti,msp432p4xx-uart +version: 0.1 + +description: > + This binding gives a base representation of the TI MSP432P4XX UART + +inherits: + - !include uart.yaml + +properties: + - compatible: + type: string + category: required + description: compatible strings + constraint: "ti,msp432p4xx-uart" + + - reg: + type: array + description: mmio register space + generation: define + category: required + + - interrupts: + type: array + category: required + description: required interrupts + generation: define +...