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 // Temporary Clock and PLL management until the clock protocol is fully
6 // developed.
7 
8 
9 #include <hwreg/bitfields.h>
10 #include <hwreg/mmio.h>
11 #include <hw/reg.h>
12 #include <zircon/syscalls.h>
13 
14 #include "aml-pcie-clk.h"
15 
16 #define AXG_MIPI_CNTL0 0xa5b80000
17 
18 #define PCIE_PLL_CNTL0 (0x36 * 4)
19 #define PCIE_PLL_CNTL1 (0x37 * 4)
20 #define PCIE_PLL_CNTL2 (0x38 * 4)
21 #define PCIE_PLL_CNTL3 (0x39 * 4)
22 #define PCIE_PLL_CNTL4 (0x3A * 4)
23 #define PCIE_PLL_CNTL5 (0x3B * 4)
24 #define PCIE_PLL_CNTL6 (0x3C * 4)
25 
26 #define AXG_PCIE_PLL_CNTL0 0x400106c8
27 #define AXG_PCIE_PLL_CNTL1 0x0084a2aa
28 #define AXG_PCIE_PLL_CNTL2 0xb75020be
29 #define AXG_PCIE_PLL_CNTL3 0x0a47488e
30 #define AXG_PCIE_PLL_CNTL4 0xc000004d
31 #define AXG_PCIE_PLL_CNTL5 0x00078000
32 #define AXG_PCIE_PLL_CNTL6 0x002323c6
33 
34 #define MESON_PLL_ENABLE (1 << 30)
35 #define MESON_PLL_RESET  (1 << 29)
36 
37 class MesonPLLControl0 : public hwreg::RegisterBase<MesonPLLControl0, uint32_t> {
38   public:
39     DEF_FIELD(8, 0, m);
40     DEF_FIELD(13, 9, n);
41     DEF_FIELD(17, 16, od);
42     DEF_BIT(29, reset);
43     DEF_BIT(30, enable);
44     DEF_BIT(31, lock);
45 
Get()46     static auto Get() {return hwreg::RegisterAddr<MesonPLLControl0>(PCIE_PLL_CNTL0); }
47 };
48 
49 class MesonPLLControl1 : public hwreg::RegisterBase<MesonPLLControl1, uint32_t> {
50   public:
51     DEF_FIELD(11, 0, div_frac);
52     DEF_BIT(12, div_mode);
53     DEF_FIELD(14, 13, dcvc_in);
54     DEF_FIELD(16, 15, dco_sdmck_sel);
55     DEF_BIT(17, dco_m_en);
56     DEF_BIT(18, dco_band_opt);
57     DEF_FIELD(21, 19, data_sel);
58     DEF_FIELD(23, 22, afc_nt);
59     DEF_FIELD(25, 24, afc_hold_t);
60     DEF_FIELD(27, 26, afc_dsel_in);
61     DEF_BIT(28, afc_dsel_bypass);
62     DEF_BIT(29, afc_clk_sel);
63     DEF_FIELD(31, 30, acq_r_ctr);
64 
Get()65     static auto Get() {return hwreg::RegisterAddr<MesonPLLControl1>(PCIE_PLL_CNTL1); }
66 };
67 
68 class MesonPLLControl6 : public hwreg::RegisterBase<MesonPLLControl6, uint32_t> {
69   public:
70     DEF_FIELD(7, 6, od2);
71     DEF_BIT(2, cml_input_sel1);
72     DEF_BIT(1, cml_input_sel0);
73     DEF_BIT(0, cml_input_en);
74 
Get()75     static auto Get() { return hwreg::RegisterAddr<MesonPLLControl6>(PCIE_PLL_CNTL6); }
76 };
77 
78 
PllSetRate(ddk::MmioBuffer * mmio)79 zx_status_t PllSetRate(ddk::MmioBuffer* mmio) {
80     // TODO(gkalsi): This statically configures the PCIe PLL to run at
81     //               100mhz. When we write a real clock driver, we want this
82     //               value to be configurable.
83 
84     mmio->Write32(AXG_MIPI_CNTL0, 0);
85     mmio->Write32(AXG_PCIE_PLL_CNTL0, PCIE_PLL_CNTL0);
86     mmio->Write32(AXG_PCIE_PLL_CNTL1, PCIE_PLL_CNTL1);
87     mmio->Write32(AXG_PCIE_PLL_CNTL2, PCIE_PLL_CNTL2);
88     mmio->Write32(AXG_PCIE_PLL_CNTL3, PCIE_PLL_CNTL3);
89     mmio->Write32(AXG_PCIE_PLL_CNTL4, PCIE_PLL_CNTL4);
90     mmio->Write32(AXG_PCIE_PLL_CNTL5, PCIE_PLL_CNTL5);
91     mmio->Write32(AXG_PCIE_PLL_CNTL6, PCIE_PLL_CNTL6);
92 
93     auto cntl0 = MesonPLLControl0::Get().ReadFrom(mmio);
94 
95     cntl0.set_enable(1);
96     cntl0.set_reset(0);
97     cntl0.WriteTo(mmio);
98 
99     cntl0.set_m(200);
100     cntl0.set_n(3);
101     cntl0.set_od(1);
102     cntl0.WriteTo(mmio);
103 
104     auto cntl1 = MesonPLLControl1::Get().ReadFrom(mmio);
105     cntl1.set_div_frac(0);
106     cntl1.WriteTo(mmio);
107 
108     auto cntl6 = MesonPLLControl6::Get().ReadFrom(mmio);
109     cntl6.set_od2(3);
110     cntl6.set_cml_input_sel1(1);
111     cntl6.set_cml_input_sel0(1);
112     cntl6.set_cml_input_en(1);
113     cntl6.WriteTo(mmio);
114 
115     // Assert the Reset pin on the PLL
116     cntl0.set_reset(1);
117     cntl0.WriteTo(mmio);
118 
119     // Wait for the reset to take effect
120     zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
121 
122     // De-assert the reset pin
123     cntl0.set_reset(0);
124     cntl0.WriteTo(mmio);
125 
126     // Wait for the PLL parameters to lock.
127     const uint64_t kTimeout = 24000000;
128     for (uint64_t attempts = 0; attempts < kTimeout; ++attempts) {
129         auto cntl0 = MesonPLLControl0::Get().ReadFrom(mmio);
130 
131         // PLL has successfully locked?
132         if (cntl0.lock()) return ZX_OK;
133     }
134 
135     return ZX_ERR_TIMED_OUT;
136 }
137