aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTushar Khandelwal <tushar.khandelwal@arm.com>2019-07-05 15:45:31 +0100
committerTushar Khandelwal <tushar.khandelwal@arm.com>2019-09-04 11:37:10 +0100
commitad1b1bb3577cbaa50e70b02083dd8c14eee39157 (patch)
tree216a178c831388082fb00e20967e23ef7a9a83d2
parent11d90fac77bc8a7770bce3a95b5f387176f924c5 (diff)
add pl011 uart driver and log module
Change-Id: I706f33683c67198a37d3fdcfe607cfd460db711e Signed-off-by: Tushar Khandelwal <tushar.khandelwal@arm.com>
-rw-r--r--framework/src/Makefile2
-rw-r--r--product/corstone-700/firmware/config_log.c60
-rw-r--r--product/corstone-700/firmware/firmware.mk19
-rw-r--r--product/corstone-700/include/escm3_mmap.h2
-rw-r--r--product/corstone-700/module/log/include/mod_log.h188
-rw-r--r--product/corstone-700/module/log/src/Makefile10
-rw-r--r--product/corstone-700/module/log/src/mod_log.c380
-rw-r--r--product/corstone-700/module/pl011/include/mod_pl011.h55
-rw-r--r--product/corstone-700/module/pl011/src/Makefile10
-rw-r--r--product/corstone-700/module/pl011/src/mod_pl011.c216
-rw-r--r--product/corstone-700/module/pl011/src/pl011.h160
11 files changed, 1101 insertions, 1 deletions
diff --git a/framework/src/Makefile b/framework/src/Makefile
index a7b3644..78a5175 100644
--- a/framework/src/Makefile
+++ b/framework/src/Makefile
@@ -26,4 +26,6 @@ endif
BS_LIB_INCLUDES += $(ARCH_DIR)/include
BS_LIB_INCLUDES += $(FWK_DIR)/include
+BS_LIB_INCLUDES += $(FWK_DIR)/../module/log/include
+BS_LIB_INCLUDES += $(FWK_DIR)/../product/corstone-700/include/
include $(BS_DIR)/lib.mk
diff --git a/product/corstone-700/firmware/config_log.c b/product/corstone-700/firmware/config_log.c
new file mode 100644
index 0000000..e9e4b47
--- /dev/null
+++ b/product/corstone-700/firmware/config_log.c
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#include <fwk_banner.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <system_clock.h>
+#include <mod_log.h>
+#include <mod_pl011.h>
+#include <escm3_mmap.h>
+
+/*
+ * PL011 module
+ */
+static const struct fwk_element pl011_element_desc_table[] = {
+ [0] = {
+ .name = "uart",
+ .data = &((struct mod_pl011_device_config) {
+ .reg_base = ES_PL011_UART_BASE,
+ .baud_rate_bps = 115200,
+ .clock_rate_hz = SYSTEM_CLOCK,
+ .clock_id = FWK_ID_NONE_INIT,
+ }),
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *get_pl011_table(fwk_id_t module_id)
+{
+ return pl011_element_desc_table;
+}
+
+struct fwk_module_config config_pl011 = {
+ .get_element_table = get_pl011_table,
+};
+
+/*
+ * Log module
+ */
+static const struct mod_log_config log_data = {
+ .device_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_PL011, 0),
+ .api_id = FWK_ID_API(FWK_MODULE_IDX_PL011, 0),
+ .log_groups = MOD_LOG_GROUP_ERROR |
+ MOD_LOG_GROUP_INFO |
+ MOD_LOG_GROUP_WARNING |
+ MOD_LOG_GROUP_DEBUG,
+ .banner = FWK_BANNER_ES
+ BUILD_VERSION_DESCRIBE_STRING "\n",
+};
+
+struct fwk_module_config config_log = {
+ .data = &log_data,
+};
diff --git a/product/corstone-700/firmware/firmware.mk b/product/corstone-700/firmware/firmware.mk
new file mode 100644
index 0000000..022eddd
--- /dev/null
+++ b/product/corstone-700/firmware/firmware.mk
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_FIRMWARE_CPU := cortex-m3
+BS_FIRMWARE_HAS_MULTITHREADING := no
+BS_FIRMWARE_HAS_NOTIFICATION := yes
+BS_FIRMWARE_HAS_OPENAMP := yes
+
+BS_FIRMWARE_MODULES := \
+ pl011 \
+ log
+
+BS_FIRMWARE_SOURCES := \
+ config_log.c
+
+include $(BS_DIR)/firmware.mk
diff --git a/product/corstone-700/include/escm3_mmap.h b/product/corstone-700/include/escm3_mmap.h
index e29194b..926a516 100644
--- a/product/corstone-700/include/escm3_mmap.h
+++ b/product/corstone-700/include/escm3_mmap.h
@@ -19,7 +19,7 @@
/* ========================================================================== */
/* ================ Peripheral memory map ============== */
/* ========================================================================== */
-#define ES_PL011_UART_BASE (0x4000F000ul)
+#define ES_PL011_UART_BASE (0x40002000ul)
/* ============================== MHU Devices ================================*/
#define MHU0_H_ES_BASE 0x40100000
diff --git a/product/corstone-700/module/log/include/mod_log.h b/product/corstone-700/module/log/include/mod_log.h
new file mode 100644
index 0000000..ca687d4
--- /dev/null
+++ b/product/corstone-700/module/log/include/mod_log.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef MOD_LOG_H
+#define MOD_LOG_H
+
+#include <fwk_id.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModuleLog Logging
+ *
+ * \brief Module used to write messages to an output device.
+ *
+ * \details This module is used to log messages (similar to a printf() call)
+ * using a configurable output device (for example, a UART).
+ *
+ * A grouping feature allows logged messages to be organized into
+ * categories that can be enabled or disabled through the module
+ * configuration.
+ * @{
+ */
+
+/*!
+ * \brief Log groups.
+ *
+ * \details Log groups are used for:
+ * * Categorizing a message during the \ref mod_log_api::log()
+ * call.
+ * * Selectively enabling or disabling certain message types.
+ *
+ * \note Log groups are not organized in a heirarchy and do not indicate any
+ * kind of priority.
+ */
+enum mod_log_group {
+ /*! Used only to indicate all log groups are muted */
+ MOD_LOG_GROUP_NONE = 0,
+
+ /*! The debug log group */
+ MOD_LOG_GROUP_DEBUG = (1 << 0),
+
+ /*! The error log group */
+ MOD_LOG_GROUP_ERROR = (1 << 1),
+
+ /*! The info log group */
+ MOD_LOG_GROUP_INFO = (1 << 2),
+
+ /*! The warning log group */
+ MOD_LOG_GROUP_WARNING = (1 << 3),
+};
+
+/*!
+ * \brief Log module API identifier.
+ *
+ * \details The log module only has a single API, which is identified by this
+ * identifier.
+ */
+#define MOD_LOG_API_ID FWK_ID_API(FWK_MODULE_IDX_LOG, 0)
+
+/*!
+ * \brief Module configuration.
+ */
+struct mod_log_config {
+ /*! Module or element identifier of the device providing the I/O stream */
+ const fwk_id_t device_id;
+
+ /*! API identifier used to select the API provided by the I/O device */
+ const fwk_id_t api_id;
+
+ /*! Default log groups. Value is a mask (see \ref mod_log_group) */
+ const unsigned int log_groups;
+
+ /*!
+ * \brief A string that will be written automatically after the module
+ * is initialized.
+ *
+ * \details Allows a firmware to provide a string that is written before any
+ * other log messages. The banner uses the \ref MOD_LOG_GROUP_INFO log
+ * group.
+ *
+ * \note May be NULL, in which case the banner functionality is not used.
+ */
+ const char *banner;
+};
+
+/*!
+ * \brief Log driver interface.
+ */
+struct mod_log_driver_api {
+ /*!
+ * \brief Pointer to the device's buffer flush function.
+ *
+ * \details When invoked, the device must ensure any buffered data is
+ * flushed out before it returns.
+ *
+ * \note This function is \b mandatory.
+ *
+ * \param device_id Device identifier.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_DEVICE Internal device error.
+ */
+ int (*flush)(fwk_id_t device_id);
+
+ /*!
+ * \brief Pointer to the function used to write a single character.
+ *
+ * \note This function is \b mandatory.
+ *
+ * \param device_id Device identifier.
+ * \param c The character to be written.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_DEVICE Internal device error.
+ */
+ int (*putchar)(fwk_id_t device_id, char c);
+};
+
+/*!
+ * \brief Module interface
+ */
+struct mod_log_api {
+ /*!
+ * \brief Log formatted data.
+ *
+ * \details Log formatted data assigned to a log group. Only logs that are
+ * assigned to enabled groups will be output. The module configuration
+ * can be used to enable and disable log groups.
+ *
+ * \param group One of the log groups that the log is assigned to (\see
+ * log_group).
+ *
+ * \param fmt String containing the formatted data to be printed out. The
+ * following formats are supported:
+ * * \%c - character format
+ * * \%d and \%i - signed 32-bit decimal format
+ * * \%lx and \%llx - 64-bit hexadecimal format
+ * * \%s - string format
+ * * \%u - unsigned 32-bit decimal format
+ * * \%x - 32-bit hexadecimal format
+ * * \%e - framework error code format
+ *
+ * Numeric formats also accept a padding flag '0\<width\>' between the
+ * '\%' and the format specifier where the resulting string number will
+ * be left padded with zeros. Examples:
+ * * log("%04d", 1) results in "0001"
+ * * log("%04d", 9999) results in "9999"
+ * * __Note__: \<width\> must be a number between 0 and 9 inclusive.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_DATA Invalid format specifier(s).
+ * \retval FWK_E_DEVICE Internal device error.
+ * \retval FWK_E_PARAM Invalid group.
+ * \retval FWK_E_PARAM Invalid 'fmt' pointer.
+ * \retval FWK_E_STATE Log module is not ready.
+ */
+ int (*log)(enum mod_log_group group, const char *fmt, ...);
+
+ /*!
+ * \brief Function used to flush the log's buffer.
+ *
+ * \details When invoked, any buffered log data will be flushed out before
+ * this function returns.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_DEVICE Internal device error.
+ * \retval FWK_E_STATE Log module is not ready.
+ */
+ int (*flush)(void);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_LOG_H */
diff --git a/product/corstone-700/module/log/src/Makefile b/product/corstone-700/module/log/src/Makefile
new file mode 100644
index 0000000..ca7aa40
--- /dev/null
+++ b/product/corstone-700/module/log/src/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Log
+BS_LIB_SOURCES = mod_log.c
+
+include $(BS_DIR)/lib.mk
diff --git a/product/corstone-700/module/log/src/mod_log.c b/product/corstone-700/module/log/src/mod_log.c
new file mode 100644
index 0000000..a9daaa0
--- /dev/null
+++ b/product/corstone-700/module/log/src/mod_log.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_assert.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_mm.h>
+#include <mod_log.h>
+
+static const struct mod_log_config *log_config;
+static struct mod_log_driver_api *log_driver;
+
+#define ALL_GROUPS_MASK (MOD_LOG_GROUP_DEBUG | \
+ MOD_LOG_GROUP_ERROR | \
+ MOD_LOG_GROUP_INFO | \
+ MOD_LOG_GROUP_WARNING)
+
+static const char * const errstr[] = {
+ [FWK_SUCCESS] = "SUCCESS",
+ [-FWK_E_PARAM] = "E_PARAM",
+ [-FWK_E_ALIGN] = "E_ALIGN",
+ [-FWK_E_SIZE] = "E_SIZE",
+ [-FWK_E_HANDLER] = "E_HANDLER",
+ [-FWK_E_ACCESS] = "E_ACCESS",
+ [-FWK_E_RANGE] = "E_RANGE",
+ [-FWK_E_TIMEOUT] = "E_TIMEOUT",
+ [-FWK_E_NOMEM] = "E_NOMEM",
+ [-FWK_E_PWRSTATE] = "E_PWRSTATE",
+ [-FWK_E_SUPPORT] = "E_SUPPORT",
+ [-FWK_E_DEVICE] = "E_DEVICE",
+ [-FWK_E_BUSY] = "E_BUSY",
+ [-FWK_E_OS] = "E_OS",
+ [-FWK_E_DATA] = "E_DATA",
+ [-FWK_E_STATE] = "E_STATE",
+ [-FWK_E_INIT] = "E_INIT",
+ [-FWK_E_OVERWRITTEN] = "E_OVERWRITTEN",
+ [-FWK_E_PANIC] = "E_PANIC",
+};
+
+static int do_putchar(char c)
+{
+ int status;
+
+ /* Include a 'carriage return' before the new line */
+ if (c == '\n') {
+ status = do_putchar('\r');
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ status = log_driver->putchar(log_config->device_id, c);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+
+ return FWK_SUCCESS;
+}
+
+static int print_uint64(uint64_t value, unsigned int base, unsigned int fill)
+{
+ /* Just need enough space to store 64 bit decimal integer */
+ unsigned char str[20];
+ unsigned int i = 0;
+ int status;
+
+ /* Decimal or hexadecimal only */
+ assert((base == 10) || (base == 16));
+
+ do {
+ str[i++] = "0123456789abcdef"[value % base];
+ } while (value /= base);
+
+ while (fill-- > i) {
+ status = do_putchar('0');
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ while (i > 0) {
+ status = do_putchar(str[--i]);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int print_int32(int32_t num, unsigned int fill)
+{
+ int status;
+ uint64_t unum;
+
+ if (num < 0) {
+ status = do_putchar('-');
+ if (status != FWK_SUCCESS)
+ return status;
+ unum = (uint64_t)-num;
+ } else
+ unum = (uint64_t)num;
+
+ return print_uint64(unum, 10, fill);
+}
+
+static int print_string(const char *str)
+{
+ int status;
+
+ while (*str) {
+ status = do_putchar(*str++);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int do_print(const char *fmt, va_list *args)
+{
+ int status;
+ int bit64;
+ int64_t num;
+ uint64_t unum;
+ unsigned int fill;
+
+ while (*fmt) {
+
+ if (*fmt == '%') {
+ fmt++;
+ bit64 = 0;
+ fill = 0;
+
+next_symbol:
+ /* Check the format specifier */
+ switch (*fmt) {
+ case 'e':
+ num = va_arg(*args, int);
+ unum = (uint64_t)(-num);
+ if ((num <= 0) && (unum < FWK_ARRAY_SIZE(errstr))) {
+
+ status = print_string("FWK_");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = print_string(errstr[unum]);
+ } else
+ status = print_int32(num, 0);
+
+ if (status != FWK_SUCCESS)
+ return status;
+ break;
+
+ case 'i': /* Fall through to next one */
+ case 'd':
+ if (bit64)
+ return FWK_E_DATA;
+
+ num = va_arg(*args, int32_t);
+
+ status = print_int32(num, fill);
+ if (status != FWK_SUCCESS)
+ return status;
+ break;
+
+ case 's':
+ status = print_string(va_arg(*args, const char *));
+ if (status != FWK_SUCCESS)
+ return status;
+ break;
+
+ case 'c':
+ status = do_putchar(va_arg(*args, int));
+ if (status != FWK_SUCCESS)
+ return status;
+ break;
+
+ case 'x':
+ if (bit64)
+ unum = va_arg(*args, uint64_t);
+ else
+ unum = va_arg(*args, uint32_t);
+ status = print_uint64(unum, 16, fill);
+ if (status != FWK_SUCCESS)
+ return status;
+ break;
+
+ case 'l':
+ bit64 = 1;
+ fmt++;
+ goto next_symbol;
+
+ case 'u':
+ if (bit64)
+ return FWK_E_DATA;
+
+ unum = va_arg(*args, uint32_t);
+
+ status = print_uint64(unum, 10, fill);
+ if (status != FWK_SUCCESS)
+ return status;
+ break;
+
+ case '0':
+ fmt++;
+ if (((*fmt) < '0') || ((*fmt) > '9'))
+ return FWK_E_DATA;
+ fill = *(fmt++) - '0';
+ goto next_symbol;
+
+ default:
+ /* Exit on any other format specifier */
+ return FWK_E_DATA;
+ }
+ fmt++;
+ continue;
+ }
+ status = do_putchar(*fmt++);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static bool is_valid_group(unsigned int group)
+{
+ /* Check if group is 'none' */
+ if (group == MOD_LOG_GROUP_NONE)
+ return false;
+
+ /* Check if group is within the limits of valid groups */
+ if (group & ~ALL_GROUPS_MASK)
+ return false;
+
+ /* Check if only one group was set */
+ return !(group & (group - 1));
+}
+
+/*
+ * Module API
+ */
+
+static int do_log(enum mod_log_group group, const char *fmt, ...)
+{
+ int status;
+ va_list args;
+
+ /* API called too early */
+ if (log_driver == NULL)
+ return FWK_E_STATE;
+
+ status = fwk_module_check_call(FWK_ID_MODULE(FWK_MODULE_IDX_LOG));
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (!is_valid_group(group))
+ return FWK_E_PARAM;
+
+ if (fmt == NULL)
+ return FWK_E_PARAM;
+
+ if (group & log_config->log_groups) {
+ va_start(args, fmt);
+ status = do_print(fmt, &args);
+ va_end(args);
+
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int do_flush(void)
+{
+ int status;
+
+ /* API called too early */
+ if (log_driver == NULL)
+ return FWK_E_STATE;
+
+ status = fwk_module_check_call(FWK_ID_MODULE(FWK_MODULE_IDX_LOG));
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = log_driver->flush(log_config->device_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_log_api module_api = {
+ .log = do_log,
+ .flush = do_flush,
+};
+
+/*
+ * Framework handlers
+ */
+static int log_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ const struct mod_log_config *config = data;
+
+ /* Module does not support elements */
+ if (element_count > 0)
+ return FWK_E_DATA;
+
+ /* Check for invalid groups in the 'log_groups' mask */
+ if (config->log_groups & ~ALL_GROUPS_MASK)
+ return FWK_E_PARAM;
+
+ log_config = config;
+
+ return FWK_SUCCESS;
+}
+
+static int log_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct mod_log_driver_api *driver;
+
+ /* Skip second round */
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ /* Get the device driver's API */
+ status = fwk_module_bind(log_config->device_id,
+ log_config->api_id,
+ &driver);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ /* Validate device driver's API */
+ if ((driver == NULL) ||
+ (driver->flush == NULL) ||
+ (driver->putchar == NULL))
+ return FWK_E_DATA;
+
+ log_driver = driver;
+
+ if (log_config->banner) {
+ status = do_log(MOD_LOG_GROUP_INFO, log_config->banner);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = do_flush();
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int log_process_bind_request(fwk_id_t requester_id, fwk_id_t id,
+ fwk_id_t api_id, const void **api)
+{
+ *api = &module_api;
+
+ return FWK_SUCCESS;
+}
+
+/* Module descriptor */
+const struct fwk_module module_log = {
+ .name = "Log",
+ .type = FWK_MODULE_TYPE_HAL,
+ .api_count = 1,
+ .init = log_init,
+ .bind = log_bind,
+ .process_bind_request = log_process_bind_request,
+};
diff --git a/product/corstone-700/module/pl011/include/mod_pl011.h b/product/corstone-700/module/pl011/include/mod_pl011.h
new file mode 100644
index 0000000..4ae23f1
--- /dev/null
+++ b/product/corstone-700/module/pl011/include/mod_pl011.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef MOD_PL011_H
+#define MOD_PL011_H
+
+#include <stdint.h>
+#include <fwk_id.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModulePl011 PL011 Driver
+ *
+ * \brief Arm PL011 device driver, fulfilling the Log module's driver API.
+ *
+ * \details This module implements a device driver for the Primecell® PL011
+ * UART.
+ * @{
+ */
+
+/*!
+ * \brief PL011 device configuration data.
+ */
+struct mod_pl011_device_config {
+ /*! Base address of the device registers */
+ uintptr_t reg_base;
+
+ /*! Baud rate (bits per second) */
+ unsigned int baud_rate_bps;
+
+ /*! Reference clock (Hertz) */
+ uint64_t clock_rate_hz;
+
+ /*! Identifier of the clock that this device depends on */
+ fwk_id_t clock_id;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_PL011_H */
diff --git a/product/corstone-700/module/pl011/src/Makefile b/product/corstone-700/module/pl011/src/Makefile
new file mode 100644
index 0000000..900501a
--- /dev/null
+++ b/product/corstone-700/module/pl011/src/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := "PL011"
+BS_LIB_SOURCES = mod_pl011.c
+
+include $(BS_DIR)/lib.mk
diff --git a/product/corstone-700/module/pl011/src/mod_pl011.c b/product/corstone-700/module/pl011/src/mod_pl011.c
new file mode 100644
index 0000000..084fb71
--- /dev/null
+++ b/product/corstone-700/module/pl011/src/mod_pl011.c
@@ -0,0 +1,216 @@
+/*
+ *
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <fwk_assert.h>
+#include <fwk_errno.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_notification.h>
+#include <mod_log.h>
+#include <mod_pl011.h>
+#include <pl011.h>
+
+static const struct mod_pl011_device_config **device_config_table;
+
+static struct pl011_reg *get_device_reg(fwk_id_t device_id)
+{
+ unsigned int device_idx;
+
+ device_idx = fwk_id_get_element_idx(device_id);
+ return (struct pl011_reg *)device_config_table[device_idx]->reg_base;
+}
+
+/*
+ * For details on the constants and equations used to calculate the baud rate
+ * settings, please consult the PL011 TRM.
+ */
+static int set_baud_rate(unsigned int baud_rate_bps, uint64_t clock_rate_hz,
+ struct pl011_reg *reg)
+{
+ uint32_t divisor_integer;
+ uint32_t divisor_fractional;
+ uint32_t divisor;
+ uint32_t clock_rate_x4;
+
+ assert(reg);
+
+ if (baud_rate_bps == 0)
+ return FWK_E_PARAM;
+
+ if ((clock_rate_hz < PL011_UARTCLK_MIN) ||
+ (clock_rate_hz > PL011_UARTCLK_MAX))
+ return FWK_E_PARAM;
+
+ /* The highest clock x4 should still fit in 32 bits */
+ assert((PL011_UARTCLK_MAX * UINT64_C(4)) < UINT32_MAX);
+
+ /* Ensure baud rate is not higher than the clock can support */
+ clock_rate_x4 = clock_rate_hz * 4;
+ if (baud_rate_bps > clock_rate_x4)
+ return FWK_E_RANGE;
+
+ /* Calculate integer and fractional divisors */
+ divisor = clock_rate_x4 / baud_rate_bps;
+ divisor_integer = divisor / 64;
+ divisor_fractional = divisor % 64;
+
+ /* The integer divisor must fit in 16 bits */
+ if (divisor_integer > 0xffff)
+ return FWK_E_RANGE;
+
+ /* The fractional divisor must fit in 6 bits */
+ if (divisor_fractional > 0x3f)
+ return FWK_E_RANGE;
+
+ /*
+ * When the integer divisor is equals to 0xffff, the fractional divisor can
+ * only be 0.
+ */
+ if ((divisor_integer == 0xffff) && (divisor_fractional != 0))
+ return FWK_E_RANGE;
+
+ /* Set registers */
+ reg->IBRD = divisor_integer;
+ reg->FBRD = divisor_fractional;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Module log driver API
+ */
+
+static int do_putchar(fwk_id_t device_id, char c)
+{
+ int status;
+ struct pl011_reg *reg;
+
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ reg = get_device_reg(device_id);
+
+ while (reg->FR & PL011_FR_TXFF)
+ continue;
+
+ reg->DR = c;
+
+ return FWK_SUCCESS;
+}
+
+static int do_flush(fwk_id_t device_id)
+{
+ int status;
+ struct pl011_reg *reg;
+
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ reg = get_device_reg(device_id);
+
+ while (reg->FR & PL011_FR_BUSY)
+ continue;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_log_driver_api driver_api = {
+ .flush = do_flush,
+ .putchar = do_putchar,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int pl011_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ if (element_count == 0)
+ return FWK_E_DATA;
+
+ /*
+ * Create an array of pointers used to store the configuration data pointer
+ * of each element.
+ */
+ device_config_table = fwk_mm_calloc(element_count,
+ sizeof(*device_config_table));
+ if (device_config_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int pl011_element_init(fwk_id_t element_id, unsigned int unused,
+ const void *data)
+{
+ struct pl011_reg *reg;
+ const struct mod_pl011_device_config *config = data;
+ int status;
+
+ reg = (struct pl011_reg *)config->reg_base;
+ if (reg == NULL)
+ return FWK_E_DATA;
+
+ status = set_baud_rate(config->baud_rate_bps,
+ config->clock_rate_hz,
+ reg);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /*
+ * Initialize PL011 device
+ */
+ reg->ECR = PL011_ECR_CLR;
+ reg->LCR_H = PL011_LCR_H_WLEN_8BITS |
+ PL011_LCR_H_FEN;
+ reg->CR = PL011_CR_UARTEN |
+ PL011_CR_RXE |
+ PL011_CR_TXE;
+
+ device_config_table[fwk_id_get_element_idx(element_id)] = config;
+
+ return FWK_SUCCESS;
+}
+
+static int pl011_process_bind_request(fwk_id_t requester_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api)
+{
+ *api = &driver_api;
+
+ return FWK_SUCCESS;
+}
+
+static int pl011_start(fwk_id_t id)
+{
+ const struct mod_pl011_device_config *config;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ config = device_config_table[fwk_id_get_element_idx(id)];
+
+ if (fwk_id_is_type(config->clock_id, FWK_ID_TYPE_NONE))
+ return FWK_SUCCESS;
+
+ return FWK_SUCCESS;
+
+}
+
+
+const struct fwk_module module_pl011 = {
+ .name = "PL011",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = 1,
+ .init = pl011_init,
+ .element_init = pl011_element_init,
+ .start = pl011_start,
+ .process_bind_request = pl011_process_bind_request,
+};
diff --git a/product/corstone-700/module/pl011/src/pl011.h b/product/corstone-700/module/pl011/src/pl011.h
new file mode 100644
index 0000000..48815f4
--- /dev/null
+++ b/product/corstone-700/module/pl011/src/pl011.h
@@ -0,0 +1,160 @@
+/*
+ *
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef PL011_H
+#define PL011_H
+
+#include <fwk_macros.h>
+
+struct pl011_reg {
+ FWK_RW uint16_t DR;
+ uint16_t RESERVED0;
+ union {
+ FWK_RW uint8_t RSR;
+ FWK_RW uint8_t ECR;
+ };
+ uint8_t RESERVED1[0x18 - 0x05];
+ FWK_R uint16_t FR;
+ uint16_t RESERVED2[3];
+ FWK_RW uint8_t ILPR;
+ uint8_t RESERVED3[3];
+ FWK_RW uint16_t IBRD;
+ uint16_t RESERVED4;
+ FWK_RW uint32_t FBRD;
+ FWK_RW uint16_t LCR_H;
+ uint16_t RESERVED5;
+ FWK_RW uint16_t CR;
+ uint16_t RESERVED6;
+ FWK_RW uint16_t IFLS;
+ uint16_t RESERVED7;
+ FWK_RW uint16_t IMSC;
+ uint16_t RESERVED8;
+ FWK_R uint16_t RIS;
+ uint16_t RESERVED9;
+ FWK_R uint16_t MIS;
+ uint16_t RESERVED10;
+ FWK_W uint16_t ICR;
+ uint16_t RESERVED11;
+ FWK_RW uint16_t DMACR;
+ uint8_t RESERVED12[0xFE0 - 0x4C];
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t CID0;
+ FWK_R uint32_t CID1;
+ FWK_R uint32_t CID2;
+ FWK_R uint32_t CID3;
+};
+
+#define PL011_DR_DATA UINT16_C(0x00FF)
+#define PL011_DR_FE UINT16_C(0x0100)
+#define PL011_DR_PE UINT16_C(0x0200)
+#define PL011_DR_BE UINT16_C(0x0400)
+#define PL011_DR_OE UINT16_C(0x0800)
+
+#define PL011_RSR_FE UINT8_C(0x01)
+#define PL011_RSR_PE UINT8_C(0x02)
+#define PL011_RSR_BE UINT8_C(0x04)
+#define PL011_RSR_OE UINT8_C(0x08)
+#define PL011_ECR_CLR UINT8_C(0xFF)
+
+#define PL011_FR_CTS UINT16_C(0x0001)
+#define PL011_FR_DSR UINT16_C(0x0002)
+#define PL011_FR_DCD UINT16_C(0x0004)
+#define PL011_FR_BUSY UINT16_C(0x0008)
+#define PL011_FR_RXFE UINT16_C(0x0010)
+#define PL011_FR_TXFF UINT16_C(0x0020)
+#define PL011_FR_RXFF UINT16_C(0x0040)
+#define PL011_FR_TXFE UINT16_C(0x0080)
+#define PL011_FR_RI UINT16_C(0x0100)
+
+#define PL011_LCR_H_BRK UINT16_C(0x0001)
+#define PL011_LCR_H_PEN UINT16_C(0x0002)
+#define PL011_LCR_H_EPS UINT16_C(0x0004)
+#define PL011_LCR_H_STP2 UINT16_C(0x0008)
+#define PL011_LCR_H_FEN UINT16_C(0x0010)
+#define PL011_LCR_H_WLEN UINT16_C(0x0060)
+#define PL011_LCR_H_WLEN_5BITS UINT16_C(0x0000)
+#define PL011_LCR_H_WLEN_6BITS UINT16_C(0x0020)
+#define PL011_LCR_H_WLEN_7BITS UINT16_C(0x0040)
+#define PL011_LCR_H_WLEN_8BITS UINT16_C(0x0060)
+#define PL011_LCR_H_SPS UINT16_C(0x0080)
+
+#define PL011_CR_UARTEN UINT16_C(0x0001)
+#define PL011_CR_SIREN UINT16_C(0x0002)
+#define PL011_CR_SIRLP UINT16_C(0x0004)
+#define PL011_CR_LBE UINT16_C(0x0080)
+#define PL011_CR_TXE UINT16_C(0x0100)
+#define PL011_CR_RXE UINT16_C(0x0200)
+#define PL011_CR_DTR UINT16_C(0x0400)
+#define PL011_CR_RTS UINT16_C(0x0800)
+#define PL011_CR_OUT1 UINT16_C(0x1000)
+#define PL011_CR_OUT2 UINT16_C(0x2000)
+#define PL011_CR_RTSEN UINT16_C(0x4000)
+#define PL011_CR_CTSEN UINT16_C(0x8000)
+
+#define PL011_IFLS_TXIFLSEL UINT16_C(0x0007)
+#define PL011_IFLS_RXIFLSEL UINT16_C(0x0038)
+
+#define PL011_IMSC_RIMIM UINT16_C(0x0001)
+#define PL011_IMSC_CTSMIM UINT16_C(0x0002)
+#define PL011_IMSC_DCDMIM UINT16_C(0x0004)
+#define PL011_IMSC_DSRMIM UINT16_C(0x0008)
+#define PL011_IMSC_RXIM UINT16_C(0x0010)
+#define PL011_IMSC_TXIM UINT16_C(0x0020)
+#define PL011_IMSC_RTIM UINT16_C(0x0040)
+#define PL011_IMSC_FEIM UINT16_C(0x0080)
+#define PL011_IMSC_PEIM UINT16_C(0x0100)
+#define PL011_IMSC_BEIM UINT16_C(0x0200)
+#define PL011_IMSC_OEIM UINT16_C(0x0400)
+
+#define PL011_RIS_RIRMIS UINT16_C(0x0001)
+#define PL011_RIS_CTSRMIS UINT16_C(0x0002)
+#define PL011_RIS_DCDRMIS UINT16_C(0x0004)
+#define PL011_RIS_DSRRMIS UINT16_C(0x0008)
+#define PL011_RIS_RXRIS UINT16_C(0x0010)
+#define PL011_RIS_TXRIS UINT16_C(0x0020)
+#define PL011_RIS_RTRIS UINT16_C(0x0040)
+#define PL011_RIS_FERIS UINT16_C(0x0080)
+#define PL011_RIS_PERIS UINT16_C(0x0100)
+#define PL011_RIS_BERIS UINT16_C(0x0200)
+#define PL011_RIS_OERIS UINT16_C(0x0400)
+
+#define PL011_MIS_RIMMIS UINT16_C(0x0001)
+#define PL011_MIS_CTSMMIS UINT16_C(0x0002)
+#define PL011_MIS_DCDMMIS UINT16_C(0x0004)
+#define PL011_MIS_DSRMMIS UINT16_C(0x0008)
+#define PL011_MIS_RXMIS UINT16_C(0x0010)
+#define PL011_MIS_TXMIS UINT16_C(0x0020)
+#define PL011_MIS_RTMIS UINT16_C(0x0040)
+#define PL011_MIS_FEMIS UINT16_C(0x0080)
+#define PL011_MIS_PEMIS UINT16_C(0x0100)
+#define PL011_MIS_BEMIS UINT16_C(0x0200)
+#define PL011_MIS_OEMIS UINT16_C(0x0400)
+
+#define PL011_ICR_RIMIC UINT16_C(0x0001)
+#define PL011_ICR_CTSMIC UINT16_C(0x0002)
+#define PL011_ICR_DCDMIC UINT16_C(0x0004)
+#define PL011_ICR_DSRMIC UINT16_C(0x0008)
+#define PL011_ICR_RXIC UINT16_C(0x0010)
+#define PL011_ICR_TXIC UINT16_C(0x0020)
+#define PL011_ICR_RTIC UINT16_C(0x0040)
+#define PL011_ICR_FEIC UINT16_C(0x0080)
+#define PL011_ICR_PEIC UINT16_C(0x0100)
+#define PL011_ICR_BEIC UINT16_C(0x0200)
+#define PL011_ICR_OEIC UINT16_C(0x0400)
+
+#define PL011_DMACR_RXDMAE UINT16_C(0x0001)
+#define PL011_DMACR_TXDMAE UINT16_C(0x0002)
+#define PL011_DMACR_DMAAONERR UINT16_C(0x0004)
+
+#define PL011_UARTCLK_MIN (1420 * FWK_KHZ)
+#define PL011_UARTCLK_MAX (542720 * FWK_KHZ)
+
+#endif /* PL011 */