diff options
Diffstat (limited to 'product/corstone-700/module/pl011/src')
-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 |
3 files changed, 386 insertions, 0 deletions
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 */ |