diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-19 06:28:01 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 13:08:32 +0200 |
commit | d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc (patch) | |
tree | caa54b4229e86a68c84ab5961af34e087dce5302 /bsps/powerpc/qoriq | |
parent | bsps/powerpc: Move shared btimer support (diff) | |
download | rtems-d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc.tar.bz2 |
bsps: Move console drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/powerpc/qoriq')
-rw-r--r-- | bsps/powerpc/qoriq/console/console-config.c | 330 | ||||
-rw-r--r-- | bsps/powerpc/qoriq/console/uart-bridge-master.c | 181 | ||||
-rw-r--r-- | bsps/powerpc/qoriq/console/uart-bridge-slave.c | 195 |
3 files changed, 706 insertions, 0 deletions
diff --git a/bsps/powerpc/qoriq/console/console-config.c b/bsps/powerpc/qoriq/console/console-config.c new file mode 100644 index 0000000000..4c1ca1d3f6 --- /dev/null +++ b/bsps/powerpc/qoriq/console/console-config.c @@ -0,0 +1,330 @@ +/** + * @file + * + * @ingroup QorIQ + * + * @brief Console configuration. + */ + +/* + * Copyright (c) 2010, 2017 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <string.h> + +#include <libfdt.h> + +#include <rtems/bspIo.h> + +#include <libchip/ns16550.h> + +#include <asm/epapr_hcalls.h> + +#include <bsp.h> +#include <bsp/fdt.h> +#include <bsp/irq.h> +#include <bsp/qoriq.h> +#include <bsp/intercom.h> +#include <bsp/uart-bridge.h> +#include <bsp/console-termios.h> + +static void output_char(char c); + +#ifdef QORIQ_IS_HYPERVISOR_GUEST +typedef struct { + rtems_termios_device_context base; + uint32_t handle; +} qoriq_bc_context; + +static bool qoriq_bc_probe(rtems_termios_device_context *base) +{ + qoriq_bc_context *ctx; + const void *fdt; + int node; + const uint32_t *handle; + int len; + + fdt = bsp_fdt_get(); + + node = fdt_node_offset_by_compatible(fdt, -1, "epapr,hv-byte-channel"); + if (node < 0) { + return false; + } + + handle = fdt_getprop(fdt, node, "hv-handle", &len); + if (handle == NULL || len != 4) { + return false; + } + + ctx = (qoriq_bc_context *) base; + ctx->handle = fdt32_to_cpu(*handle); + + BSP_output_char = output_char; + return true; +} + +static int qoriq_bc_read_polled(rtems_termios_device_context *base) +{ + qoriq_bc_context *ctx; + char buf[EV_BYTE_CHANNEL_MAX_BYTES]; + unsigned int count; + unsigned int status; + + ctx = (qoriq_bc_context *) base; + count = 1; + status = ev_byte_channel_receive(ctx->handle, &count, buf); + + if (status != EV_SUCCESS || count == 0) { + return -1; + } + + return (unsigned char) buf[0]; +} + +static void qoriq_bc_write_polled( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + qoriq_bc_context *ctx; + uint32_t handle; + + ctx = (qoriq_bc_context *) base; + handle = ctx->handle; + + while (len > 0) { + unsigned int count; + unsigned int status; + char buf2[EV_BYTE_CHANNEL_MAX_BYTES]; + const char *out; + + if (len < EV_BYTE_CHANNEL_MAX_BYTES) { + count = len; + out = memcpy(buf2, buf, len); + } else { + count = EV_BYTE_CHANNEL_MAX_BYTES; + out = buf; + } + + status = ev_byte_channel_send(handle, &count, out); + + if (status == EV_SUCCESS) { + len -= count; + buf += count; + } + } +} + +static const rtems_termios_device_handler qoriq_bc_handler_polled = { + .poll_read = qoriq_bc_read_polled, + .write = qoriq_bc_write_polled, + .mode = TERMIOS_POLLED +}; + +static qoriq_bc_context qoriq_bc_context_0 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("BC 0"), +}; +#endif /* QORIQ_IS_HYPERVISOR_GUEST */ + +#if (QORIQ_UART_0_ENABLE + QORIQ_UART_BRIDGE_0_ENABLE == 2) \ + || (QORIQ_UART_1_ENABLE + QORIQ_UART_BRIDGE_1_ENABLE == 2) + #define BRIDGE_MASTER +#elif QORIQ_UART_BRIDGE_0_ENABLE || QORIQ_UART_BRIDGE_1_ENABLE + #define BRIDGE_SLAVE +#endif + +#ifdef BSP_USE_UART_INTERRUPTS + #define DEVICE_FNS &ns16550_handler_interrupt +#else + #define DEVICE_FNS &ns16550_handler_polled +#endif + +#if QORIQ_UART_0_ENABLE || QORIQ_UART_1_ENABLE + static bool uart_probe(rtems_termios_device_context *base) + { + ns16550_context *ctx = (ns16550_context *) base; + + ctx->clock = BSP_bus_frequency; + + return ns16550_probe(base); + } + + static uint8_t get_register(uintptr_t addr, uint8_t i) + { + volatile uint8_t *reg = (uint8_t *) addr; + + return reg [i]; + } + + static void set_register(uintptr_t addr, uint8_t i, uint8_t val) + { + volatile uint8_t *reg = (uint8_t *) addr; + + reg [i] = val; + } +#endif + +#if QORIQ_UART_0_ENABLE +static ns16550_context qoriq_uart_context_0 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART 0"), + .get_reg = get_register, + .set_reg = set_register, + .port = (uintptr_t) &qoriq.uart_0, + .irq = QORIQ_IRQ_DUART_1, + .initial_baud = BSP_CONSOLE_BAUD +}; +#endif + +#if QORIQ_UART_1_ENABLE +static ns16550_context qoriq_uart_context_1 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART 1"), + .get_reg = get_register, + .set_reg = set_register, + .port = (uintptr_t) &qoriq.uart_1, + .irq = QORIQ_IRQ_DUART_1, + .initial_baud = BSP_CONSOLE_BAUD +}; +#endif + +#ifdef BRIDGE_MASTER + #define BRIDGE_PROBE qoriq_uart_bridge_master_probe + #define BRIDGE_FNS &qoriq_uart_bridge_master + #if QORIQ_UART_BRIDGE_0_ENABLE + static uart_bridge_master_context bridge_0_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 0"), + .device_path = "/dev/ttyS0", + .type = INTERCOM_TYPE_UART_0, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_0_context.transmit_fifo + ) + }; + #define BRIDGE_0_CONTEXT &bridge_0_context.base + #endif + #if QORIQ_UART_BRIDGE_1_ENABLE + static uart_bridge_master_context bridge_1_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 1"), + .device_path = "/dev/ttyS1", + .type = INTERCOM_TYPE_UART_1, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_1_context.transmit_fifo + ) + }; + #define BRIDGE_1_CONTEXT &bridge_1_context.base + #endif +#endif + +#ifdef BRIDGE_SLAVE + #define BRIDGE_PROBE console_device_probe_default + #define BRIDGE_FNS &qoriq_uart_bridge_slave + #if QORIQ_UART_BRIDGE_0_ENABLE + static uart_bridge_slave_context bridge_0_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 0"), + .type = INTERCOM_TYPE_UART_0, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_0_context.transmit_fifo + ) + }; + #define BRIDGE_0_CONTEXT &bridge_0_context.base + #endif + #if QORIQ_UART_BRIDGE_1_ENABLE + static uart_bridge_slave_context bridge_1_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 1"), + .type = INTERCOM_TYPE_UART_1, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_1_context.transmit_fifo + ) + }; + #define BRIDGE_1_CONTEXT &bridge_1_context.base + #endif +#endif + +const console_device console_device_table[] = { + #ifdef QORIQ_IS_HYPERVISOR_GUEST + { + .device_file = "/dev/ttyBC0", + .probe = qoriq_bc_probe, + .handler = &qoriq_bc_handler_polled, + .context = &qoriq_bc_context_0.base + }, + #endif + #if QORIQ_UART_0_ENABLE + { + .device_file = "/dev/ttyS0", + .probe = uart_probe, + .handler = DEVICE_FNS, + .context = &qoriq_uart_context_0.base + }, + #endif + #if QORIQ_UART_1_ENABLE + { + .device_file = "/dev/ttyS1", + .probe = uart_probe, + .handler = DEVICE_FNS, + .context = &qoriq_uart_context_1.base + }, + #endif + #if QORIQ_UART_BRIDGE_0_ENABLE + { + #if QORIQ_UART_1_ENABLE + .device_file = "/dev/ttyB0", + #else + .device_file = "/dev/ttyS0", + #endif + .probe = BRIDGE_PROBE, + .handler = BRIDGE_FNS, + .context = BRIDGE_0_CONTEXT + }, + #endif + #if QORIQ_UART_BRIDGE_1_ENABLE + { + #if QORIQ_UART_1_ENABLE + .device_file = "/dev/ttyB1", + #else + .device_file = "/dev/ttyS1", + #endif + .probe = BRIDGE_PROBE, + .handler = BRIDGE_FNS, + .context = BRIDGE_1_CONTEXT + } + #endif +}; + +const size_t console_device_count = RTEMS_ARRAY_SIZE(console_device_table); + +static void output_char(char c) +{ + rtems_termios_device_context *base = console_device_table[0].context; + +#ifdef QORIQ_IS_HYPERVISOR_GUEST + qoriq_bc_write_polled(base, &c, 1); +#else + ns16550_polled_putchar(base, c); +#endif +} + +#ifdef QORIQ_IS_HYPERVISOR_GUEST +static void qoriq_bc_output_char_init(char c) +{ + rtems_termios_device_context *base = console_device_table[0].context; + + qoriq_bc_probe(base); + output_char(c); +} + +BSP_output_char_function_type BSP_output_char = qoriq_bc_output_char_init; +#else +BSP_output_char_function_type BSP_output_char = output_char; +#endif + +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/powerpc/qoriq/console/uart-bridge-master.c b/bsps/powerpc/qoriq/console/uart-bridge-master.c new file mode 100644 index 0000000000..588e0a42ad --- /dev/null +++ b/bsps/powerpc/qoriq/console/uart-bridge-master.c @@ -0,0 +1,181 @@ +/** + * @file + * + * @ingroup QorIQUartBridge + * + * @brief UART bridge master implementation. + */ + +/* + * Copyright (c) 2011-2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <sys/stat.h> +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> + +#include <bspopts.h> +#include <bsp/uart-bridge.h> + +#define TRANSMIT_EVENT RTEMS_EVENT_13 + +static void serial_settings(int fd) +{ + struct termios term; + int rv = tcgetattr(fd, &term); + assert(rv == 0); + + term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + term.c_oflag &= ~OPOST; + term.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + term.c_cflag &= ~(CSIZE | PARENB); + term.c_cflag |= CS8; + + term.c_cc [VMIN] = 1; + term.c_cc [VTIME] = 1; + + rv = tcsetattr(fd, TCSANOW, &term); + assert(rv == 0); +} + +static void uart_bridge_master_service(intercom_packet *packet, void *arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_master_context *ctx = arg; + + sc = rtems_chain_append_with_notification( + &ctx->transmit_fifo, + &packet->glue.node, + ctx->transmit_task, + TRANSMIT_EVENT + ); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void receive_task(rtems_task_argument arg) +{ + uart_bridge_master_context *ctx = (uart_bridge_master_context *) arg; + intercom_type type = ctx->type; + + int fd = open(ctx->device_path, O_RDONLY); + assert(fd >= 0); + + serial_settings(fd); + + while (true) { + intercom_packet *packet = qoriq_intercom_allocate_packet( + type, + INTERCOM_SIZE_64 + ); + ssize_t in = read(fd, packet->data, packet->size - 1); + if (in > 0) { + packet->size = (size_t) in; + qoriq_intercom_send_packet(QORIQ_UART_BRIDGE_SLAVE_CORE, packet); + } else { + qoriq_intercom_free_packet(packet); + } + } +} + +static void transmit_task(rtems_task_argument arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_master_context *ctx = (uart_bridge_master_context *) arg; + rtems_chain_control *fifo = &ctx->transmit_fifo; + + int fd = open(ctx->device_path, O_WRONLY); + assert(fd >= 0); + + serial_settings(fd); + + while (true) { + intercom_packet *packet = NULL; + sc = rtems_chain_get_with_wait( + fifo, + TRANSMIT_EVENT, + RTEMS_NO_TIMEOUT, + (rtems_chain_node **) &packet + ); + assert(sc == RTEMS_SUCCESSFUL); + write(fd, packet->data, packet->size); + qoriq_intercom_free_packet(packet); + } +} + +static rtems_id create_task( + char name, + rtems_task_entry entry, + uart_bridge_master_context *ctx +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_id task = RTEMS_ID_NONE; + char index = (char) ('0' + ctx->type - INTERCOM_TYPE_UART_0); + + sc = rtems_task_create( + rtems_build_name('U', 'B', name, index), + QORIQ_UART_BRIDGE_TASK_PRIORITY, + 0, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &task + ); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start( + task, + entry, + (rtems_task_argument) ctx + ); + assert(sc == RTEMS_SUCCESSFUL); + + return task; +} + +bool qoriq_uart_bridge_master_probe(rtems_termios_device_context *base) +{ + uart_bridge_master_context *ctx = (uart_bridge_master_context *) base; + intercom_type type = ctx->type; + + qoriq_intercom_service_install(type, uart_bridge_master_service, ctx); + create_task('R', receive_task, ctx); + ctx->transmit_task = create_task('T', transmit_task, ctx); + + return true; +} + +static bool first_open( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + return false; +} + +static bool set_attributes( + rtems_termios_device_context *base, + const struct termios *term +) +{ + return false; +} + +const rtems_termios_device_handler qoriq_uart_bridge_master = { + .first_open = first_open, + .set_attributes = set_attributes, + .mode = TERMIOS_POLLED +}; diff --git a/bsps/powerpc/qoriq/console/uart-bridge-slave.c b/bsps/powerpc/qoriq/console/uart-bridge-slave.c new file mode 100644 index 0000000000..44d4cfb712 --- /dev/null +++ b/bsps/powerpc/qoriq/console/uart-bridge-slave.c @@ -0,0 +1,195 @@ +/** + * @file + * + * @ingroup QorIQUartBridge + * + * @brief UART bridge slave implementation. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <assert.h> + +#include <libchip/sersupp.h> + +#include <bspopts.h> +#include <bsp/uart-bridge.h> + +#define TRANSMIT_EVENT RTEMS_EVENT_13 + +static rtems_mode disable_preemption(void) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_mode prev_mode = 0; + + sc = rtems_task_mode (RTEMS_NO_PREEMPT, RTEMS_PREEMPT_MASK, &prev_mode); + assert(sc == RTEMS_SUCCESSFUL); + + return prev_mode; +} + +static void restore_preemption(rtems_mode prev_mode) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + sc = rtems_task_mode (prev_mode, RTEMS_PREEMPT_MASK, &prev_mode); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void uart_bridge_slave_service(intercom_packet *packet, void *arg) +{ + uart_bridge_slave_context *ctx = arg; + struct rtems_termios_tty *tty = ctx->tty; + + /* Workaround for https://www.rtems.org/bugzilla/show_bug.cgi?id=1736 */ + rtems_mode prev_mode = disable_preemption(); + + rtems_termios_enqueue_raw_characters(tty, packet->data, (int) packet->size); + qoriq_intercom_free_packet(packet); + + restore_preemption(prev_mode); +} + +static void transmit_task(rtems_task_argument arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) arg; + rtems_chain_control *fifo = &ctx->transmit_fifo; + struct rtems_termios_tty *tty = ctx->tty; + + while (true) { + intercom_packet *packet = NULL; + sc = rtems_chain_get_with_wait( + fifo, + TRANSMIT_EVENT, + RTEMS_NO_TIMEOUT, + (rtems_chain_node **) &packet + ); + assert(sc == RTEMS_SUCCESSFUL); + + /* Workaround for https://www.rtems.org/bugzilla/show_bug.cgi?id=1736 */ + rtems_mode prev_mode = disable_preemption(); + + size_t size = packet->size; + qoriq_intercom_send_packet(QORIQ_UART_BRIDGE_MASTER_CORE, packet); + rtems_termios_dequeue_characters(tty, (int) size); + + restore_preemption(prev_mode); + } +} + +static void create_transmit_task( + uart_bridge_slave_context *ctx +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_id task = RTEMS_ID_NONE; + char index = (char) ('0' + ctx->type - INTERCOM_TYPE_UART_0); + + sc = rtems_task_create( + rtems_build_name('U', 'B', 'T', index), + QORIQ_UART_BRIDGE_TASK_PRIORITY, + 0, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &task + ); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start( + task, + transmit_task, + (rtems_task_argument) ctx + ); + assert(sc == RTEMS_SUCCESSFUL); + + ctx->transmit_task = task; +} + +static bool first_open( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; + intercom_type type = ctx->type; + + ctx->tty = tty; + rtems_termios_set_initial_baud(tty, 115200); + create_transmit_task(ctx); + qoriq_intercom_service_install(type, uart_bridge_slave_service, ctx); + + return true; +} + +static void last_close( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; + + qoriq_intercom_service_remove(ctx->type); +} + +static void write_with_interrupts( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + if (len > 0) { + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; + intercom_packet *packet = qoriq_intercom_allocate_packet( + ctx->type, + INTERCOM_SIZE_64 + ); + + packet->size = len; + memcpy(packet->data, buf, len); + + /* + * Due to the lovely Termios implementation we have to hand this over to + * another context. + */ + sc = rtems_chain_append_with_notification( + &ctx->transmit_fifo, + &packet->glue.node, + ctx->transmit_task, + TRANSMIT_EVENT + ); + assert(sc == RTEMS_SUCCESSFUL); + } +} + +static bool set_attributes( + rtems_termios_device_context *base, + const struct termios *term +) +{ + return false; +} + +const rtems_termios_device_handler qoriq_uart_bridge_slave = { + .first_open = first_open, + .last_close = last_close, + .write = write_with_interrupts, + .set_attributes = set_attributes, + .mode = TERMIOS_IRQ_DRIVEN +}; |