1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include "clock.h"
14#include <string.h>
15#include <assert.h>
16#include "../../services.h"
17
18/* Fixed clocks */
19static freq_t _fixed_clk_get_freq(clk_t *clk)
20{
21    return clk->req_freq;
22}
23
24static freq_t _fixed_clk_set_freq(clk_t *clk, freq_t hz UNUSED)
25{
26    return clk_get_freq(clk);
27}
28
29void _fixed_clk_recal(clk_t *clk UNUSED)
30{
31    assert(0);
32}
33
34clk_t *_fixed_clk_init(clk_t *clk)
35{
36    return clk;
37}
38
39/* Default clocks. Simply report the recorded default frequency */
40freq_t _default_clk_get_freq(clk_t *clk)
41{
42    assert(clk->id < NCLOCKS);
43    return ps_freq_default[clk->id];
44}
45
46freq_t _default_clk_set_freq(clk_t *clk, freq_t hz UNUSED)
47{
48    return clk_get_freq(clk);
49}
50
51void _default_clk_recal(clk_t *clk UNUSED)
52{
53    assert(0);
54}
55
56clk_t *_default_clk_init(clk_t *clk)
57{
58    return clk;
59}
60
61static clk_t *get_clock_default(clock_sys_t *clock_sys UNUSED, enum clk_id id)
62{
63    if (id >= NCLOCKS) {
64        return NULL;
65    } else {
66        clk_t *clk;
67        clk = ps_clocks[id];
68        /* Destroy original clock links */
69        clk->clk_sys = clock_sys;
70        clk->init = _default_clk_init;
71        clk->get_freq = _default_clk_get_freq;
72        clk->set_freq = _default_clk_set_freq;
73        clk->recal = _default_clk_recal;
74        return clk;
75    }
76}
77
78static int gate_enable_default(clock_sys_t *clock_sys UNUSED, enum clock_gate gate UNUSED,
79                               enum clock_gate_mode mode UNUSED)
80{
81    /* Assume the gate is already enabled */
82    return 0;
83}
84
85int clock_sys_init_default(clock_sys_t *clock_sys)
86{
87    clock_sys->get_clock = &get_clock_default;
88    clock_sys->gate_enable = &gate_enable_default;
89    /* Clock subsystem is invalid if there is not private data attached */
90    clock_sys->priv = (void *)0xdeadbeef;
91    return 0;
92}
93
94int clock_sys_set_default_freq(enum clk_id id, freq_t hz)
95{
96    if (id >= NCLOCKS) {
97        return -1;
98    } else {
99        ps_freq_default[id] = hz;
100        return 0;
101    }
102}
103
104clk_t *ps_get_clock(clock_sys_t *sys, enum clk_id id)
105{
106    if (id >= NCLOCKS) {
107        return NULL;
108    } else {
109        clk_t *clk;
110        clk = ps_clocks[id];
111        assert(clk);
112        assert(ps_clocks[id]->init);
113        clk->clk_sys = sys;
114        return clk_init(clk);
115    }
116}
117
118void clk_print_tree(clk_t *clk, const char *prefix)
119{
120    int depth = strlen(prefix);
121    char new_prefix[depth + 2];
122    strcpy(&new_prefix[0], prefix);
123    new_prefix[depth] = '|';
124    new_prefix[depth + 1] = '\0';
125    while (clk != NULL) {
126        const char *units[] = {"hz", "Khz", "Mhz", "Ghz"};
127        const char **u = units;
128        freq_t freq;
129        int freqh, freql;
130        freq = clk_get_freq(clk);
131        /* Find frequency with appropriate units */
132        while (freq > 10000 * 1000) {
133            freq /= 1000;
134            u++;
135        }
136        freqh = freq / 1000;
137        freql = freq % 1000;
138        /* Generate tree graphics */
139        if (clk->sibling == NULL) {
140            strcpy(new_prefix + depth, " ");
141        }
142        if (freqh == 0) {
143            printf("%s\\%s (%03d %s)\n", new_prefix, clk->name, freql, u[0]);
144        } else if (freql == 0) {
145            printf("%s\\%s (%d %s)\n", new_prefix, clk->name, freqh, u[1]);
146        } else {
147            printf("%s\\%s (%d.%03d %s)\n", new_prefix, clk->name, freqh, freql, u[1]);
148        }
149        clk_print_tree(clk->child, new_prefix);
150        clk = clk->sibling;
151    }
152}
153
154void clk_register_child(clk_t *parent, clk_t *child)
155{
156    /* Lets make sure that we were initialised correctly
157     * to avoid tree loops */
158    if (child->parent != NULL) {
159        /* If we are registered with a parent */
160        clk_t *sibling = parent->child;
161        /* Make sure that we are a sibling of the parent's child */
162        while (sibling != child) {
163            assert(sibling);
164            sibling = sibling->sibling;
165        }
166    }
167    if (child->parent == NULL) {
168        child->parent = parent;
169        child->sibling = parent->child;
170        parent->child = child;
171    } else if (child->parent != parent) {
172        printf("%s->%s\n | %s already has parent %s",
173               child->name, parent->name, child->name,
174               child->parent->name);
175        assert(!"Changing parents not supported yet");
176    }
177}
178
179clk_t clk_generate_fixed_clk(enum clk_id id, freq_t frequency)
180{
181    clk_t ret = { _CLK_OPS(id, "Fixed clock", fixed_clk, NULL) };
182    ret.req_freq = frequency;
183    return ret;
184}
185