| /* |
| * 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; |
| } |