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