blob: 46a743f84e3feecdd96d61d9fd29fe509f999338 [file] [log] [blame]
/*
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <camkes/io.h>
#include <platsupport/clock.h>
#include <utils/util.h>
#include "plat.h"
typedef struct clock_entry {
bool initialised;
seL4_Word owner;
clk_t *clk;
} clock_entry_t;
static ps_io_ops_t *ops;
static clock_entry_t *clock_table;
/* Prototypes for these functions are not generated by the camkes templates yet */
seL4_Word the_clock_get_sender_id(void);
static inline bool check_clk_initialised(clk_id_t clk_id)
{
return clock_table[clk_id].initialised;
}
static inline bool check_valid_clk_id(clk_id_t clk_id)
{
return (clk_id >= 0 && clk_id < NCLOCKS);
}
static inline bool check_valid_clock_gate(clock_gate_t clock_gate)
{
return (clock_gate >= 0 && clock_gate <= NCLKGATES);
}
static inline bool check_is_owner(clk_id_t clk_id, seL4_Word client_id)
{
return (clock_table[clk_id].owner == client_id);
}
static inline seL4_Word get_client_id(void)
{
return the_clock_get_sender_id();
}
int the_clock_init_clock(clk_id_t clk_id)
{
int error = 0;
if (!check_valid_clk_id(clk_id)) {
ZF_LOGE("Invalid clock ID");
error = -EINVAL;
goto out;
}
seL4_Word client_id = get_client_id();
if (check_clk_initialised(clk_id)) {
if (!check_is_owner(clk_id, client_id)) {
ZF_LOGE("Clock is already initialised by another owner");
error = -EBUSY;
goto out;
} else {
error = 0;
goto out;
}
}
clock_table[clk_id].clk = clk_get_clock(&ops->clock_sys, clk_id);
if (!clock_table[clk_id].clk) {
ZF_LOGE("Failed to get clock for clock ID %d", clk_id);
error = -ENODEV;
goto out;
}
clock_table[clk_id].initialised = true;
clock_table[clk_id].owner = client_id;
out:
return error;
}
int the_clock_set_gate_mode(clock_gate_t gate, clock_gate_mode_t mode)
{
/* TODO Some basic form of access control to the gates?,
* Don't want a component to turn off the clock underneath another component */
int error = clk_gate_enable(&ops->clock_sys, gate, mode);
return error;
}
freq_t the_clock_get_freq(clk_id_t clk_id)
{
freq_t freq = 0;
if (!check_valid_clk_id(clk_id)) {
ZF_LOGE("Invalid clock ID");
freq = 0;
goto out;
}
seL4_Word client_id = get_client_id();
if (!check_clk_initialised(clk_id)) {
ZF_LOGE("Clock isn't initialised");
freq = 0;
goto out;
}
if (!check_is_owner(clk_id, client_id)) {
ZF_LOGE("Client is not the owner of this clock");
freq = 0;
goto out;
}
freq = clk_get_freq(clock_table[clk_id].clk);
out:
return freq;
}
freq_t the_clock_set_freq(clk_id_t clk_id, freq_t hz)
{
freq_t set_freq = 0;
if (!check_valid_clk_id(clk_id)) {
ZF_LOGE("Invalid clock ID");
set_freq = 0;
goto out;
}
seL4_Word client_id = get_client_id();
if (!check_clk_initialised(clk_id)) {
ZF_LOGE("Clock isn't initialised");
set_freq = 0;
goto out;
}
if (!check_is_owner(clk_id, client_id)) {
ZF_LOGE("Client is not the owner of this clock");
set_freq = 0;
goto out;
}
set_freq = clk_set_freq(clock_table[clk_id].clk, hz);
out:
return set_freq;
}
int the_clock_register_child(clk_id_t parent, clk_id_t child)
{
int error = 0;
if (!check_valid_clk_id(parent) || !check_valid_clk_id(child)) {
error = -EINVAL;
goto out;
}
seL4_Word client_id = get_client_id();
if (!check_clk_initialised(parent)) {
ZF_LOGE("Parent clock isn't initialised");
error = -EPERM;
goto out;
}
if (!check_is_owner(parent, client_id)) {
ZF_LOGE("Client is not the owner of the parent clock");
error = -EPERM;
goto out;
}
if (!check_clk_initialised(child)) {
ZF_LOGE("Child clock isn't initialised");
error = -EPERM;
goto out;
}
if (!check_is_owner(child, client_id)) {
ZF_LOGE("Client is not the owner of the child clock");
error = -EPERM;
goto out;
}
clk_register_child(clock_table[parent].clk, clock_table[child].clk);
error = 0;
out:
return error;
}
int ClockServer_init(ps_io_ops_t *io_ops)
{
ops = io_ops;
int error;
if (plat_init) {
/* Perform some platform specific initialisation before initialising
* the clock subsystem */
error = plat_init(ops);
ZF_LOGF_IF(error, "Failed to perform the platform initialisation");
}
error = clock_sys_init(ops, &ops->clock_sys);
ZF_LOGF_IF(error, "Failed to initialise the clock subsystem");
error = ps_calloc(&ops->malloc_ops, 1, sizeof(*clock_table) * NCLOCKS, (void **) &clock_table);
ZF_LOGF_IF(error, "Failed to allocate memory for the clock table");
return 0;
}