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