/* * Copyright 2019, Data61 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) * ABN 41 687 119 230. * * This software may be distributed and modified according to the terms of * the BSD 2-Clause license. Note that NO WARRANTY is provided. * See "LICENSE_BSD2.txt" for details. * * @TAG(DATA61_BSD) */ #include #include #include #include #include #include #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; }