aboutsummaryrefslogtreecommitdiff
path: root/product/corstone-700/module/pl011/src
diff options
context:
space:
mode:
Diffstat (limited to 'product/corstone-700/module/pl011/src')
-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
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 */