diff --git a/drivers/serial/Kconfig.ns16550 b/drivers/serial/Kconfig.ns16550 index cc2612d827..9e43609ab3 100644 --- a/drivers/serial/Kconfig.ns16550 +++ b/drivers/serial/Kconfig.ns16550 @@ -47,6 +47,25 @@ config UART_NS16550_DLF Says n if you are not sure if hardware supports this. +config UART_NS16550_LINE_CTRL + bool "Enable Serial Line Control for Apps" + default n + depends on UART_LINE_CTRL && UART_NS16550 + help + This enables the API for apps to control the serial line, + such as CTS and RTS. + + Says n if not sure. + +config UART_NS16550_DRV_CMD + bool "Enable Driver Commands" + default n + depends on UART_DRV_CMD && UART_NS16550 + help + This enables the API for apps to send commands to driver. + + Says n if not sure. + # ---------- Port 0 ---------- menuconfig UART_NS16550_PORT_0 diff --git a/drivers/serial/uart_ns16550.c b/drivers/serial/uart_ns16550.c index 9814fa758c..ba8bf8bb95 100644 --- a/drivers/serial/uart_ns16550.c +++ b/drivers/serial/uart_ns16550.c @@ -46,6 +46,8 @@ #include #endif /* CONFIG_PCI */ +#include "uart_ns16550.h" + /* register definitions */ #define REG_THR 0x00 /* Transmitter holding reg. */ @@ -221,6 +223,25 @@ static inline void set_dlf(struct device *dev, uint32_t val) } #endif +static void set_baud_rate(struct device *dev, uint32_t baud_rate) +{ + struct uart_device_config * const dev_cfg = DEV_CFG(dev); + struct uart_ns16550_dev_data_t * const dev_data = DEV_DATA(dev); + uint32_t divisor; /* baud rate divisor */ + + if ((baud_rate != 0) && (dev_cfg->sys_clk_freq != 0)) { + /* calculate baud rate divisor */ + divisor = (dev_cfg->sys_clk_freq / baud_rate) >> 4; + + /* set the DLAB to access the baud rate divisor registers */ + OUTBYTE(LCR(dev), LCR_DLAB); + OUTBYTE(BRDL(dev), (unsigned char)(divisor & 0xff)); + OUTBYTE(BRDH(dev), (unsigned char)((divisor >> 8) & 0xff)); + + dev_data->baud_rate = baud_rate; + } +} + #if defined(CONFIG_UART_NS16550_PCI) static inline int ns16550_pci_uart_scan(struct device *dev) @@ -264,11 +285,9 @@ static inline int ns16550_pci_uart_scan(struct device *dev) */ static int uart_ns16550_init(struct device *dev) { - struct uart_device_config * const dev_cfg = DEV_CFG(dev); struct uart_ns16550_dev_data_t * const dev_data = DEV_DATA(dev); int old_level; /* old interrupt lock level */ - uint32_t divisor; /* baud rate divisor */ uint8_t mdc = 0; if (!ns16550_pci_uart_scan(dev)) { @@ -279,15 +298,7 @@ static int uart_ns16550_init(struct device *dev) old_level = irq_lock(); - if ((dev_data->baud_rate != 0) && (dev_cfg->sys_clk_freq != 0)) { - /* calculate baud rate divisor */ - divisor = (dev_cfg->sys_clk_freq / dev_data->baud_rate) >> 4; - - /* set the DLAB to access the baud rate divisor registers */ - OUTBYTE(LCR(dev), LCR_DLAB); - OUTBYTE(BRDL(dev), (unsigned char)(divisor & 0xff)); - OUTBYTE(BRDH(dev), (unsigned char)((divisor >> 8) & 0xff)); - } + set_baud_rate(dev, dev_data->baud_rate); #ifdef CONFIG_UART_NS16550_DLF set_dlf(dev, dev_data->dlf); @@ -549,6 +560,79 @@ static unsigned int uart_ns16550_irq_get(struct device *dev) #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +#ifdef CONFIG_UART_NS16550_LINE_CTRL + +/** + * @brief Manipualte line control for UART. + * + * @param dev UART device struct (of type struct uart_device_config_t) + * @param ctrl The line control to be manipulated + * @param val Value to set the line control + * + * @return DEV_OK if successful, failed otherwise + */ +static int uart_ns16550_line_ctrl_set(struct device *dev, + uint32_t ctrl, uint32_t val) +{ + uint32_t mdc, chg; + + switch (ctrl) { + case LINE_CTRL_BAUD_RATE: + set_baud_rate(dev, val); + return DEV_OK; + + case LINE_CTRL_RTS: + case LINE_CTRL_DTR: + mdc = INBYTE(MDC(dev)); + + if (ctrl == LINE_CTRL_RTS) { + chg = MCR_RTS; + } else { + chg = MCR_DTR; + } + + if (val) { + mdc |= chg; + } else { + mdc &= ~(chg); + } + OUTBYTE(MDC(dev), mdc); + return DEV_OK; + } + + return DEV_INVALID_OP; +} + +#endif /* CONFIG_UART_NS16550_LINE_CTRL */ + +#ifdef CONFIG_UART_NS16550_DRV_CMD + +/** + * @brief Send extra command to driver + * + * @param dev UART device struct (of type struct uart_device_config_t) + * @param cmd Command to driver + * @param p Parameter to the command + * + * @return DEV_OK if successful, failed otherwise + */ +static int uart_ns16550_drv_cmd(struct device *dev, uint32_t cmd, uint32_t p) +{ + switch (cmd) { + +#ifdef CONFIG_UART_NS16550_DLF + case CMD_SET_DLF: + set_dlf(dev, p); + return DEV_OK; +#endif + + } + + return DEV_INVALID_OP; +} + +#endif /* CONFIG_UART_NS16550_DRV_CMD */ + static struct uart_driver_api uart_ns16550_driver_api = { .poll_in = uart_ns16550_poll_in, @@ -571,6 +655,14 @@ static struct uart_driver_api uart_ns16550_driver_api = { .irq_get = uart_ns16550_irq_get, #endif + +#ifdef CONFIG_UART_NS16550_LINE_CTRL + .line_ctrl_set = uart_ns16550_line_ctrl_set, +#endif + +#ifdef CONFIG_UART_NS16550_DRV_CMD + .drv_cmd = uart_ns16550_drv_cmd, +#endif }; #ifdef CONFIG_UART_NS16550_PORT_0 diff --git a/drivers/serial/uart_ns16550.h b/drivers/serial/uart_ns16550.h new file mode 100644 index 0000000000..5f6300a9d8 --- /dev/null +++ b/drivers/serial/uart_ns16550.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 Intel Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Header file for the NS16550 UART + */ + +#ifndef _UART_NS16550_H_ +#define _UART_NS16550_H_ + +#define CMD_SET_DLF 0x01 + +#endif /* _UART_NS16550_H_ */