forked from Mirrors/opensbi

Now that driver lifecycle is managed from within the SBI timer core, platforms need only to initialize the driver once during cold init. Remove the remaining platform hooks that are no longer used. Signed-off-by: Samuel Holland <samuel.holland@sifive.com> Reviewed-by: Anup Patel <anup@brainfault.org>
169 lines
4.4 KiB
C
169 lines
4.4 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
|
*
|
|
* Authors:
|
|
* Anup Patel <anup.patel@wdc.com>
|
|
*/
|
|
|
|
#include <libfdt.h>
|
|
#include <sbi/sbi_error.h>
|
|
#include <sbi/sbi_heap.h>
|
|
#include <sbi/sbi_list.h>
|
|
#include <sbi_utils/fdt/fdt_helper.h>
|
|
#include <sbi_utils/timer/fdt_timer.h>
|
|
#include <sbi_utils/timer/aclint_mtimer.h>
|
|
|
|
struct timer_mtimer_quirks {
|
|
bool is_clint;
|
|
unsigned int clint_mtime_offset;
|
|
bool clint_without_mtime;
|
|
bool has_64bit_mmio;
|
|
};
|
|
|
|
struct timer_mtimer_node {
|
|
struct sbi_dlist head;
|
|
struct aclint_mtimer_data data;
|
|
};
|
|
static SBI_LIST_HEAD(mtn_list);
|
|
|
|
static struct aclint_mtimer_data *mt_reference = NULL;
|
|
|
|
static int timer_mtimer_cold_init(const void *fdt, int nodeoff,
|
|
const struct fdt_match *match)
|
|
{
|
|
int rc;
|
|
unsigned long addr[2], size[2];
|
|
struct timer_mtimer_node *mtn, *n;
|
|
struct aclint_mtimer_data *mt;
|
|
const struct timer_mtimer_quirks *quirks = match->data;
|
|
bool is_clint = quirks && quirks->is_clint;
|
|
|
|
mtn = sbi_zalloc(sizeof(*mtn));
|
|
if (!mtn)
|
|
return SBI_ENOMEM;
|
|
mt = &mtn->data;
|
|
|
|
rc = fdt_parse_aclint_node(fdt, nodeoff, true, !is_clint,
|
|
&addr[0], &size[0], &addr[1], &size[1],
|
|
&mt->first_hartid, &mt->hart_count);
|
|
if (rc) {
|
|
sbi_free(mtn);
|
|
return rc;
|
|
}
|
|
mt->has_64bit_mmio = true;
|
|
mt->has_shared_mtime = false;
|
|
|
|
rc = fdt_parse_timebase_frequency(fdt, &mt->mtime_freq);
|
|
if (rc) {
|
|
sbi_free(mtn);
|
|
return rc;
|
|
}
|
|
|
|
if (is_clint) { /* SiFive CLINT */
|
|
/* Set CLINT addresses */
|
|
mt->mtimecmp_addr = addr[0] + ACLINT_DEFAULT_MTIMECMP_OFFSET;
|
|
mt->mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE;
|
|
if (!quirks->clint_without_mtime) {
|
|
mt->mtime_addr = addr[0] + ACLINT_DEFAULT_MTIME_OFFSET;
|
|
mt->mtime_size = size[0] - mt->mtimecmp_size;
|
|
/* Adjust MTIMER address and size for CLINT device */
|
|
mt->mtime_addr += quirks->clint_mtime_offset;
|
|
mt->mtime_size -= quirks->clint_mtime_offset;
|
|
} else {
|
|
mt->mtime_addr = mt->mtime_size = 0;
|
|
}
|
|
mt->mtimecmp_addr += quirks->clint_mtime_offset;
|
|
} else { /* RISC-V ACLINT MTIMER */
|
|
/* Set ACLINT MTIMER addresses */
|
|
mt->mtime_addr = addr[0];
|
|
mt->mtime_size = size[0];
|
|
mt->mtimecmp_addr = addr[1];
|
|
mt->mtimecmp_size = size[1];
|
|
}
|
|
|
|
/* Apply additional quirks */
|
|
if (quirks) {
|
|
mt->has_64bit_mmio = quirks->has_64bit_mmio;
|
|
}
|
|
|
|
/* Check if MTIMER device has shared MTIME address */
|
|
if (mt->mtime_size) {
|
|
mt->has_shared_mtime = false;
|
|
sbi_list_for_each_entry(n, &mtn_list, head) {
|
|
if (n->data.mtime_addr == mt->mtime_addr) {
|
|
mt->has_shared_mtime = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
/* Assume shared time CSR */
|
|
mt->has_shared_mtime = true;
|
|
}
|
|
|
|
/* Initialize the MTIMER device */
|
|
rc = aclint_mtimer_cold_init(mt, mt_reference);
|
|
if (rc) {
|
|
sbi_free(mtn);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Select first MTIMER device with no associated HARTs as our
|
|
* reference MTIMER device. This is only a temporary strategy
|
|
* of selecting reference MTIMER device. In future, we might
|
|
* define an optional DT property or some other mechanism to
|
|
* help us select the reference MTIMER device.
|
|
*/
|
|
if (!mt->hart_count && !mt_reference) {
|
|
mt_reference = mt;
|
|
/*
|
|
* Set reference for already propbed MTIMER devices
|
|
* with non-shared MTIME
|
|
*/
|
|
sbi_list_for_each_entry(n, &mtn_list, head) {
|
|
if (!n->data.has_shared_mtime)
|
|
aclint_mtimer_set_reference(&n->data, mt);
|
|
}
|
|
}
|
|
|
|
/* Explicitly sync-up MTIMER devices not associated with any HARTs */
|
|
if (!mt->hart_count)
|
|
aclint_mtimer_sync(mt);
|
|
|
|
sbi_list_add_tail(&mtn->head, &mtn_list);
|
|
return 0;
|
|
}
|
|
|
|
static const struct timer_mtimer_quirks sifive_clint_quirks = {
|
|
.is_clint = true,
|
|
.clint_mtime_offset = CLINT_MTIMER_OFFSET,
|
|
.has_64bit_mmio = true,
|
|
};
|
|
|
|
static const struct timer_mtimer_quirks thead_clint_quirks = {
|
|
.is_clint = true,
|
|
.clint_mtime_offset = CLINT_MTIMER_OFFSET,
|
|
.clint_without_mtime = true,
|
|
};
|
|
|
|
static const struct timer_mtimer_quirks thead_aclint_quirks = {
|
|
.has_64bit_mmio = false,
|
|
};
|
|
|
|
static const struct fdt_match timer_mtimer_match[] = {
|
|
{ .compatible = "riscv,clint0", .data = &sifive_clint_quirks },
|
|
{ .compatible = "sifive,clint0", .data = &sifive_clint_quirks },
|
|
{ .compatible = "thead,c900-clint", .data = &thead_clint_quirks },
|
|
{ .compatible = "thead,c900-aclint-mtimer",
|
|
.data = &thead_aclint_quirks },
|
|
{ .compatible = "riscv,aclint-mtimer" },
|
|
{ },
|
|
};
|
|
|
|
struct fdt_timer fdt_timer_mtimer = {
|
|
.match_table = timer_mtimer_match,
|
|
.cold_init = timer_mtimer_cold_init,
|
|
};
|