⛏️ index : haiku.git

/*
 * 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_