diff options
Diffstat (limited to 'platform/common')
-rw-r--r-- | platform/common/fdt.c | 314 | ||||
-rw-r--r-- | platform/common/include/plat/fdt.h | 73 | ||||
-rw-r--r-- | platform/common/include/plat/irqchip/plic.h | 22 | ||||
-rw-r--r-- | platform/common/include/plat/serial/sifive-uart.h | 22 | ||||
-rw-r--r-- | platform/common/include/plat/serial/uart8250.h | 23 | ||||
-rw-r--r-- | platform/common/include/plat/sys/clint.h | 35 | ||||
-rw-r--r-- | platform/common/irqchip/objects.mk | 10 | ||||
-rw-r--r-- | platform/common/irqchip/plic.c | 118 | ||||
-rw-r--r-- | platform/common/objects.mk | 10 | ||||
-rw-r--r-- | platform/common/serial/objects.mk | 11 | ||||
-rw-r--r-- | platform/common/serial/sifive-uart.c | 97 | ||||
-rw-r--r-- | platform/common/serial/uart8250.c | 117 | ||||
-rw-r--r-- | platform/common/sys/clint.c | 131 | ||||
-rw-r--r-- | platform/common/sys/objects.mk | 10 |
14 files changed, 993 insertions, 0 deletions
diff --git a/platform/common/fdt.c b/platform/common/fdt.c new file mode 100644 index 0000000..4f6017c --- /dev/null +++ b/platform/common/fdt.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <plat/fdt.h> + +#define FDT_MAGIC 0xd00dfeed +#define FDT_VERSION 17 + +struct fdt_header { + u32 magic; + u32 totalsize; + u32 off_dt_struct; + u32 off_dt_strings; + u32 off_mem_rsvmap; + u32 version; + u32 last_comp_version; /* <= 17 */ + u32 boot_cpuid_phys; + u32 size_dt_strings; + u32 size_dt_struct; +} __attribute__((packed)); + +#define FDT_BEGIN_NODE 1 +#define FDT_END_NODE 2 +#define FDT_PROP 3 +#define FDT_NOP 4 +#define FDT_END 9 + +u32 fdt_rev32(u32 v) +{ + return ((v & 0x000000FF) << 24) | + ((v & 0x0000FF00) << 8) | + ((v & 0x00FF0000) >> 8) | + ((v & 0xFF000000) >> 24); +} + +ulong fdt_strlen(const char *str) +{ + ulong ret = 0; + + while (*str != '\0') { + ret++; + str++; + } + + return ret; +} + +int fdt_strcmp(const char *a, const char *b) +{ + /* search first diff or end of string */ + for (; *a == *b && *a != '\0'; a++, b++); + return *a - *b; +} + +int fdt_prop_string_index(const struct fdt_prop *prop, + const char *str) +{ + int i; + ulong l = 0; + const char *p, *end; + + p = prop->value; + end = p + prop->len; + + for (i = 0; p < end; i++, p += l) { + l = fdt_strlen(p) + 1; + if (p + l > end) + return -1; + if (fdt_strcmp(str, p) == 0) + return i; /* Found it; return index */ + } + + return -1; +} + +struct recursive_iter_info { + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv); + void *fn_priv; + const char *str; +}; + +#define DATA32(ptr) fdt_rev32(*((u32*)ptr)) + +static void recursive_iter(char **data, struct recursive_iter_info *info, + const struct fdt_node *parent) +{ + struct fdt_node node; + struct fdt_prop prop; + + if (DATA32(*data) != FDT_BEGIN_NODE) + return; + + node.data = *data; + + (*data) += sizeof(u32); + + node.parent = parent; + node.name = *data; + + *data += fdt_strlen(*data) + 1; + while ((ulong)(*data) % sizeof(u32) != 0) + (*data)++; + + node.depth = (parent) ? (parent->depth + 1) : 1; + + /* Default cell counts, as per the FDT spec */ + node.address_cells = 2; + node.size_cells = 1; + + info->fn(&node, NULL, info->fn_priv); + + while (DATA32(*data) != FDT_END_NODE) { + switch (DATA32(*data)) { + case FDT_PROP: + prop.node = &node; + *data += sizeof(u32); + prop.len = DATA32(*data); + *data += sizeof(u32); + prop.name = &info->str[DATA32(*data)]; + *data += sizeof(u32); + prop.value = *data; + *data += prop.len; + while ((ulong)(*data) % sizeof(u32) != 0) + (*data)++; + info->fn(&node, &prop, info->fn_priv); + break; + case FDT_NOP: + *data += sizeof(u32); + break; + case FDT_BEGIN_NODE: + recursive_iter(data, info, &node); + break; + default: + return; + }; + } + + *data += sizeof(u32); +} + +struct match_iter_info { + int (*match)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv); + void *match_priv; + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv); + void *fn_priv; + const char *str; +}; + +static void match_iter(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + char *data; + struct match_iter_info *minfo = priv; + struct fdt_prop nprop; + + /* Do nothing if node+prop dont match */ + if (!minfo->match(node, prop, minfo->match_priv)) + return; + + /* Call function for node */ + if (minfo->fn) + minfo->fn(node, NULL, minfo->fn_priv); + + /* Convert node to character stream */ + data = node->data; + data += sizeof(u32); + + /* Skip node name */ + data += fdt_strlen(data) + 1; + while ((ulong)(data) % sizeof(u32) != 0) + data++; + + /* Find node property and its value */ + while (DATA32(data) == FDT_PROP) { + nprop.node = node; + data += sizeof(u32); + nprop.len = DATA32(data); + data += sizeof(u32); + nprop.name = &minfo->str[DATA32(data)]; + data += sizeof(u32); + nprop.value = data; + data += nprop.len; + while ((ulong)(data) % sizeof(u32) != 0) + (data)++; + /* Call function for every property */ + if (minfo->fn) + minfo->fn(node, &nprop, minfo->fn_priv); + } +} + +int fdt_match_node_prop(void *fdt, + int (*match)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *match_priv, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv) +{ + char *data; + u32 string_offset, data_offset; + struct fdt_header *header; + struct match_iter_info minfo; + struct recursive_iter_info rinfo; + + if (!fdt || !match) + return -1; + + header = fdt; + if (fdt_rev32(header->magic) != FDT_MAGIC || + fdt_rev32(header->last_comp_version) > FDT_VERSION) + return -1; + string_offset = fdt_rev32(header->off_dt_strings); + data_offset = fdt_rev32(header->off_dt_struct); + + minfo.match = match; + minfo.match_priv = match_priv; + minfo.fn = fn; + minfo.fn_priv = fn_priv; + minfo.str = (const char *)(fdt + string_offset); + + rinfo.fn = match_iter; + rinfo.fn_priv = &minfo; + rinfo.str = minfo.str; + + data = (char *)(fdt + data_offset); + recursive_iter(&data, &rinfo, NULL); + + return 0; +} + +struct match_compat_info { + const char *compat; +}; + +static int match_compat(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + struct match_compat_info *cinfo = priv; + + if (!prop) + return 0; + + if (fdt_strcmp(prop->name, "compatible")) + return 0; + + if (fdt_prop_string_index(prop, cinfo->compat) < 0) + return 0; + + return 1; +} + +int fdt_compat_node_prop(void *fdt, + const char *compat, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv) +{ + struct match_compat_info cinfo = { .compat = compat }; + + return fdt_match_node_prop(fdt, match_compat, &cinfo, + fn, fn_priv); +} + +static int match_walk(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + if (!prop) + return 1; + + return 0; +} + +int fdt_walk(void *fdt, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv) +{ + return fdt_match_node_prop(fdt, match_walk, NULL, + fn, fn_priv); +} + +u32 fdt_size(void *fdt) +{ + struct fdt_header *header; + + if (!fdt) + return 0; + + header = fdt; + if (fdt_rev32(header->magic) != FDT_MAGIC || + fdt_rev32(header->last_comp_version) > FDT_VERSION) + return 0; + + return fdt_rev32(header->totalsize); +} diff --git a/platform/common/include/plat/fdt.h b/platform/common/include/plat/fdt.h new file mode 100644 index 0000000..246243f --- /dev/null +++ b/platform/common/include/plat/fdt.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __FDT_H__ +#define __FDT_H__ + +#include <sbi/sbi_types.h> + +struct fdt_node { + char *data; + const struct fdt_node *parent; + const char *name; + int depth; + int address_cells; + int size_cells; +}; + +struct fdt_prop { + const struct fdt_node *node; + const char *name; + void *value; + u32 len; +}; + +/* Reverse byte-order of 32bit number */ +u32 fdt_rev32(u32 v); + +/* Length of a string */ +ulong fdt_strlen(const char *str); + +/* Compate two strings */ +int fdt_strcmp(const char *a, const char *b); + +/* Find index of matching string from a list of strings */ +int fdt_prop_string_index(const struct fdt_prop *prop, + const char *str); + +/* Iterate over each property of matching node */ +int fdt_match_node_prop(void *fdt, + int (*match)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *match_priv, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv); + +/* Iterate over each property of compatible node */ +int fdt_compat_node_prop(void *fdt, + const char *compat, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv); + +/* Iterate over each node and property */ +int fdt_walk(void *fdt, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv); + +/* Get size of FDT */ +u32 fdt_size(void *fdt); + +#endif diff --git a/platform/common/include/plat/irqchip/plic.h b/platform/common/include/plat/irqchip/plic.h new file mode 100644 index 0000000..7c062aa --- /dev/null +++ b/platform/common/include/plat/irqchip/plic.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __IRQCHIP_PLIC_H__ +#define __IRQCHIP_PLIC_H__ + +#include <sbi/sbi_types.h> + +int plic_fdt_fixup(void *fdt, const char *compat); + +int plic_warm_irqchip_init(u32 target_hart); + +int plic_cold_irqchip_init(unsigned long base, + u32 num_sources, u32 hart_count); + +#endif diff --git a/platform/common/include/plat/serial/sifive-uart.h b/platform/common/include/plat/serial/sifive-uart.h new file mode 100644 index 0000000..b932b16 --- /dev/null +++ b/platform/common/include/plat/serial/sifive-uart.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SERIAL_SIFIVE_UART_H__ +#define __SERIAL_SIFIVE_UART_H__ + +#include <sbi/sbi_types.h> + +void sifive_uart_putc(char ch); + +char sifive_uart_getc(void); + +int sifive_uart_init(unsigned long base, + u32 in_freq, u32 baudrate); + +#endif diff --git a/platform/common/include/plat/serial/uart8250.h b/platform/common/include/plat/serial/uart8250.h new file mode 100644 index 0000000..ca19a9f --- /dev/null +++ b/platform/common/include/plat/serial/uart8250.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SERIAL_UART8250_H__ +#define __SERIAL_UART8250_H__ + +#include <sbi/sbi_types.h> + +void uart8250_putc(char ch); + +char uart8250_getc(void); + +int uart8250_init(unsigned long base, + u32 in_freq, u32 baudrate, + u32 reg_shift, u32 reg_width); + +#endif diff --git a/platform/common/include/plat/sys/clint.h b/platform/common/include/plat/sys/clint.h new file mode 100644 index 0000000..642d83a --- /dev/null +++ b/platform/common/include/plat/sys/clint.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SYS_CLINT_H__ +#define __SYS_CLINT_H__ + +#include <sbi/sbi_types.h> + +void clint_ipi_inject(u32 target_hart, u32 source_hart); + +void clint_ipi_sync(u32 target_hart, u32 source_hart); + +void clint_ipi_clear(u32 target_hart); + +int clint_warm_ipi_init(u32 target_hart); + +int clint_cold_ipi_init(unsigned long base, u32 hart_count); + +u64 clint_timer_value(void); + +void clint_timer_event_stop(u32 target_hart); + +void clint_timer_event_start(u32 target_hart, u64 next_event); + +int clint_warm_timer_init(u32 target_hart); + +int clint_cold_timer_init(unsigned long base, u32 hart_count); + +#endif diff --git a/platform/common/irqchip/objects.mk b/platform/common/irqchip/objects.mk new file mode 100644 index 0000000..84fffa6 --- /dev/null +++ b/platform/common/irqchip/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +platform-common-objs-$(PLATFORM_IRQCHIP_PLIC) += irqchip/plic.o diff --git a/platform/common/irqchip/plic.c b/platform/common/irqchip/plic.c new file mode 100644 index 0000000..404d1b7 --- /dev/null +++ b/platform/common/irqchip/plic.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sbi/riscv_io.h> +#include <plat/fdt.h> +#include <plat/irqchip/plic.h> + +#define PLIC_PRIORITY_BASE 0x0 +#define PLIC_PENDING_BASE 0x1000 +#define PLIC_ENABLE_BASE 0x2000 +#define PLIC_ENABLE_STRIDE 0x80 +#define PLIC_CONTEXT_BASE 0x200000 +#define PLIC_CONTEXT_STRIDE 0x1000 + +static u32 plic_hart_count; +static u32 plic_num_sources; +static volatile void *plic_base; + +static void plic_set_priority(u32 source, u32 val) +{ + writel(val, plic_base); +} + +static void plic_set_m_thresh(u32 hartid, u32 val) +{ + volatile void *plic_m_thresh = plic_base + + PLIC_CONTEXT_BASE + + PLIC_CONTEXT_STRIDE * (2 * hartid); + writel(val, plic_m_thresh); +} + +static void plic_set_s_thresh(u32 hartid, u32 val) +{ + volatile void *plic_s_thresh = plic_base + + PLIC_CONTEXT_BASE + + PLIC_CONTEXT_STRIDE * (2 * hartid + 1); + writel(val, plic_s_thresh); +} + +static void plic_set_s_ie(u32 hartid, u32 word_index, u32 val) +{ + volatile void *plic_s_ie = plic_base + + PLIC_ENABLE_BASE + + PLIC_ENABLE_STRIDE * (2 * hartid + 1); + writel(val, plic_s_ie + word_index * 4); +} + +static void plic_fdt_fixup_prop(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + u32 *cells; + u32 i, cells_count; + + if (!prop) + return; + if (fdt_strcmp(prop->name, "interrupts-extended")) + return; + + cells = prop->value; + cells_count = prop->len / sizeof(u32); + + if (!cells_count) + return; + + for (i = 0; i < cells_count; i++) { + if (i % 4 == 1) + cells[i] = fdt_rev32(0xffffffff); + } +} + +int plic_fdt_fixup(void *fdt, const char *compat) +{ + fdt_compat_node_prop(fdt, compat, plic_fdt_fixup_prop, NULL); + return 0; +} + +int plic_warm_irqchip_init(u32 target_hart) +{ + size_t i, ie_words = plic_num_sources / 32 + 1; + + if (plic_hart_count <= target_hart) + return -1; + + /* By default, enable all IRQs for S-mode of target HART */ + for (i = 0; i < ie_words; i++) + plic_set_s_ie(target_hart, i, -1); + + /* By default, enable M-mode threshold */ + plic_set_m_thresh(target_hart, 1); + + /* By default, disable S-mode threshold */ + plic_set_s_thresh(target_hart, 0); + + return 0; +} + +int plic_cold_irqchip_init(unsigned long base, + u32 num_sources, u32 hart_count) +{ + int i; + + plic_hart_count = hart_count; + plic_num_sources = num_sources; + plic_base = (void *)base; + + /* Configure default priorities of all IRQs */ + for (i = 0; i < plic_num_sources; i++) + plic_set_priority(i, 1); + + return 0; +} diff --git a/platform/common/objects.mk b/platform/common/objects.mk new file mode 100644 index 0000000..712e950 --- /dev/null +++ b/platform/common/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +platform-common-objs-y += fdt.o diff --git a/platform/common/serial/objects.mk b/platform/common/serial/objects.mk new file mode 100644 index 0000000..cb6062d --- /dev/null +++ b/platform/common/serial/objects.mk @@ -0,0 +1,11 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +platform-common-objs-$(PLATFORM_SERIAL_UART8250) += serial/uart8250.o +platform-common-objs-$(PLATFORM_SERIAL_SIFIVE_UART) += serial/sifive-uart.o diff --git a/platform/common/serial/sifive-uart.c b/platform/common/serial/sifive-uart.c new file mode 100644 index 0000000..60eade3 --- /dev/null +++ b/platform/common/serial/sifive-uart.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sbi/riscv_io.h> +#include <sbi/sbi_console.h> +#include <plat/serial/sifive-uart.h> + +#define UART_REG_TXFIFO 0 +#define UART_REG_RXFIFO 1 +#define UART_REG_TXCTRL 2 +#define UART_REG_RXCTRL 3 +#define UART_REG_IE 4 +#define UART_REG_IP 5 +#define UART_REG_DIV 6 + +#define UART_TXFIFO_FULL 0x80000000 +#define UART_RXFIFO_EMPTY 0x80000000 +#define UART_RXFIFO_DATA 0x000000ff +#define UART_TXCTRL_TXEN 0x1 +#define UART_RXCTRL_RXEN 0x1 + +static volatile void *uart_base; +static u32 uart_in_freq; +static u32 uart_baudrate; + +/** + * Find minimum divisor divides in_freq to max_target_hz; + * Based on uart driver n SiFive FSBL. + * + * f_baud = f_in / (div + 1) => div = (f_in / f_baud) - 1 + * The nearest integer solution requires rounding up as to not exceed max_target_hz. + * div = ceil(f_in / f_baud) - 1 + * = floor((f_in - 1 + f_baud) / f_baud) - 1 + * This should not overflow as long as (f_in - 1 + f_baud) does not exceed + * 2^32 - 1, which is unlikely since we represent frequencies in kHz. + */ +static inline unsigned int uart_min_clk_divisor(uint64_t in_freq, + uint64_t max_target_hz) +{ + uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz); + // Avoid underflow + if (quotient == 0) { + return 0; + } else { + return quotient - 1; + } +} + +static u32 get_reg(u32 num) +{ + return readl(uart_base + (num * 0x4)); +} + +static void set_reg(u32 num, u32 val) +{ + writel(val, uart_base + (num * 0x4)); +} + +void sifive_uart_putc(char ch) +{ + while (get_reg(UART_REG_TXFIFO) & UART_TXFIFO_FULL); + + set_reg(UART_REG_TXFIFO, ch); +} + +char sifive_uart_getc(void) +{ + u32 ret = get_reg(UART_REG_RXFIFO); + if (!(ret & UART_RXFIFO_EMPTY)) + return ret & UART_RXFIFO_DATA; + return 0; +} + +int sifive_uart_init(unsigned long base, + u32 in_freq, u32 baudrate) +{ + uart_base = (volatile void *)base; + uart_in_freq = in_freq; + uart_baudrate = baudrate; + + /* Configure baudrate */ + set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate)); + /* Disable interrupts */ + set_reg(UART_REG_IE, 0); + /* Enable TX */ + set_reg(UART_REG_TXCTRL, UART_TXCTRL_TXEN); + /* Enable Rx */ + set_reg(UART_REG_RXCTRL, UART_RXCTRL_RXEN); + + return 0; +} diff --git a/platform/common/serial/uart8250.c b/platform/common/serial/uart8250.c new file mode 100644 index 0000000..7a99045 --- /dev/null +++ b/platform/common/serial/uart8250.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sbi/riscv_io.h> +#include <plat/serial/uart8250.h> + +#define UART_RBR_OFFSET 0 /* In: Recieve Buffer Register */ +#define UART_THR_OFFSET 0 /* Out: Transmitter Holding Register */ +#define UART_DLL_OFFSET 0 /* Out: Divisor Latch Low */ +#define UART_IER_OFFSET 1 /* I/O: Interrupt Enable Register */ +#define UART_DLM_OFFSET 1 /* Out: Divisor Latch High */ +#define UART_FCR_OFFSET 2 /* Out: FIFO Control Register */ +#define UART_IIR_OFFSET 2 /* I/O: Interrupt Identification Register */ +#define UART_LCR_OFFSET 3 /* Out: Line Control Register */ +#define UART_MCR_OFFSET 4 /* Out: Modem Control Register */ +#define UART_LSR_OFFSET 5 /* In: Line Status Register */ +#define UART_MSR_OFFSET 6 /* In: Modem Status Register */ +#define UART_SCR_OFFSET 7 /* I/O: Scratch Register */ +#define UART_MDR1_OFFSET 8 /* I/O: Mode Register */ + +#define UART_LSR_FIFOE 0x80 /* Fifo error */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */ + +static volatile void *uart8250_base; +static u32 uart8250_in_freq; +static u32 uart8250_baudrate; +static u32 uart8250_reg_width; +static u32 uart8250_reg_shift; + +static u32 get_reg(u32 num) +{ + u32 offset = num << uart8250_reg_shift; + + if (uart8250_reg_width == 1) + return readb(uart8250_base + offset); + else if (uart8250_reg_width == 2) + return readw(uart8250_base + offset); + else + return readl(uart8250_base + offset); +} + +static void set_reg(u32 num, u32 val) +{ + u32 offset = num << uart8250_reg_shift; + + if (uart8250_reg_width == 1) + writeb(val, uart8250_base + offset); + else if (uart8250_reg_width == 2) + writew(val, uart8250_base + offset); + else + writel(val, uart8250_base + offset); +} + +void uart8250_putc(char ch) +{ + while ((get_reg(UART_LSR_OFFSET) & UART_LSR_THRE) == 0); + + set_reg(UART_THR_OFFSET, ch); +} + +char uart8250_getc(void) +{ + if (get_reg(UART_LSR_OFFSET) & UART_LSR_DR) + return get_reg(UART_RBR_OFFSET); + return 0; +} + +int uart8250_init(unsigned long base, + u32 in_freq, u32 baudrate, + u32 reg_shift, u32 reg_width) +{ + u16 bdiv; + + uart8250_base = (volatile void *)base; + uart8250_reg_shift = reg_shift; + uart8250_reg_width = reg_width; + uart8250_in_freq = in_freq; + uart8250_baudrate = baudrate; + + bdiv = uart8250_in_freq / (16 * uart8250_baudrate); + + /* Disable all interrupts */ + set_reg(UART_IER_OFFSET, 0x00); + /* Enable DLAB */ + set_reg(UART_LCR_OFFSET, 0x80); + /* Set divisor low byte */ + set_reg(UART_DLL_OFFSET, bdiv & 0xff); + /* Set divisor high byte */ + set_reg(UART_DLM_OFFSET, (bdiv >> 8) & 0xff); + /* 8 bits, no parity, one stop bit */ + set_reg(UART_LCR_OFFSET, 0x03); + /* Enable FIFO */ + set_reg(UART_FCR_OFFSET, 0x01); + /* No modem control DTR RTS */ + set_reg(UART_MCR_OFFSET, 0x00); + /* Clear line status */ + get_reg(UART_LSR_OFFSET); + /* Read receive buffer */ + get_reg(UART_RBR_OFFSET); + /* Set scratchpad */ + set_reg(UART_SCR_OFFSET, 0x00); + + return 0; +} diff --git a/platform/common/sys/clint.c b/platform/common/sys/clint.c new file mode 100644 index 0000000..e84c382 --- /dev/null +++ b/platform/common/sys/clint.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sbi/riscv_io.h> +#include <sbi/riscv_atomic.h> +#include <plat/sys/clint.h> + +static u32 clint_ipi_hart_count; +static volatile void *clint_ipi_base; +static volatile u32 *clint_ipi; + +void clint_ipi_inject(u32 target_hart, u32 source_hart) +{ + if ((clint_ipi_hart_count <= target_hart) || + (clint_ipi_hart_count <= source_hart)) + return; + + /* Set CLINT IPI */ + writel(1, &clint_ipi[target_hart]); +} + +void clint_ipi_sync(u32 target_hart, u32 source_hart) +{ + u32 target_ipi, incoming_ipi; + + if ((clint_ipi_hart_count <= target_hart) || + (clint_ipi_hart_count <= source_hart)) + return; + + /* Wait until target HART has handled IPI */ + incoming_ipi = 0; + while (1) { + target_ipi = readl(&clint_ipi[target_hart]); + if (!target_ipi) + break; + + incoming_ipi |= + atomic_raw_xchg_uint(&clint_ipi[source_hart], 0); + } + + if (incoming_ipi) + writel(incoming_ipi, &clint_ipi[source_hart]); +} + +void clint_ipi_clear(u32 target_hart) +{ + if (clint_ipi_hart_count <= target_hart) + return; + + /* Clear CLINT IPI */ + writel(0, &clint_ipi[target_hart]); +} + +int clint_warm_ipi_init(u32 target_hart) +{ + if (clint_ipi_hart_count <= target_hart || + !clint_ipi_base) + return -1; + + /* Clear CLINT IPI */ + clint_ipi_clear(target_hart); + + return 0; +} + +int clint_cold_ipi_init(unsigned long base, u32 hart_count) +{ + /* Figure-out CLINT IPI register address */ + clint_ipi_hart_count = hart_count; + clint_ipi_base = (void *)base; + clint_ipi = (u32 *)clint_ipi_base; + + return 0; +} + +static u32 clint_time_hart_count; +static volatile void *clint_time_base; +static volatile u64 *clint_time_val; +static volatile u64 *clint_time_cmp; + +u64 clint_timer_value(void) +{ + return readq_relaxed(clint_time_val); +} + +void clint_timer_event_stop(u32 target_hart) +{ + if (clint_time_hart_count <= target_hart) + return; + + /* Clear CLINT Time Compare */ + writeq_relaxed(-1ULL, &clint_time_cmp[target_hart]); +} + +void clint_timer_event_start(u32 target_hart, u64 next_event) +{ + if (clint_time_hart_count <= target_hart) + return; + + /* Program CLINT Time Compare */ + writeq_relaxed(next_event, &clint_time_cmp[target_hart]); +} + +int clint_warm_timer_init(u32 target_hart) +{ + if (clint_time_hart_count <= target_hart || + !clint_time_base) + return -1; + + /* Clear CLINT Time Compare */ + writeq_relaxed(-1ULL, &clint_time_cmp[target_hart]); + + return 0; +} + +int clint_cold_timer_init(unsigned long base, u32 hart_count) +{ + /* Figure-out CLINT Time register address */ + clint_time_hart_count = hart_count; + clint_time_base = (void *)base; + clint_time_val = (u64 *)(clint_time_base + 0xbff8); + clint_time_cmp = (u64 *)(clint_time_base + 0x4000); + + return 0; +} diff --git a/platform/common/sys/objects.mk b/platform/common/sys/objects.mk new file mode 100644 index 0000000..183d8db --- /dev/null +++ b/platform/common/sys/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +platform-common-objs-$(PLATFORM_SYS_CLINT) += sys/clint.o |