1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddk/debug.h>
6#include <hw/reg.h>
7
8#include <zircon/assert.h>
9#include <zircon/types.h>
10
11#include <soc/aml-s905d2/s905d2-hiu.h>
12#include <soc/aml-s905d2/s905d2-hw.h>
13
14zx_status_t s905d2_hiu_init(zx_handle_t bti, aml_hiu_dev_t* device) {
15
16    zx_handle_t resource = get_root_resource();
17    zx_status_t status;
18
19    status = io_buffer_init_physical(&device->regs_iobuff, bti, S905D2_HIU_BASE,
20                                     S905D2_HIU_LENGTH, resource,
21                                     ZX_CACHE_POLICY_UNCACHED_DEVICE);
22    if (status != ZX_OK) {
23        zxlogf(ERROR, "%s: io_buffer_init_physical failed %d\n", __func__, status);
24        return status;
25    }
26
27    device->virt_regs = (zx_vaddr_t)(io_buffer_virt(&device->regs_iobuff));
28
29    return ZX_OK;
30}
31
32static zx_status_t s905d2_pll_init_regs(aml_pll_dev_t* pll_dev) {
33    aml_hiu_dev_t* device = pll_dev->hiu;
34
35    if (pll_dev->pll_num == HIFI_PLL) {
36        //write config values
37        hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL1, G12A_HIFI_PLL_CNTL1);
38        hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL2, G12A_HIFI_PLL_CNTL2);
39        hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL3, G12A_HIFI_PLL_CNTL3);
40        hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL4, G12A_HIFI_PLL_CNTL4);
41        hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL5, G12A_HIFI_PLL_CNTL5);
42        hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL6, G12A_HIFI_PLL_CNTL6);
43        zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
44        return ZX_OK;
45    } else if (pll_dev->pll_num == SYS_PLL) {
46        //write config values
47        hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL1, G12A_SYS_PLL_CNTL1);
48        hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL2, G12A_SYS_PLL_CNTL2);
49        hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL3, G12A_SYS_PLL_CNTL3);
50        hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL4, G12A_SYS_PLL_CNTL4);
51        hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL5, G12A_SYS_PLL_CNTL5);
52        hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL6, G12A_SYS_PLL_CNTL6);
53        zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
54        return ZX_OK;
55    } else if (pll_dev->pll_num == GP0_PLL) {
56        //write config values
57        hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL1, G12A_GP0_PLL_CNTL1);
58        hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL2, G12A_GP0_PLL_CNTL2);
59        hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL3, G12A_GP0_PLL_CNTL3);
60        hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL4, G12A_GP0_PLL_CNTL4);
61        hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL5, G12A_GP0_PLL_CNTL5);
62        hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL6, G12A_GP0_PLL_CNTL6);
63        zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
64        return ZX_OK;
65    }
66    return ZX_OK;
67}
68
69zx_status_t s905d2_pll_init(aml_hiu_dev_t* device, aml_pll_dev_t* pll_dev, hhi_plls_t pll_num) {
70    ZX_DEBUG_ASSERT(device);
71    ZX_DEBUG_ASSERT(pll_dev);
72
73    pll_dev->hiu = device;
74
75    pll_dev->rate_table = s905d2_pll_get_rate_table(pll_num);
76    pll_dev->rate_idx = 0;
77    pll_dev->frequency = 0;
78    pll_dev->pll_num = pll_num;
79    pll_dev->rate_count = s905d2_get_rate_table_count(pll_num);
80
81    ZX_DEBUG_ASSERT(pll_dev->rate_table);
82    ZX_DEBUG_ASSERT(pll_dev->rate_count);
83
84    //Disable and reset the pll
85    s905d2_pll_disable(pll_dev);
86    //Write configuration registers
87    return s905d2_pll_init_regs(pll_dev);
88}
89
90bool s905d2_pll_disable(aml_pll_dev_t* pll_dev) {
91    uint32_t offs = hiu_get_pll_offs(pll_dev);
92    uint32_t ctl0 = hiu_clk_get_reg(pll_dev->hiu, offs);
93
94    bool retval = ctl0 & HHI_PLL_CNTL0_EN;
95
96    ctl0 = (ctl0 & ~HHI_PLL_CNTL0_EN) | HHI_PLL_CNTL0_RESET;
97    hiu_clk_set_reg(pll_dev->hiu, offs, ctl0);
98
99    return retval;
100}
101
102zx_status_t s905d2_pll_ena(aml_pll_dev_t* pll_dev) {
103    ZX_DEBUG_ASSERT(pll_dev);
104
105    uint32_t offs = hiu_get_pll_offs(pll_dev);
106    uint32_t reg_val = hiu_clk_get_reg(pll_dev->hiu, offs);
107
108    // Set Enable bit
109    reg_val |= HHI_PLL_CNTL0_EN;
110    hiu_clk_set_reg(pll_dev->hiu, offs, reg_val);
111    zx_nanosleep(zx_deadline_after(ZX_USEC(50)));
112
113    // Clear Reset bit
114    reg_val &= ~HHI_PLL_CNTL0_RESET;
115    hiu_clk_set_reg(pll_dev->hiu, offs, reg_val);
116
117    uint32_t wait_count = 100;
118    while (wait_count) {
119        if (hiu_clk_get_reg(pll_dev->hiu, offs) & HHI_PLL_LOCK) {
120            return ZX_OK;
121        }
122        zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
123        wait_count--;
124    }
125
126    return ZX_ERR_TIMED_OUT;
127}
128
129/* Notes:
130    -VCO needs to be between 3-6GHz per the datasheet. It appears that if you
131    provide values which would result in a VCO outside of this range, it will
132    still oscillate, but at unknown (but likely close to target) frequency.
133*/
134zx_status_t s905d2_pll_set_rate(aml_pll_dev_t* pll_dev, uint64_t freq) {
135    ZX_DEBUG_ASSERT(pll_dev);
136
137    const hhi_pll_rate_t* pll_rate;
138
139    zx_status_t status = s905d2_pll_fetch_rate(pll_dev, freq, &pll_rate);
140    if (status != ZX_OK) {
141        return status;
142    }
143    //Disable/reset the pll, save previous state
144    bool ena = s905d2_pll_disable(pll_dev);
145
146    //Initialize the registers to defaults (may not be retained after reset)
147    s905d2_pll_init_regs(pll_dev);
148
149    uint32_t offs = hiu_get_pll_offs(pll_dev);
150    uint32_t ctl0 = hiu_clk_get_reg(pll_dev->hiu, offs);
151
152    ctl0 &= ~HHI_PLL_CNTL0_M;
153    ctl0 |= pll_rate->m << HHI_PLL_CNTL0_M_SHIFT;
154
155    ctl0 &= ~HHI_PLL_CNTL0_N;
156    ctl0 |= pll_rate->n << HHI_PLL_CNTL0_N_SHIFT;
157
158    ctl0 &= ~HHI_PLL_CNTL0_OD;
159    ctl0 |= pll_rate->od << HHI_PLL_CNTL0_OD_SHIFT;
160
161    hiu_clk_set_reg(pll_dev->hiu, offs, ctl0);
162
163    hiu_clk_set_reg(pll_dev->hiu, offs + 4, pll_rate->frac);
164
165    if (ena) {
166        return s905d2_pll_ena(pll_dev);
167    }
168
169    return ZX_OK;
170}
171