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
+...