ocores_i2c: add
Change-Id: Ib48f8dbc0bf485b469b590f1472994d250b6ed45 Reviewed-on: https://review.haiku-os.org/c/haiku/+/5900 Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org> Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
parent
8798a36c9b
commit
85d9d60045
@ -1,3 +1,4 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel busses i2c ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses i2c ocores ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses i2c pch ;
|
||||
|
11
src/add-ons/kernel/busses/i2c/ocores/Jamfile
Normal file
11
src/add-ons/kernel/busses/i2c/ocores/Jamfile
Normal file
@ -0,0 +1,11 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel busses i2c ocores ;
|
||||
|
||||
SubDirC++Flags -fno-rtti ;
|
||||
|
||||
UsePrivateHeaders i2c ;
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon ocores_i2c :
|
||||
ocores_i2c.cpp
|
||||
kernel_interface.cpp
|
||||
;
|
59
src/add-ons/kernel/busses/i2c/ocores/kernel_interface.cpp
Normal file
59
src/add-ons/kernel/busses/i2c/ocores/kernel_interface.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2022, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "ocores_i2c.h"
|
||||
|
||||
|
||||
device_manager_info* gDeviceManager;
|
||||
i2c_for_controller_interface* gI2c;
|
||||
|
||||
|
||||
i2c_sim_interface gOcoresI2cDriver = {
|
||||
.info = {
|
||||
.info = {
|
||||
.name = OCORES_I2C_DRIVER_MODULE_NAME,
|
||||
},
|
||||
.supports_device = [](device_node* parent) {
|
||||
return OcoresI2c::SupportsDevice(parent);
|
||||
},
|
||||
.register_device = [](device_node* parent) {
|
||||
return OcoresI2c::RegisterDevice(parent);
|
||||
},
|
||||
.init_driver = [](device_node* node, void** driverCookie) {
|
||||
return OcoresI2c::InitDriver(node, *(OcoresI2c**)driverCookie);
|
||||
},
|
||||
.uninit_driver = [](void* driverCookie) {
|
||||
return static_cast<OcoresI2c*>(driverCookie)->UninitDriver();
|
||||
},
|
||||
},
|
||||
.set_i2c_bus = [](i2c_bus_cookie cookie, i2c_bus bus) {
|
||||
static_cast<OcoresI2c*>(cookie)->SetI2cBus(bus);
|
||||
},
|
||||
.exec_command = [](i2c_bus_cookie cookie, i2c_op op,
|
||||
i2c_addr slaveAddress, const void *cmdBuffer, size_t cmdLength,
|
||||
void* dataBuffer, size_t dataLength) {
|
||||
return static_cast<OcoresI2c*>(cookie)->ExecCommand(op, slaveAddress,
|
||||
(const uint8*)cmdBuffer, cmdLength, (uint8*)dataBuffer, dataLength);
|
||||
},
|
||||
.acquire_bus = [](i2c_bus_cookie cookie) {
|
||||
return static_cast<OcoresI2c*>(cookie)->AcquireBus();
|
||||
},
|
||||
.release_bus = [](i2c_bus_cookie cookie) {
|
||||
static_cast<OcoresI2c*>(cookie)->ReleaseBus();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
_EXPORT module_dependency module_dependencies[] = {
|
||||
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
|
||||
{ I2C_FOR_CONTROLLER_MODULE_NAME, (module_info**)&gI2c },
|
||||
{}
|
||||
};
|
||||
|
||||
_EXPORT module_info *modules[] = {
|
||||
(module_info *)&gOcoresI2cDriver,
|
||||
NULL
|
||||
};
|
232
src/add-ons/kernel/busses/i2c/ocores/ocores_i2c.cpp
Normal file
232
src/add-ons/kernel/busses/i2c/ocores/ocores_i2c.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2022, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "ocores_i2c.h"
|
||||
#include <bus/FDT.h>
|
||||
|
||||
#include <AutoDeleterDrivers.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
|
||||
|
||||
int32
|
||||
OcoresI2c::InterruptReceived(void* arg)
|
||||
{
|
||||
return static_cast<OcoresI2c*>(arg)->InterruptReceivedInt();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
OcoresI2c::InterruptReceivedInt()
|
||||
{
|
||||
// TODO: implement interrupt handling, use polling for now
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::WaitCompletion()
|
||||
{
|
||||
while (!fRegs->status.interrupt) {}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::WriteByte(OcoresI2cRegsCommand cmd, uint8 val)
|
||||
{
|
||||
cmd.intAck = true;
|
||||
cmd.write = true;
|
||||
//dprintf("OcoresI2c::WriteByte(cmd: %#02x, val: %#02x)\n", cmd.val, val);
|
||||
fRegs->data = val;
|
||||
fRegs->command.val = cmd.val;
|
||||
CHECK_RET(WaitCompletion());
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::ReadByte(OcoresI2cRegsCommand cmd, uint8& val)
|
||||
{
|
||||
cmd.intAck = true;
|
||||
cmd.read = true;
|
||||
cmd.nack = cmd.stop;
|
||||
fRegs->command.val = cmd.val;
|
||||
CHECK_RET(WaitCompletion());
|
||||
val = fRegs->data;
|
||||
//dprintf("OcoresI2c::ReadByte(cmd: %#02x, val: %#02x)\n", cmd.val, val);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::WriteAddress(i2c_addr adr, bool isRead)
|
||||
{
|
||||
// TODO: 10 bit address support
|
||||
//dprintf("OcoresI2c::WriteAddress(adr: %#04x, isRead: %d)\n", adr, isRead);
|
||||
uint8 val = OcoresI2cRegsAddress7{.read = isRead, .address = (uint8)adr}.val;
|
||||
CHECK_RET(WriteByte({.start = true}, val));
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
//#pragma mark - driver
|
||||
|
||||
float
|
||||
OcoresI2c::SupportsDevice(device_node* parent)
|
||||
{
|
||||
const char* bus;
|
||||
status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
|
||||
if (status < B_OK)
|
||||
return -1.0f;
|
||||
|
||||
if (strcmp(bus, "fdt") != 0)
|
||||
return 0.0f;
|
||||
|
||||
const char* compatible;
|
||||
status = gDeviceManager->get_attr_string(parent, "fdt/compatible", &compatible, false);
|
||||
if (status < B_OK)
|
||||
return -1.0f;
|
||||
|
||||
if (strcmp(compatible, "opencores,i2c-ocores") != 0)
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::RegisterDevice(device_node* parent)
|
||||
{
|
||||
device_attr attrs[] = {
|
||||
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Opencores I2C Controller"} },
|
||||
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {string: I2C_FOR_CONTROLLER_MODULE_NAME} },
|
||||
{}
|
||||
};
|
||||
|
||||
return gDeviceManager->register_node(parent, OCORES_I2C_DRIVER_MODULE_NAME, attrs, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::InitDriver(device_node* node, OcoresI2c*& outDriver)
|
||||
{
|
||||
ObjectDeleter<OcoresI2c> driver(new(std::nothrow) OcoresI2c());
|
||||
if (!driver.IsSet())
|
||||
return B_NO_MEMORY;
|
||||
|
||||
CHECK_RET(driver->InitDriverInt(node));
|
||||
outDriver = driver.Detach();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::InitDriverInt(device_node* node)
|
||||
{
|
||||
fNode = node;
|
||||
dprintf("+OcoresI2c::InitDriver()\n");
|
||||
|
||||
DeviceNodePutter<&gDeviceManager> parent(gDeviceManager->get_parent_node(node));
|
||||
|
||||
const char* bus;
|
||||
CHECK_RET(gDeviceManager->get_attr_string(parent.Get(), B_DEVICE_BUS, &bus, false));
|
||||
if (strcmp(bus, "fdt") != 0)
|
||||
return B_ERROR;
|
||||
|
||||
fdt_device_module_info *parentModule;
|
||||
fdt_device* parentDev;
|
||||
CHECK_RET(gDeviceManager->get_driver(parent.Get(),
|
||||
(driver_module_info**)&parentModule, (void**)&parentDev));
|
||||
|
||||
uint64 regs = 0;
|
||||
uint64 regsLen = 0;
|
||||
if (!parentModule->get_reg(parentDev, 0, ®s, ®sLen))
|
||||
return B_ERROR;
|
||||
|
||||
fRegsArea.SetTo(map_physical_memory("Ocores i2c MMIO", regs, regsLen, B_ANY_KERNEL_ADDRESS,
|
||||
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fRegs));
|
||||
if (!fRegsArea.IsSet())
|
||||
return fRegsArea.Get();
|
||||
|
||||
uint64 irq;
|
||||
if (!parentModule->get_interrupt(parentDev, 0, NULL, &irq))
|
||||
return B_ERROR;
|
||||
fIrqVector = irq; // TODO: take interrupt controller into account
|
||||
|
||||
// TODO: enable when implement interrupt handling
|
||||
if (false)
|
||||
install_io_interrupt_handler(fIrqVector, InterruptReceived, this, 0);
|
||||
|
||||
dprintf("-OcoresI2c::InitDriver()\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OcoresI2c::UninitDriver()
|
||||
{
|
||||
if (false)
|
||||
remove_io_interrupt_handler(fIrqVector, InterruptReceived, this);
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
//#pragma mark - i2c controller
|
||||
|
||||
void
|
||||
OcoresI2c::SetI2cBus(i2c_bus bus)
|
||||
{
|
||||
dprintf("OcoresI2c::SetI2cBus()\n");
|
||||
fBus = bus;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::ExecCommand(i2c_op op,
|
||||
i2c_addr slaveAddress, const uint8 *cmdBuffer, size_t cmdLength,
|
||||
uint8* dataBuffer, size_t dataLength)
|
||||
{
|
||||
//dprintf("OcoresI2c::ExecCommand()\n");
|
||||
(void)op;
|
||||
if (cmdLength > 0) {
|
||||
CHECK_RET(WriteAddress(slaveAddress, false));
|
||||
do {
|
||||
if (fRegs->status.nackReceived) {
|
||||
fRegs->command.val = OcoresI2cRegsCommand{
|
||||
.intAck = true,
|
||||
.stop = true
|
||||
}.val;
|
||||
return B_ERROR;
|
||||
}
|
||||
cmdLength--;
|
||||
CHECK_RET(WriteByte({.stop = cmdLength == 0 && dataLength == 0}, *cmdBuffer++));
|
||||
} while (cmdLength > 0);
|
||||
}
|
||||
if (dataLength > 0) {
|
||||
CHECK_RET(WriteAddress(slaveAddress, true));
|
||||
do {
|
||||
dataLength--;
|
||||
CHECK_RET(ReadByte({.stop = dataLength == 0}, *dataBuffer++));
|
||||
} while (dataLength > 0);
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OcoresI2c::AcquireBus()
|
||||
{
|
||||
return mutex_lock(&fLock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OcoresI2c::ReleaseBus()
|
||||
{
|
||||
mutex_unlock(&fLock);
|
||||
}
|
124
src/add-ons/kernel/busses/i2c/ocores/ocores_i2c.h
Normal file
124
src/add-ons/kernel/busses/i2c/ocores/ocores_i2c.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2022, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _OCORES_I2C_H_
|
||||
#define _OCORES_I2C_H_
|
||||
|
||||
#include <i2c.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <AutoDeleterOS.h>
|
||||
#include <lock.h>
|
||||
|
||||
|
||||
#define CHECK_RET(err) {status_t _err = (err); if (_err < B_OK) return _err;}
|
||||
|
||||
#define OCORES_I2C_DRIVER_MODULE_NAME "busses/i2c/ocores_i2c/driver_v1"
|
||||
|
||||
|
||||
static_assert(B_HOST_IS_LENDIAN);
|
||||
|
||||
union OcoresI2cRegsAddress7 {
|
||||
struct {
|
||||
uint8 read: 1;
|
||||
uint8 address: 7;
|
||||
};
|
||||
uint8 val;
|
||||
};
|
||||
|
||||
union OcoresI2cRegsControl {
|
||||
struct {
|
||||
uint8 unused1: 6;
|
||||
uint8 intEnabled: 1;
|
||||
uint8 enabled: 1;
|
||||
};
|
||||
uint8 val;
|
||||
};
|
||||
|
||||
union OcoresI2cRegsCommand {
|
||||
struct {
|
||||
uint8 intAck: 1;
|
||||
uint8 unused1: 2;
|
||||
uint8 nack: 1;
|
||||
uint8 write: 1;
|
||||
uint8 read: 1;
|
||||
uint8 stop: 1;
|
||||
uint8 start: 1;
|
||||
};
|
||||
uint8 val;
|
||||
};
|
||||
|
||||
union OcoresI2cRegsStatus {
|
||||
struct {
|
||||
uint8 interrupt: 1;
|
||||
uint8 transferInProgress: 1;
|
||||
uint8 reserved1: 3;
|
||||
uint8 arbitrationLost: 1;
|
||||
uint8 busy: 1;
|
||||
uint8 nackReceived: 1;
|
||||
};
|
||||
uint8 val;
|
||||
};
|
||||
|
||||
struct OcoresI2cRegs {
|
||||
uint8 preLo;
|
||||
uint8 align1[3];
|
||||
uint8 preHi;
|
||||
uint8 align2[3];
|
||||
OcoresI2cRegsControl control;
|
||||
uint8 align3[3];
|
||||
uint8 data;
|
||||
uint8 align4[3];
|
||||
union {
|
||||
OcoresI2cRegsCommand command;
|
||||
OcoresI2cRegsStatus status;
|
||||
};
|
||||
uint8 align5[3];
|
||||
};
|
||||
|
||||
|
||||
class OcoresI2c {
|
||||
public:
|
||||
static float SupportsDevice(device_node* parent);
|
||||
static status_t RegisterDevice(device_node* parent);
|
||||
static status_t InitDriver(device_node* node, OcoresI2c*& outDriver);
|
||||
void UninitDriver();
|
||||
|
||||
void SetI2cBus(i2c_bus bus);
|
||||
status_t ExecCommand(i2c_op op,
|
||||
i2c_addr slaveAddress, const uint8 *cmdBuffer, size_t cmdLength,
|
||||
uint8* dataBuffer, size_t dataLength);
|
||||
status_t AcquireBus();
|
||||
void ReleaseBus();
|
||||
|
||||
private:
|
||||
inline status_t InitDriverInt(device_node* node);
|
||||
static int32 InterruptReceived(void* arg);
|
||||
inline int32 InterruptReceivedInt();
|
||||
|
||||
status_t WaitCompletion();
|
||||
status_t WriteByte(OcoresI2cRegsCommand cmd, uint8 val);
|
||||
status_t ReadByte(OcoresI2cRegsCommand cmd, uint8& val);
|
||||
status_t WriteAddress(i2c_addr address, bool isRead);
|
||||
|
||||
private:
|
||||
struct mutex fLock = MUTEX_INITIALIZER("Opencores i2c");
|
||||
|
||||
AreaDeleter fRegsArea;
|
||||
volatile OcoresI2cRegs* fRegs{};
|
||||
long fIrqVector = -1;
|
||||
|
||||
device_node* fNode{};
|
||||
i2c_bus fBus{};
|
||||
};
|
||||
|
||||
|
||||
extern device_manager_info* gDeviceManager;
|
||||
extern i2c_for_controller_interface* gI2c;
|
||||
extern i2c_sim_interface gOcoresI2cDriver;
|
||||
|
||||
#endif // _OCORES_I2C_H_
|
Loading…
Reference in New Issue
Block a user