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 "mt8167-i2c.h"
6
7 #include <ddk/binding.h>
8 #include <ddk/debug.h>
9 #include <ddk/device.h>
10 #include <ddk/platform-defs.h>
11 #include <ddk/protocol/i2cimpl.h>
12 #include <ddk/protocol/platform-device-lib.h>
13 #include <ddk/protocol/platform/bus.h>
14 #include <ddk/protocol/platform/device.h>
15
16 #include <fbl/alloc_checker.h>
17 #include <fbl/auto_call.h>
18 #include <fbl/unique_ptr.h>
19
20 #include <zircon/syscalls/port.h>
21 #include <zircon/types.h>
22
23 //#define TEST_USB_REGS_READ
24
25 namespace mt8167_i2c {
26
27 constexpr size_t kMaxTransferSize = UINT16_MAX - 1; // More than enough.
28 constexpr size_t kHwFifoSize = 8;
29 constexpr uint32_t kEventCompletion = ZX_USER_SIGNAL_0;
30 constexpr zx::duration kTimeout = zx::msec(10);
31
I2cImplGetBusCount()32 uint32_t Mt8167I2c::I2cImplGetBusCount() {
33 return bus_count_;
34 }
35
I2cImplGetMaxTransferSize(uint32_t bus_id,size_t * out_size)36 zx_status_t Mt8167I2c::I2cImplGetMaxTransferSize(uint32_t bus_id, size_t* out_size) {
37 *out_size = kMaxTransferSize;
38 return ZX_OK;
39 }
40
I2cImplSetBitrate(uint32_t bus_id,uint32_t bitrate)41 zx_status_t Mt8167I2c::I2cImplSetBitrate(uint32_t bus_id, uint32_t bitrate) {
42 // TODO(andresoportus): Support changing frequencies.
43 return ZX_ERR_NOT_SUPPORTED;
44 }
45
I2cImplTransact(uint32_t id,const i2c_impl_op_t * ops,size_t count)46 zx_status_t Mt8167I2c::I2cImplTransact(uint32_t id, const i2c_impl_op_t* ops, size_t count) {
47 zx_status_t status = ZX_OK;
48 if (id >= bus_count_) {
49 return ZX_ERR_INVALID_ARGS;
50 }
51
52 auto control_reg = ControlReg::Get().ReadFrom(&keys_[id].mmio);
53 control_reg.set_ackerr_det_en(1).set_clk_ext_en(1).WriteTo(&keys_[id].mmio);
54
55 for (size_t i = 0; i < count; ++i) {
56 if (ops[i].address > 0xFF) {
57 return ZX_ERR_NOT_SUPPORTED;
58 }
59 uint8_t addr = static_cast<uint8_t>(ops[i].address);
60 // TODO(andresoportus): Add support for HW transaction (write followed by read).
61 status = Transact(ops[i].is_read, id, addr, ops[i].data_buffer, ops[i].data_size,
62 ops[i].stop);
63 if (status != ZX_OK) {
64 zxlogf(ERROR, "%s: status %d\n", __FUNCTION__, status);
65 Reset(id);
66 return status;
67 }
68 }
69
70 return ZX_OK;
71 }
72
IrqThread()73 int Mt8167I2c::IrqThread() {
74 zx_port_packet_t packet;
75 while (1) {
76 auto status = irq_port_.wait(zx::time::infinite(), &packet);
77 zxlogf(TRACE, "Port key %lu triggered\n", packet.key);
78 if (status != ZX_OK) {
79 zxlogf(ERROR, "%s: irq_port_.wait failed %d \n", __func__, status);
80 return status;
81 }
82 auto id = static_cast<uint32_t>(packet.key);
83 ZX_ASSERT(id < keys_.size());
84 keys_[id].irq.ack();
85 auto st = IntrStatReg::Get().ReadFrom(&keys_[id].mmio);
86 if (st.arb_lost() || st.hs_nacker() || st.ackerr()) {
87 zxlogf(ERROR, "%s: error 0x%08X\n", __func__,
88 IntrStatReg::Get().ReadFrom(&keys_[id].mmio).reg_value());
89 IntrStatReg::Get().ReadFrom(&keys_[id].mmio).Print();
90 }
91 keys_[id].event.signal(0, kEventCompletion);
92 }
93 }
94
Reset(uint32_t id)95 void Mt8167I2c::Reset(uint32_t id) {
96 SoftResetReg::Get().ReadFrom(&keys_[id].mmio).set_soft_reset(1).WriteTo(&keys_[id].mmio);
97 IntrStatReg::Get().FromValue(0xFFFFFFFF).WriteTo(&keys_[id].mmio); // Write to clear register.
98 }
99
DataMove(bool is_read,uint32_t id,void * buf,size_t len)100 void Mt8167I2c::DataMove(bool is_read, uint32_t id, void* buf, size_t len) {
101 uint8_t* p = static_cast<uint8_t*>(buf);
102 for (size_t i = 0; i < len; ++i) {
103 if (is_read) {
104 p[i] = DataPortReg::Get().ReadFrom(&keys_[id].mmio).reg_value();
105 } else {
106 DataPortReg::Get().FromValue(p[i]).WriteTo(&keys_[id].mmio);
107 }
108 }
109 }
110
Transact(bool is_read,uint32_t id,uint8_t addr,void * buf,size_t len,bool stop)111 zx_status_t Mt8167I2c::Transact(bool is_read, uint32_t id, uint8_t addr, void* buf,
112 size_t len, bool stop) {
113 zx_status_t status;
114
115 // TODO(andresoportus): Only stop when stop is set.
116 // TODO(andresoportus): Add support for arbitrary sizes.
117 if (len > kHwFifoSize) {
118 return ZX_ERR_NOT_SUPPORTED;
119 }
120
121 uint8_t addr_dir = static_cast<uint8_t>((addr << 1) | is_read);
122 FifoAddrClrReg::Get().ReadFrom(&keys_[id].mmio).set_fifo_addr_clr(1).WriteTo(&keys_[id].mmio);
123 SlaveAddrReg::Get().ReadFrom(&keys_[id].mmio).set_reg_value(addr_dir).WriteTo(&keys_[id].mmio);
124 TransferLenReg::Get().FromValue(static_cast<uint8_t>(len)).WriteTo(&keys_[id].mmio);
125 TransacLenReg::Get().FromValue(1).WriteTo(&keys_[id].mmio); // Single transaction of len bytes.
126
127 IntrStatReg::Get().FromValue(0xFFFFFFFF).WriteTo(&keys_[id].mmio); // Write to clear register.
128
129 if (!is_read) {
130 DataMove(is_read, id, buf, len);
131 }
132
133 StartReg::Get().ReadFrom(&keys_[id].mmio).set_start(1).WriteTo(&keys_[id].mmio);
134 status = keys_[id].event.wait_one(kEventCompletion, zx::deadline_after(kTimeout), nullptr);
135 if (status != ZX_OK) {
136 return status;
137 }
138 status = keys_[id].event.signal(kEventCompletion, 0);
139 if (status != ZX_OK) {
140 return status;
141 }
142 if (is_read) {
143 DataMove(is_read, id, buf, len);
144 }
145 auto st = IntrStatReg::Get().ReadFrom(&keys_[id].mmio);
146 if (st.arb_lost() || st.hs_nacker() || st.ackerr()) {
147 return ZX_ERR_INTERNAL;
148 }
149 return ZX_OK;
150 }
151
ShutDown()152 void Mt8167I2c::ShutDown() {
153 for (uint32_t id = 0; id < bus_count_; id++) {
154 keys_[id].irq.destroy();
155 }
156 thrd_join(irq_thread_, NULL);
157 }
158
DdkUnbind()159 void Mt8167I2c::DdkUnbind() {
160 ShutDown();
161 DdkRemove();
162 }
163
DdkRelease()164 void Mt8167I2c::DdkRelease() {
165 delete this;
166 }
167
TestThread()168 int Mt8167I2c::TestThread() {
169 #ifdef TEST_USB_REGS_READ
170 constexpr uint32_t bus_id = 2;
171 constexpr uint8_t addr = 0x48;
172 Reset(bus_id);
173 for (uint8_t data_write = 0; data_write < 0xF; ++data_write) {
174 uint8_t data_read;
175 i2c_impl_op_t ops[] = {
176 {.address = addr,
177 .data_buffer = &data_write,
178 .data_size = 1,
179 .is_read = false,
180 .stop = false},
181 {.address = addr,
182 .data_buffer = &data_read,
183 .data_size = 1,
184 .is_read = true,
185 .stop = true},
186 };
187 auto status = I2cImplTransact(bus_id, ops, countof(ops));
188 if (status == ZX_OK) {
189 zxlogf(INFO, "I2C Addr: 0x%02X Reg:0x%02X Value:0x%02X\n", addr, data_write, data_read);
190 }
191 }
192 #endif
193 return 0;
194 }
195
Bind()196 zx_status_t Mt8167I2c::Bind() {
197 zx_status_t status;
198
199 status = zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &irq_port_);
200 if (status != ZX_OK) {
201 return status;
202 }
203
204 pdev_protocol_t pdev;
205 status = device_get_protocol(parent(), ZX_PROTOCOL_PDEV, &pdev);
206 if (status != ZX_OK) {
207 zxlogf(ERROR, "%s ZX_PROTOCOL_PLATFORM_DEV failed %d\n", __FUNCTION__, status);
208 return ZX_ERR_NOT_SUPPORTED;
209 }
210
211 pdev_device_info_t info;
212 status = pdev_get_device_info(&pdev, &info);
213 if (status != ZX_OK) {
214 zxlogf(ERROR, "%s pdev_get_device_info failed %d\n", __FUNCTION__, status);
215 return ZX_ERR_NOT_SUPPORTED;
216 }
217
218 bus_count_ = info.mmio_count - 1; // Last MMIO is for XO clock.
219 if (bus_count_ != MT8167_I2C_CNT) {
220 zxlogf(ERROR, "%s wrong I2C count %d\n", __FUNCTION__, bus_count_);
221 return ZX_ERR_INTERNAL;
222 }
223
224 mmio_buffer_t mmio;
225 status = pdev_map_mmio_buffer2(&pdev, bus_count_, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
226 if (status != ZX_OK) {
227 zxlogf(ERROR, "%s pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
228 return status;
229 }
230 xo_regs_ = XoRegs(mmio); // Last MMIO is for XO clock.
231
232 for (uint32_t id = 0; id < bus_count_; id++) {
233 status = pdev_map_mmio_buffer2(&pdev, id, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
234 if (status != ZX_OK) {
235 zxlogf(ERROR, "%s pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
236 return status;
237 }
238
239 zx::event event;
240 status = zx::event::create(0, &event);
241 if (status != ZX_OK) {
242 zxlogf(ERROR, "%s zx::event::create failed %d\n", __FUNCTION__, status);
243 return status;
244 }
245 keys_.push_back({ddk::MmioBuffer(mmio), zx::interrupt(), std::move(event)});
246
247 status = pdev_map_interrupt(&pdev, id, keys_[id].irq.reset_and_get_address());
248 if (status != ZX_OK) {
249 return status;
250 }
251 status = keys_[id].irq.bind(irq_port_, id, 0); // id is the port key used.
252 if (status != ZX_OK) {
253 return status;
254 }
255
256 // TODO(andresoportus): Add support for turn on only during transactions?.
257 xo_regs_.value().ClockEnable(id, true);
258
259 // TODO(andresoportus): Add support for DMA mode.
260 }
261
262 auto thunk = [](void* arg) -> int { return reinterpret_cast<Mt8167I2c*>(arg)->IrqThread(); };
263 int rc = thrd_create_with_name(&irq_thread_, thunk, this, "mt8167-i2c");
264 if (rc != thrd_success) {
265 return ZX_ERR_INTERNAL;
266 }
267
268 status = DdkAdd("mt8167-i2c");
269 if (status != ZX_OK) {
270 zxlogf(ERROR, "%s DdkAdd failed: %d\n", __FUNCTION__, status);
271 ShutDown();
272 }
273 return status;
274 }
275
Init()276 zx_status_t Mt8167I2c::Init() {
277 auto cleanup = fbl::MakeAutoCall([&]() { ShutDown(); });
278
279 pbus_protocol_t pbus;
280 if (device_get_protocol(parent(), ZX_PROTOCOL_PBUS, &pbus) != ZX_OK) {
281 zxlogf(ERROR, "%s ZX_PROTOCOL_PLATFORM_BUS not available\n", __FUNCTION__);
282 return ZX_ERR_NOT_SUPPORTED;
283 }
284 i2c_impl_protocol_t i2c_proto = {
285 .ops = &i2c_impl_protocol_ops_,
286 .ctx = this,
287 };
288 const platform_proxy_cb_t kCallback = {NULL, NULL};
289 auto status = pbus_register_protocol(&pbus, ZX_PROTOCOL_I2C_IMPL, &i2c_proto, sizeof(i2c_proto),
290 &kCallback);
291 if (status != ZX_OK) {
292 zxlogf(ERROR, "%s pbus_register_protocol failed: %d\n", __FUNCTION__, status);
293 return status;
294 }
295
296 #ifdef TEST_USB_REGS_READ
297 auto thunk =
298 [](void* arg) -> int { return reinterpret_cast<Mt8167I2c*>(arg)->TestThread(); };
299 int rc = thrd_create_with_name(&irq_thread_, thunk, this, "mt8167-i2c-test");
300 if (rc != thrd_success) {
301 return ZX_ERR_INTERNAL;
302 }
303 #endif
304
305 cleanup.cancel();
306 return ZX_OK;
307 }
308
Create(zx_device_t * parent)309 zx_status_t Mt8167I2c::Create(zx_device_t* parent) {
310 fbl::AllocChecker ac;
311 auto dev = fbl::make_unique_checked<Mt8167I2c>(&ac, parent);
312 if (!ac.check()) {
313 zxlogf(ERROR, "%s ZX_ERR_NO_MEMORY\n", __FUNCTION__);
314 return ZX_ERR_NO_MEMORY;
315 }
316
317 auto status = dev->Bind();
318 if (status != ZX_OK) {
319 return status;
320 }
321
322 // devmgr is now in charge of the memory for dev
323 auto ptr = dev.release();
324
325 return ptr->Init();
326 }
327
328 } // namespace mt8167_i2c
329
mt8167_i2c_bind(void * ctx,zx_device_t * parent)330 extern "C" zx_status_t mt8167_i2c_bind(void* ctx, zx_device_t* parent) {
331 return mt8167_i2c::Mt8167I2c::Create(parent);
332 }
333