xref: /haiku/src/add-ons/kernel/busses/i2c/ocores/ocores_i2c.cpp (revision 7003a4cb1d16ce99aba14c5abf75e04230246f74)
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, &regs, &regsLen))
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