blob: 7313875f81e0ecc7506d663270a6f43310f5e272 [file] [log] [blame]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "clock.h"
#include <string.h>
#include <assert.h>
#include "../../services.h"
/* Fixed clocks */
static freq_t _fixed_clk_get_freq(clk_t *clk)
{
return clk->req_freq;
}
static freq_t _fixed_clk_set_freq(clk_t *clk, freq_t hz UNUSED)
{
return clk_get_freq(clk);
}
void _fixed_clk_recal(clk_t *clk UNUSED)
{
assert(0);
}
clk_t *_fixed_clk_init(clk_t *clk)
{
return clk;
}
/* Default clocks. Simply report the recorded default frequency */
freq_t _default_clk_get_freq(clk_t *clk)
{
assert(clk->id < NCLOCKS);
return ps_freq_default[clk->id];
}
freq_t _default_clk_set_freq(clk_t *clk, freq_t hz UNUSED)
{
return clk_get_freq(clk);
}
void _default_clk_recal(clk_t *clk UNUSED)
{
assert(0);
}
clk_t *_default_clk_init(clk_t *clk)
{
return clk;
}
static clk_t *get_clock_default(clock_sys_t *clock_sys UNUSED, enum clk_id id)
{
if (id >= NCLOCKS) {
return NULL;
} else {
clk_t *clk;
clk = ps_clocks[id];
/* Destroy original clock links */
clk->clk_sys = clock_sys;
clk->init = _default_clk_init;
clk->get_freq = _default_clk_get_freq;
clk->set_freq = _default_clk_set_freq;
clk->recal = _default_clk_recal;
return clk;
}
}
static int gate_enable_default(clock_sys_t *clock_sys UNUSED, enum clock_gate gate UNUSED,
enum clock_gate_mode mode UNUSED)
{
/* Assume the gate is already enabled */
return 0;
}
int clock_sys_init_default(clock_sys_t *clock_sys)
{
clock_sys->get_clock = &get_clock_default;
clock_sys->gate_enable = &gate_enable_default;
/* Clock subsystem is invalid if there is not private data attached */
clock_sys->priv = (void *)0xdeadbeef;
return 0;
}
int clock_sys_set_default_freq(enum clk_id id, freq_t hz)
{
if (id >= NCLOCKS) {
return -1;
} else {
ps_freq_default[id] = hz;
return 0;
}
}
clk_t *ps_get_clock(clock_sys_t *sys, enum clk_id id)
{
if (id >= NCLOCKS) {
return NULL;
} else {
clk_t *clk;
clk = ps_clocks[id];
assert(clk);
assert(ps_clocks[id]->init);
clk->clk_sys = sys;
return clk_init(clk);
}
}
void clk_print_tree(clk_t *clk, const char *prefix)
{
int depth = strlen(prefix);
char new_prefix[depth + 2];
strcpy(&new_prefix[0], prefix);
new_prefix[depth] = '|';
new_prefix[depth + 1] = '\0';
while (clk != NULL) {
const char *units[] = {"hz", "Khz", "Mhz", "Ghz"};
const char **u = units;
freq_t freq;
int freqh, freql;
freq = clk_get_freq(clk);
/* Find frequency with appropriate units */
while (freq > 10000 * 1000) {
freq /= 1000;
u++;
}
freqh = freq / 1000;
freql = freq % 1000;
/* Generate tree graphics */
if (clk->sibling == NULL) {
strcpy(new_prefix + depth, " ");
}
if (freqh == 0) {
ZF_LOGI("%s\\%s (%03d %s)", new_prefix, clk->name, freql, u[0]);
} else if (freql == 0) {
ZF_LOGI("%s\\%s (%d %s)", new_prefix, clk->name, freqh, u[1]);
} else {
ZF_LOGI("%s\\%s (%d.%03d %s)", new_prefix, clk->name, freqh, freql, u[1]);
}
clk_print_tree(clk->child, new_prefix);
clk = clk->sibling;
}
}
void clk_register_child(clk_t *parent, clk_t *child)
{
/* Lets make sure that we were initialised correctly
* to avoid tree loops */
if (child->parent != NULL) {
/* If we are registered with a parent */
clk_t *sibling = parent->child;
/* Make sure that we are a sibling of the parent's child */
while (sibling != child) {
assert(sibling);
sibling = sibling->sibling;
}
}
if (child->parent == NULL) {
child->parent = parent;
child->sibling = parent->child;
parent->child = child;
} else if (child->parent != parent) {
ZF_LOGI("%s->%s (%s already had parent %s)",
child->name, parent->name, child->name, child->parent->name);
assert(!"Changing parents not supported yet");
}
}
clk_t clk_generate_fixed_clk(enum clk_id id, freq_t frequency)
{
clk_t ret = { _CLK_OPS(id, "Fixed clock", fixed_clk, NULL) };
ret.req_freq = frequency;
return ret;
}