1 // Copyright 2017 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 <stdint.h>
6 #include <threads.h>
7 #include <unistd.h>
8 
9 #include <bits/limits.h>
10 #include <ddk/debug.h>
11 
12 #include <zircon/assert.h>
13 #include <zircon/types.h>
14 
15 #include <soc/aml-a113/a113-clocks.h>
16 
17 #define DIV_ROUND_UP(n,d) ((n + d - 1) / d)
18 
19 /* create instance of a113_clock_t and do basic initialization.
20 */
a113_clk_init(a113_clk_dev_t ** device)21 zx_status_t a113_clk_init(a113_clk_dev_t **device) {
22 
23     *device = calloc(1, sizeof(a113_clk_dev_t));
24     if (!(*device)) {
25         return ZX_ERR_NO_MEMORY;
26     }
27 
28     zx_handle_t resource = get_root_resource();
29     zx_status_t status;
30 
31     status = mmio_buffer_init_physical(&(*device)->mmio, A113_CLOCKS_BASE_PHYS,PAGE_SIZE,
32                                        resource, ZX_CACHE_POLICY_UNCACHED_DEVICE);
33 
34     if (status != ZX_OK) {
35         zxlogf(ERROR, "a113_clk_init: mmio_buffer_init_physical failed %d\n", status);
36         goto init_fail;
37     }
38     (*device)->regs_vaddr = (*device)->mmio.vaddr;
39 
40     return ZX_OK;
41 
42 init_fail:
43     mmio_buffer_release(&(*device)->mmio);
44     free(*device);
45     return status;
46 }
47 
a113_clk_update_reg(a113_clk_dev_t * dev,uint32_t offset,uint32_t pos,uint32_t bits,uint32_t value)48 static void a113_clk_update_reg(a113_clk_dev_t *dev, uint32_t offset,
49                                                      uint32_t pos,
50                                                      uint32_t bits,
51                                                      uint32_t value) {
52     uint32_t reg = a113_clk_get_reg(dev, offset);
53     reg &= ~(((1 << bits) - 1) << pos);
54     reg |=  (value & ((1 << bits) - 1)) << pos;
55     a113_clk_set_reg(dev, offset, reg);
56 }
57 
a113_clk_set_mpll2(a113_clk_dev_t * device,uint64_t rate,uint64_t * actual)58 zx_status_t a113_clk_set_mpll2(a113_clk_dev_t *device, uint64_t rate, uint64_t *actual) {
59     /* Overall ratio is reference/(n + sdm/16384)
60        In this case the 2.0GHz fixed rate pll is the reference
61     */
62     uint64_t n = A113_FIXED_PLL_RATE/rate;  //calculate the integer ratio;
63     ZX_DEBUG_ASSERT( n < (1 << 9));
64 
65     uint64_t sdm = DIV_ROUND_UP((A113_FIXED_PLL_RATE - n * rate) * SDM_FRACTIONALITY,
66                                 rate);
67     ZX_DEBUG_ASSERT(sdm < (1 << 14));
68     zxlogf(INFO,"%s sdm= %ld  n= %ld\n",__func__,sdm,n);
69     a113_clk_update_reg(device, A113_HHI_MPLL_CNTL8, 0, 14, (uint32_t)sdm);
70     a113_clk_update_reg(device, A113_HHI_MPLL_CNTL8, 16, 9, (uint32_t)n);
71 
72     // Enable sdm divider
73     a113_clk_update_reg(device, A113_HHI_MPLL_CNTL8, 15, 1, 1);
74     // Enable mpll2
75     a113_clk_update_reg(device, A113_HHI_MPLL_CNTL8, 14, 1, 1);
76     // Gate mpll2 through to rest of system
77     a113_clk_update_reg(device, A113_HHI_PLL_TOP_MISC, 2, 1, 1);
78 
79     if (actual) {
80         *actual = ((uint64_t)SDM_FRACTIONALITY * A113_FIXED_PLL_RATE) /
81                   ((SDM_FRACTIONALITY * n) +sdm);
82     }
83 
84     return ZX_OK;
85 }
86