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 16int32 17OcoresI2c::InterruptReceived(void* arg) 18{ 19 return static_cast<OcoresI2c*>(arg)->InterruptReceivedInt(); 20} 21 22 23int32 24OcoresI2c::InterruptReceivedInt() 25{ 26 // TODO: implement interrupt handling, use polling for now 27 return B_HANDLED_INTERRUPT; 28} 29 30 31status_t 32OcoresI2c::WaitCompletion() 33{ 34 while (!fRegs->status.interrupt) {} 35 return B_OK; 36} 37 38 39status_t 40OcoresI2c::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 52status_t 53OcoresI2c::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 66status_t 67OcoresI2c::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 79float 80OcoresI2c::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 105status_t 106OcoresI2c::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 118status_t 119OcoresI2c::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 131status_t 132OcoresI2c::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 173void 174OcoresI2c::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 184void 185OcoresI2c::SetI2cBus(i2c_bus bus) 186{ 187 dprintf("OcoresI2c::SetI2cBus()\n"); 188 fBus = bus; 189} 190 191 192status_t 193OcoresI2c::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 224status_t 225OcoresI2c::AcquireBus() 226{ 227 return mutex_lock(&fLock); 228} 229 230 231void 232OcoresI2c::ReleaseBus() 233{ 234 mutex_unlock(&fLock); 235} 236