1 /*
2 * Copyright 2022, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "ocores_i2c.h"
8 #include <bus/FDT.h>
9
10 #include <AutoDeleterDrivers.h>
11
12 #include <string.h>
13 #include <new>
14
15
16 int32
InterruptReceived(void * arg)17 OcoresI2c::InterruptReceived(void* arg)
18 {
19 return static_cast<OcoresI2c*>(arg)->InterruptReceivedInt();
20 }
21
22
23 int32
InterruptReceivedInt()24 OcoresI2c::InterruptReceivedInt()
25 {
26 // TODO: implement interrupt handling, use polling for now
27 return B_HANDLED_INTERRUPT;
28 }
29
30
31 status_t
WaitCompletion()32 OcoresI2c::WaitCompletion()
33 {
34 while (!fRegs->status.interrupt) {}
35 return B_OK;
36 }
37
38
39 status_t
WriteByte(OcoresI2cRegsCommand cmd,uint8 val)40 OcoresI2c::WriteByte(OcoresI2cRegsCommand cmd, uint8 val)
41 {
42 cmd.intAck = true;
43 cmd.write = true;
44 //dprintf("OcoresI2c::WriteByte(cmd: %#02x, val: %#02x)\n", cmd.val, val);
45 fRegs->data = val;
46 fRegs->command.val = cmd.val;
47 CHECK_RET(WaitCompletion());
48 return B_OK;
49 }
50
51
52 status_t
ReadByte(OcoresI2cRegsCommand cmd,uint8 & val)53 OcoresI2c::ReadByte(OcoresI2cRegsCommand cmd, uint8& val)
54 {
55 cmd.intAck = true;
56 cmd.read = true;
57 cmd.nack = cmd.stop;
58 fRegs->command.val = cmd.val;
59 CHECK_RET(WaitCompletion());
60 val = fRegs->data;
61 //dprintf("OcoresI2c::ReadByte(cmd: %#02x, val: %#02x)\n", cmd.val, val);
62 return B_OK;
63 }
64
65
66 status_t
WriteAddress(i2c_addr adr,bool isRead)67 OcoresI2c::WriteAddress(i2c_addr adr, bool isRead)
68 {
69 // TODO: 10 bit address support
70 //dprintf("OcoresI2c::WriteAddress(adr: %#04x, isRead: %d)\n", adr, isRead);
71 uint8 val = OcoresI2cRegsAddress7{.read = isRead, .address = (uint8)adr}.val;
72 CHECK_RET(WriteByte({.start = true}, val));
73 return B_OK;
74 }
75
76
77 //#pragma mark - driver
78
79 float
SupportsDevice(device_node * parent)80 OcoresI2c::SupportsDevice(device_node* parent)
81 {
82 const char* bus;
83 status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
84 if (status < B_OK)
85 return -1.0f;
86
87 if (strcmp(bus, "fdt") != 0)
88 return 0.0f;
89
90 const char* compatible;
91 status = gDeviceManager->get_attr_string(parent, "fdt/compatible", &compatible, false);
92 if (status < B_OK)
93 return -1.0f;
94
95 if (strcmp(compatible, "opencores,i2c-ocores") != 0
96 && strcmp(compatible, "sifive,fu740-c000-i2c") != 0
97 && strcmp(compatible, "sifive,i2c0") != 0) {
98 return 0.0f;
99 }
100
101 return 1.0f;
102 }
103
104
105 status_t
RegisterDevice(device_node * parent)106 OcoresI2c::RegisterDevice(device_node* parent)
107 {
108 device_attr attrs[] = {
109 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Opencores I2C Controller"} },
110 { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {string: I2C_FOR_CONTROLLER_MODULE_NAME} },
111 {}
112 };
113
114 return gDeviceManager->register_node(parent, OCORES_I2C_DRIVER_MODULE_NAME, attrs, NULL, NULL);
115 }
116
117
118 status_t
InitDriver(device_node * node,OcoresI2c * & outDriver)119 OcoresI2c::InitDriver(device_node* node, OcoresI2c*& outDriver)
120 {
121 ObjectDeleter<OcoresI2c> driver(new(std::nothrow) OcoresI2c());
122 if (!driver.IsSet())
123 return B_NO_MEMORY;
124
125 CHECK_RET(driver->InitDriverInt(node));
126 outDriver = driver.Detach();
127 return B_OK;
128 }
129
130
131 status_t
InitDriverInt(device_node * node)132 OcoresI2c::InitDriverInt(device_node* node)
133 {
134 fNode = node;
135 dprintf("+OcoresI2c::InitDriver()\n");
136
137 DeviceNodePutter<&gDeviceManager> parent(gDeviceManager->get_parent_node(node));
138
139 const char* bus;
140 CHECK_RET(gDeviceManager->get_attr_string(parent.Get(), B_DEVICE_BUS, &bus, false));
141 if (strcmp(bus, "fdt") != 0)
142 return B_ERROR;
143
144 fdt_device_module_info *parentModule;
145 fdt_device* parentDev;
146 CHECK_RET(gDeviceManager->get_driver(parent.Get(),
147 (driver_module_info**)&parentModule, (void**)&parentDev));
148
149 uint64 regs = 0;
150 uint64 regsLen = 0;
151 if (!parentModule->get_reg(parentDev, 0, ®s, ®sLen))
152 return B_ERROR;
153
154 fRegsArea.SetTo(map_physical_memory("Ocores i2c MMIO", regs, regsLen, B_ANY_KERNEL_ADDRESS,
155 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fRegs));
156 if (!fRegsArea.IsSet())
157 return fRegsArea.Get();
158
159 uint64 irq;
160 if (!parentModule->get_interrupt(parentDev, 0, NULL, &irq))
161 return B_ERROR;
162 fIrqVector = irq; // TODO: take interrupt controller into account
163
164 // TODO: enable when implement interrupt handling
165 if (false)
166 install_io_interrupt_handler(fIrqVector, InterruptReceived, this, 0);
167
168 dprintf("-OcoresI2c::InitDriver()\n");
169 return B_OK;
170 }
171
172
173 void
UninitDriver()174 OcoresI2c::UninitDriver()
175 {
176 if (false)
177 remove_io_interrupt_handler(fIrqVector, InterruptReceived, this);
178 delete this;
179 }
180
181
182 //#pragma mark - i2c controller
183
184 void
SetI2cBus(i2c_bus bus)185 OcoresI2c::SetI2cBus(i2c_bus bus)
186 {
187 dprintf("OcoresI2c::SetI2cBus()\n");
188 fBus = bus;
189 }
190
191
192 status_t
ExecCommand(i2c_op op,i2c_addr slaveAddress,const uint8 * cmdBuffer,size_t cmdLength,uint8 * dataBuffer,size_t dataLength)193 OcoresI2c::ExecCommand(i2c_op op,
194 i2c_addr slaveAddress, const uint8 *cmdBuffer, size_t cmdLength,
195 uint8* dataBuffer, size_t dataLength)
196 {
197 //dprintf("OcoresI2c::ExecCommand()\n");
198 (void)op;
199 if (cmdLength > 0) {
200 CHECK_RET(WriteAddress(slaveAddress, false));
201 do {
202 if (fRegs->status.nackReceived) {
203 fRegs->command.val = OcoresI2cRegsCommand{
204 .intAck = true,
205 .stop = true
206 }.val;
207 return B_ERROR;
208 }
209 cmdLength--;
210 CHECK_RET(WriteByte({.stop = cmdLength == 0 && dataLength == 0}, *cmdBuffer++));
211 } while (cmdLength > 0);
212 }
213 if (dataLength > 0) {
214 CHECK_RET(WriteAddress(slaveAddress, true));
215 do {
216 dataLength--;
217 CHECK_RET(ReadByte({.stop = dataLength == 0}, *dataBuffer++));
218 } while (dataLength > 0);
219 }
220 return B_OK;
221 }
222
223
224 status_t
AcquireBus()225 OcoresI2c::AcquireBus()
226 {
227 return mutex_lock(&fLock);
228 }
229
230
231 void
ReleaseBus()232 OcoresI2c::ReleaseBus()
233 {
234 mutex_unlock(&fLock);
235 }
236