diff options
author | Tushar Khandelwal <tushar.khandelwal@arm.com> | 2019-07-05 15:45:31 +0100 |
---|---|---|
committer | Tushar Khandelwal <tushar.khandelwal@arm.com> | 2019-09-04 11:37:10 +0100 |
commit | ad1b1bb3577cbaa50e70b02083dd8c14eee39157 (patch) | |
tree | 216a178c831388082fb00e20967e23ef7a9a83d2 | |
parent | 11d90fac77bc8a7770bce3a95b5f387176f924c5 (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/Makefile | 2 | ||||
-rw-r--r-- | product/corstone-700/firmware/config_log.c | 60 | ||||
-rw-r--r-- | product/corstone-700/firmware/firmware.mk | 19 | ||||
-rw-r--r-- | product/corstone-700/include/escm3_mmap.h | 2 | ||||
-rw-r--r-- | product/corstone-700/module/log/include/mod_log.h | 188 | ||||
-rw-r--r-- | product/corstone-700/module/log/src/Makefile | 10 | ||||
-rw-r--r-- | product/corstone-700/module/log/src/mod_log.c | 380 | ||||
-rw-r--r-- | product/corstone-700/module/pl011/include/mod_pl011.h | 55 | ||||
-rw-r--r-- | product/corstone-700/module/pl011/src/Makefile | 10 | ||||
-rw-r--r-- | product/corstone-700/module/pl011/src/mod_pl011.c | 216 | ||||
-rw-r--r-- | product/corstone-700/module/pl011/src/pl011.h | 160 |
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 */ |