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