drivers: serial: Add MSP-EXP432P401R UART
This patch adds support for on board UART_0 on MSP-EXP432P401R-LAUNCHXL.
Driver makes use of driverlib available in ROM by default, thus saving
code space.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 8b3fecd..ac28e6e 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -101,4 +101,6 @@
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 0000000..b729f1a
--- /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 923e8e8..1c0225d 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -20,3 +20,4 @@
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 0000000..23fee10
--- /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 <driverlib/gpio.h>
+
+#include <uart.h>
+
+/* Driverlib includes */
+#include <driverlib/rom.h>
+#include <driverlib/rom_map.h>
+#include <driverlib/uart.h>
+
+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 0000000..721d302
--- /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
+...