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 "mtk-clk.h"
6 
7 #include <ddk/protocol/platform/bus.h>
8 #include <ddk/protocol/platform/device.h>
9 #include <ddktl/pdev.h>
10 #include <fbl/algorithm.h>
11 #include <fbl/alloc_checker.h>
12 #include <fbl/unique_ptr.h>
13 #include <hwreg/bitfields.h>
14 #include <soc/mt8167/mt8167-clk.h>
15 #include <zircon/device/clk.h>
16 
17 namespace clk {
18 
19 struct MtkClkGateRegs {
20     const zx_off_t set;
21     const zx_off_t clr;
22 };
23 
24 struct MtkClkGate {
25     const MtkClkGateRegs regs;
26     const uint8_t bit;
27 };
28 
29 constexpr MtkClkGateRegs kClkGatingCtrl0 = {.set = 0x50, .clr = 0x80};
30 constexpr MtkClkGateRegs kClkGatingCtrl1 = {.set = 0x54, .clr = 0x84};
31 constexpr MtkClkGateRegs kClkGatingCtrl8 = {.set = 0xa0, .clr = 0xb0};
32 
33 constexpr MtkClkGate kMtkClkGates[] = {
34     [board_mt8167::kClkThermal] = {.regs = kClkGatingCtrl1, .bit = 1},
35     [board_mt8167::kClkI2c0] = {.regs = kClkGatingCtrl1, .bit = 3},
36     [board_mt8167::kClkI2c1] = {.regs = kClkGatingCtrl1, .bit = 4},
37     [board_mt8167::kClkI2c2] = {.regs = kClkGatingCtrl1, .bit = 16},
38     [board_mt8167::kClkPmicWrapAp] = {.regs = kClkGatingCtrl1, .bit = 20},
39     [board_mt8167::kClkPmicWrap26M] = {.regs = kClkGatingCtrl1, .bit = 29},
40     [board_mt8167::kClkAuxAdc] = {.regs = kClkGatingCtrl1, .bit = 30},
41     [board_mt8167::kClkSlowMfg] = {.regs = kClkGatingCtrl8, .bit = 7},
42     [board_mt8167::kClkAxiMfg] = {.regs = kClkGatingCtrl8, .bit = 6},
43     [board_mt8167::kClkMfgMm] = {.regs = kClkGatingCtrl0, .bit = 2},
44 };
45 struct clock_info {
46     uint32_t idx;
47     const char* name;
48 };
49 
50 static struct clock_info clks[] = {
51     {.idx = 1, .name = "mainpll_div8"},
52     {.idx = 2, .name = "mainpll_div11"},
53     {.idx = 3, .name = "mainpll_div12"},
54     {.idx = 4, .name = "mainpll_div20"},
55     {.idx = 5, .name = "mainpll_div7"},
56     {.idx = 6, .name = "univpll_div16"},
57     {.idx = 7, .name = "univpll_div24"},
58     {.idx = 8, .name = "nfix2"},
59     {.idx = 9, .name = "whpll"},
60     {.idx = 10, .name = "wpll"},
61     {.idx = 11, .name = "26mhz"},
62     {.idx = 18, .name = "mfg"},
63     {.idx = 45, .name = "axi_mfg"},
64     {.idx = 46, .name = "slow_mfg"},
65     {.idx = 67, .name = "mmpll"},
66 };
67 
68 namespace {
69 
70 class FrequencyMeterControl : public hwreg::RegisterBase<FrequencyMeterControl, uint32_t> {
71 public:
72     enum {
73         kFixClk26Mhz = 0,
74         kFixClk32Khz = 2,
75     };
76 
77     DEF_FIELD(29, 28, ck_div);
78     DEF_FIELD(24, 24, fixclk_sel);
79     DEF_FIELD(22, 16, monclk_sel);
80     DEF_BIT(15, enable);
81     DEF_BIT(14, reset);
82     DEF_FIELD(11, 0, window);
83 
Get()84     static auto Get() { return hwreg::RegisterAddr<FrequencyMeterControl>(0x10); }
85 };
86 
87 } // namespace
88 
Bind()89 zx_status_t MtkClk::Bind() {
90     zx_status_t status;
91     pbus_protocol_t pbus;
92     status = device_get_protocol(parent(), ZX_PROTOCOL_PBUS, &pbus);
93     if (status != ZX_OK) {
94         zxlogf(ERROR, "MtkClk: failed to get ZX_PROTOCOL_PBUS, st = %d\n", status);
95         return status;
96     }
97 
98     clk_protocol_t clk_proto = {
99         .ops = &clk_protocol_ops_,
100         .ctx = this,
101     };
102 
103     const platform_proxy_cb_t kCallback = {nullptr, nullptr};
104     status = pbus_register_protocol(&pbus, ZX_PROTOCOL_CLK, &clk_proto, sizeof(clk_proto),
105                                     &kCallback);
106     if (status != ZX_OK) {
107         zxlogf(ERROR, "MtkClk::Create: pbus_register_protocol failed, st = %d\n", status);
108         return status;
109     }
110 
111     return DdkAdd("mtk-clk");
112 }
113 
Create(zx_device_t * parent)114 zx_status_t MtkClk::Create(zx_device_t* parent) {
115     zx_status_t status;
116 
117     pdev_protocol_t pdev_proto;
118     if ((status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev_proto)) != ZX_OK) {
119         zxlogf(ERROR, "%s: ZX_PROTOCOL_PDEV not available\n", __FILE__);
120         return status;
121     }
122 
123     ddk::PDev pdev(&pdev_proto);
124     std::optional<ddk::MmioBuffer> mmio;
125     if ((status = pdev.MapMmio(0, &mmio)) != ZX_OK) {
126         zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed\n", __FILE__);
127         return status;
128     }
129 
130     fbl::AllocChecker ac;
131     fbl::unique_ptr<MtkClk> device(new (&ac) MtkClk(parent, std::move(*mmio)));
132     if (!ac.check()) {
133         zxlogf(ERROR, "%s: MtkClk alloc failed\n", __FILE__);
134         return ZX_ERR_NO_MEMORY;
135     }
136 
137     if ((status = device->Bind()) != ZX_OK) {
138         zxlogf(ERROR, "%s: MtkClk bind failed: %d\n", __FILE__, status);
139         return status;
140     }
141 
142     __UNUSED auto* dummy = device.release();
143 
144     return ZX_OK;
145 }
146 
ClkEnable(uint32_t index)147 zx_status_t MtkClk::ClkEnable(uint32_t index) {
148     if (index >= fbl::count_of(kMtkClkGates)) {
149         return ZX_ERR_INVALID_ARGS;
150     }
151 
152     const MtkClkGate& gate = kMtkClkGates[index];
153     mmio_.Write32(1 << gate.bit, gate.regs.clr);
154     return ZX_OK;
155 }
156 
ClkDisable(uint32_t index)157 zx_status_t MtkClk::ClkDisable(uint32_t index) {
158     if (index >= fbl::count_of(kMtkClkGates)) {
159         return ZX_ERR_INVALID_ARGS;
160     }
161 
162     const MtkClkGate& gate = kMtkClkGates[index];
163     mmio_.Write32(1 << gate.bit, gate.regs.set);
164     return ZX_OK;
165 }
166 
ClkMeasure(uint32_t clk,clk_freq_info_t * info)167 zx_status_t MtkClk::ClkMeasure(uint32_t clk, clk_freq_info_t* info) {
168     if (clk >= fbl::count_of(clks)) {
169         return ZX_ERR_INVALID_ARGS;
170     }
171 
172     size_t max_len = sizeof(info->clk_name);
173     size_t len = strnlen(clks[clk].name, max_len);
174     if (len == max_len) {
175         return ZX_ERR_INVALID_ARGS;
176     }
177 
178     memcpy(info->clk_name, clks[clk].name, len + 1);
179 
180     constexpr uint32_t kWindowSize = 512;
181     constexpr uint32_t kFixedClockFreqMHz = 26000000 / 1000000;
182     FrequencyMeterControl::Get().FromValue(0).set_reset(true).WriteTo(&mmio_);
183     FrequencyMeterControl::Get().FromValue(0).set_reset(false).WriteTo(&mmio_);
184     auto ctrl = FrequencyMeterControl::Get().FromValue(0);
185     ctrl.set_window(kWindowSize - 1).set_monclk_sel(clks[clk].idx);
186     ctrl.set_fixclk_sel(FrequencyMeterControl::kFixClk26Mhz).set_enable(true);
187     ctrl.WriteTo(&mmio_);
188 
189     hw_wmb();
190 
191     // Sleep at least kWindowSize ticks of the fixed clock.
192     zx_nanosleep(zx_deadline_after(ZX_USEC(30)));
193 
194     // Assume it completed calculating.
195 
196     constexpr uint32_t kFrequencyMeterReadData = 0x14;
197     uint32_t count = mmio_.Read32(kFrequencyMeterReadData);
198     info->clk_freq = (count * kFixedClockFreqMHz) / kWindowSize;
199     FrequencyMeterControl::Get().FromValue(0).set_reset(true).WriteTo(&mmio_);
200     FrequencyMeterControl::Get().FromValue(0).set_reset(false).WriteTo(&mmio_);
201     return ZX_OK;
202 }
203 
DdkIoctl(uint32_t op,const void * in_buf,size_t in_len,void * out_buf,size_t out_len,size_t * out_actual)204 zx_status_t MtkClk::DdkIoctl(uint32_t op, const void* in_buf,
205                              size_t in_len, void* out_buf,
206                              size_t out_len, size_t* out_actual) {
207     switch (op) {
208     case IOCTL_CLK_MEASURE: {
209         if (in_buf == nullptr || in_len != sizeof(uint32_t) ||
210             out_buf == nullptr || out_len != sizeof(clk_freq_info_t)) {
211             return ZX_ERR_INVALID_ARGS;
212         }
213         auto index = *(static_cast<const uint32_t*>(in_buf));
214         auto* info = static_cast<clk_freq_info_t*>(out_buf);
215         *out_actual = sizeof(clk_freq_info_t);
216         return ClkMeasure(index, info);
217     }
218     case IOCTL_CLK_GET_COUNT: {
219         if (out_buf == nullptr || out_len != sizeof(uint32_t)) {
220             return ZX_ERR_INVALID_ARGS;
221         }
222         auto* num_count = static_cast<uint32_t*>(out_buf);
223         *num_count = static_cast<uint32_t>(fbl::count_of(clks));
224         *out_actual = sizeof(uint32_t);
225         return ZX_OK;
226     }
227     default:
228         return ZX_ERR_NOT_SUPPORTED;
229     }
230 }
231 
232 }  // namespace clk
233 
mtk_clk_bind(void * ctx,zx_device_t * parent)234 extern "C" zx_status_t mtk_clk_bind(void* ctx, zx_device_t* parent) {
235     return clk::MtkClk::Create(parent);
236 }
237