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
15/***********
16 *** DIV ***
17 ***********/
18
19freq_t
20_div_get_freq(clk_t* clk)
21{
22    clk_regs_io_t** clk_regs;
23    uint32_t div;
24    uint32_t fin;
25    int clkid;
26    clkid = exynos_clk_get_priv_id(clk);
27    clk_regs = clk_get_clk_regs(clk);
28    fin = clk_get_freq(clk->parent);
29    div = exynos_cmu_get_div(clk_regs, clkid, 1);
30    return fin / (div + 1);
31}
32
33freq_t
34_div_set_freq(clk_t* clk, freq_t hz)
35{
36    clk_regs_io_t** clk_regs;
37    uint32_t div;
38    uint32_t fin;
39    int clkid;
40    clkid = exynos_clk_get_priv_id(clk);
41    clk_regs = clk_get_clk_regs(clk);
42    fin = clk_get_freq(clk->parent);
43    if (fin / 1 < hz) {
44        /* Parent frequency too low */
45        fin = clk_set_freq(clk->parent, hz * (MASK(DIV_VAL_BITS) / 2 + 1));
46    }
47    if (fin / (MASK(DIV_VAL_BITS) + 1) > hz) {
48        /* Parent frequency too high */
49        fin = clk_set_freq(clk->parent, hz * (MASK(DIV_VAL_BITS) / 2 + 1));
50    }
51    div = fin / hz;
52    if (div > MASK(DIV_VAL_BITS)) {
53        /* This should have been caught by now, but best to protect against overflow */
54        div = MASK(CLK_DIV_BITS);
55    }
56
57    exynos_cmu_set_div(clk_regs, clkid, 1, div);
58    return clk_get_freq(clk);
59}
60
61void
62_div_recal(clk_t* clk)
63{
64    assert(0);
65}
66
67/***********
68 *** PLL ***
69 ***********/
70
71freq_t
72_pll_get_freq(clk_t* clk)
73{
74    const struct pll_priv* pll_priv;
75    int clkid, pll_idx;
76
77    pll_priv = exynos_clk_get_priv_pll(clk);
78    clkid = pll_priv->clkid;
79    pll_idx = pll_priv->pll_offset;
80
81    return exynos_pll_get_freq(clk, clkid, pll_idx);
82}
83
84freq_t
85_pll_set_freq(clk_t* clk, freq_t hz)
86{
87    volatile struct pll_regs* pll_regs;
88    const struct pll_priv* pll_priv;
89    clk_regs_io_t** clk_regs;
90    struct mpsk_tbl *tbl;
91    int tbl_size;
92    int mhz = hz / (1 * MHZ);
93    uint32_t mps, k;
94    uint32_t con0, con1;
95    int i;
96    int clkid, c, r, o, pll_idx;
97
98    /* get clk regs address and clkid */
99    clk_regs = clk_get_clk_regs(clk);
100    pll_priv = exynos_clk_get_priv_pll(clk);
101    clkid = pll_priv->clkid;
102    pll_idx = pll_priv->pll_offset;
103    clkid_decode(clkid, &c, &r, &o);
104    /* prepare searching the correct frequency parameter */
105    tbl = pll_priv->tbl;
106    tbl_size = pll_priv->pll_tbl_size;
107    pll_regs = (volatile struct pll_regs*)&clk_regs[c]->pll_lock[pll_idx];
108    /* Search the table for an appropriate frequency value and get parameters */
109    mps = tbl[tbl_size - 1].mps;
110    k   = tbl[tbl_size - 1].k;
111    for (i = 0; i < tbl_size; i++) {
112        if (tbl[i].mhz >= mhz) {
113            mps = tbl[i].mps;
114            k = tbl[i].k;
115            break;
116        }
117    }
118    /* set PLL_FOUT to XXTI and bypass PLL */
119    exynos_cmu_set_src(clk_regs, clkid, 0x0);
120    /* updating involved bits in con0 and con1 regs */
121    con0 = pll_regs->con0 & ~PLL_MPS_MASK;
122    con1 = pll_regs->con1 & ~PLL_K_MASK;
123    if (pll_priv->type == PLLTYPE_MPSK) {
124        pll_regs->con1 = (con1 | k);
125    }
126    pll_regs->con0 = (con0 | mps | PLL_ENABLE);
127    while (!(pll_regs->con0 & PLL_LOCKED));
128    /* PLL is configured, set PLL_FOUT to PLL */
129    exynos_cmu_set_src(clk_regs, clkid, 0x1);
130
131    return clk_get_freq(clk);
132}
133
134void
135_pll_recal(clk_t* clk)
136{
137    assert(0);
138}
139
140clk_t*
141_pll_init(clk_t* clk)
142{
143    clk_t* parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MASTER);
144    clk_init(parent);
145    clk_register_child(parent, clk);
146    return clk;
147}
148