pch_i2c: i2c bus driver for Intel PCH (Skylake+)
acpi initial support Change-Id: Id48b1211356e47b7dec4c74ad49eeb7bd704bc67 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2458 Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
@@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel busses ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses ata ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses agp_gart ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses i2c ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses mmc ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses random ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses scsi ;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel busses i2c ;
|
||||
|
||||
SubDirC++Flags -fno-rtti ;
|
||||
|
||||
UsePrivateHeaders i2c ;
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) bus_managers acpi acpica include ] ;
|
||||
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) bus_managers acpi acpica include platform ] ;
|
||||
|
||||
KernelAddon pch_i2c :
|
||||
pch_i2c.cpp
|
||||
pch_i2c_acpi.cpp
|
||||
pch_i2c_pci.cpp
|
||||
;
|
||||
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ACPI.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <condition_variable.h>
|
||||
#include <bus/PCI.h>
|
||||
#include <PCI_x86.h>
|
||||
|
||||
|
||||
#include "pch_i2c.h"
|
||||
|
||||
|
||||
device_manager_info* gDeviceManager;
|
||||
i2c_for_controller_interface* gI2c;
|
||||
acpi_module_info* gACPI;
|
||||
|
||||
|
||||
static void
|
||||
enable_device(pch_i2c_sim_info* bus, bool enable)
|
||||
{
|
||||
uint32 status = enable ? 1 : 0;
|
||||
for (int tries = 100; tries >= 0; tries--) {
|
||||
write32(bus->registers + PCH_IC_ENABLE, status);
|
||||
if ((read32(bus->registers + PCH_IC_ENABLE_STATUS) & 1) == status)
|
||||
return;
|
||||
snooze(25);
|
||||
}
|
||||
|
||||
ERROR("enable_device failed\n");
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
pch_i2c_interrupt_handler(pch_i2c_sim_info* bus)
|
||||
{
|
||||
int32 handled = B_HANDLED_INTERRUPT;
|
||||
|
||||
// Check if this interrupt is ours
|
||||
uint32 enable = read32(bus->registers + PCH_IC_ENABLE);
|
||||
if (enable == 0)
|
||||
return B_UNHANDLED_INTERRUPT;
|
||||
|
||||
uint32 status = read32(bus->registers + PCH_IC_INTR_STAT);
|
||||
if ((status & PCH_IC_INTR_STAT_RX_UNDER) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_RX_UNDER, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_RX_OVER) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_RX_OVER, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_TX_OVER) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_TX_OVER, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_RD_REQ) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_RD_REQ, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_TX_ABRT) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_TX_ABRT, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_RX_DONE) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_RX_DONE, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_ACTIVITY) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_ACTIVITY, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_STOP_DET) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_STOP_DET, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_START_DET) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_START_DET, 0);
|
||||
if ((status & PCH_IC_INTR_STAT_GEN_CALL) != 0)
|
||||
write32(bus->registers + PCH_IC_CLR_GEN_CALL, 0);
|
||||
|
||||
TRACE("pch_i2c_interrupt_handler %" B_PRIx32 "\n", status);
|
||||
|
||||
if ((status & ~PCH_IC_INTR_STAT_ACTIVITY) == 0)
|
||||
return handled;
|
||||
/*if ((status & PCH_IC_INTR_STAT_TX_ABRT) != 0)
|
||||
tx error */
|
||||
if ((status & PCH_IC_INTR_STAT_RX_FULL) != 0)
|
||||
ConditionVariable::NotifyAll(&bus->readwait, B_OK);
|
||||
if ((status & PCH_IC_INTR_STAT_TX_EMPTY) != 0)
|
||||
ConditionVariable::NotifyAll(&bus->writewait, B_OK);
|
||||
if ((status & PCH_IC_INTR_STAT_STOP_DET) != 0) {
|
||||
bus->busy = 0;
|
||||
ConditionVariable::NotifyAll(&bus->busy, B_OK);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static void
|
||||
set_sim(i2c_bus_cookie cookie, i2c_bus sim)
|
||||
{
|
||||
CALLED();
|
||||
pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
|
||||
bus->sim = sim;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exec_command(i2c_bus_cookie cookie, i2c_op op, i2c_addr slaveAddress,
|
||||
const void *cmdBuffer, size_t cmdLength, void* dataBuffer,
|
||||
size_t dataLength)
|
||||
{
|
||||
CALLED();
|
||||
pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
|
||||
|
||||
if (atomic_test_and_set(&bus->busy, 1, 0) != 0)
|
||||
return B_BUSY;
|
||||
|
||||
TRACE("exec_command: acquired busy flag\n");
|
||||
|
||||
uint32 status = 0;
|
||||
for (int tries = 100; tries >= 0; tries--) {
|
||||
status = read32(bus->registers + PCH_IC_STATUS);
|
||||
if ((status & PCH_IC_STATUS_ACTIVITY) == 0)
|
||||
break;
|
||||
snooze(1000);
|
||||
}
|
||||
|
||||
if ((status & PCH_IC_STATUS_ACTIVITY) != 0) {
|
||||
bus->busy = 0;
|
||||
return B_BUSY;
|
||||
}
|
||||
|
||||
TRACE("exec_command: write slave address\n");
|
||||
|
||||
enable_device(bus, false);
|
||||
write32(bus->registers + PCH_IC_CON,
|
||||
read32(bus->registers + PCH_IC_CON) & ~PCH_IC_CON_10BIT_ADDR_MASTER);
|
||||
write32(bus->registers + PCH_IC_TAR, slaveAddress);
|
||||
|
||||
write32(bus->registers + PCH_IC_INTR_MASK, 0);
|
||||
read32(bus->registers + PCH_IC_CLR_INTR);
|
||||
|
||||
enable_device(bus, true);
|
||||
|
||||
read32(bus->registers + PCH_IC_CLR_INTR);
|
||||
write32(bus->registers + PCH_IC_INTR_MASK, PCH_IC_INTR_STAT_TX_EMPTY);
|
||||
|
||||
// wait for write
|
||||
// wait_lock
|
||||
|
||||
if (cmdLength > 0) {
|
||||
TRACE("exec_command: write command buffer\n");
|
||||
uint16 txLimit = bus->tx_fifo_depth
|
||||
- read32(bus->registers + PCH_IC_TXFLR);
|
||||
if (cmdLength > txLimit) {
|
||||
ERROR("exec_command can't write, cmd too long %" B_PRIuSIZE
|
||||
" (max %d)\n", cmdLength, txLimit);
|
||||
bus->busy = 0;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
uint8* buffer = (uint8*)cmdBuffer;
|
||||
for (size_t i = 0; i < cmdLength; i++) {
|
||||
uint32 cmd = buffer[i];
|
||||
if (i == cmdLength - 1 && dataLength == 0 && IS_STOP_OP(op))
|
||||
cmd |= PCH_IC_DATA_CMD_STOP;
|
||||
write32(bus->registers + PCH_IC_DATA_CMD, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("exec_command: processing buffer %" B_PRIuSIZE " bytes\n",
|
||||
dataLength);
|
||||
uint16 txLimit = bus->tx_fifo_depth
|
||||
- read32(bus->registers + PCH_IC_TXFLR);
|
||||
uint8* buffer = (uint8*)dataBuffer;
|
||||
size_t readPos = 0;
|
||||
size_t i = 0;
|
||||
while (i < dataLength) {
|
||||
uint32 cmd = PCH_IC_DATA_CMD_READ;
|
||||
if (IS_WRITE_OP(op))
|
||||
cmd = buffer[i];
|
||||
|
||||
if (i == 0 && cmdLength > 0 && IS_READ_OP(op))
|
||||
cmd |= PCH_IC_DATA_CMD_RESTART;
|
||||
|
||||
if (i == (dataLength - 1) && IS_STOP_OP(op))
|
||||
cmd |= PCH_IC_DATA_CMD_STOP;
|
||||
|
||||
write32(bus->registers + PCH_IC_DATA_CMD, cmd);
|
||||
|
||||
if (IS_READ_OP(op) && IS_BLOCK_OP(op) && readPos == 0)
|
||||
txLimit = 1;
|
||||
txLimit--;
|
||||
i++;
|
||||
|
||||
// here read the data if needed
|
||||
while (IS_READ_OP(op) && (txLimit == 0 || i == dataLength)) {
|
||||
write32(bus->registers + PCH_IC_INTR_MASK,
|
||||
PCH_IC_INTR_STAT_RX_FULL);
|
||||
|
||||
// sleep until wake up by intr handler
|
||||
struct ConditionVariable condition;
|
||||
condition.Publish(&bus->readwait, "pch_i2c");
|
||||
ConditionVariableEntry variableEntry;
|
||||
status_t status = variableEntry.Wait(&bus->readwait,
|
||||
B_RELATIVE_TIMEOUT, 500000L);
|
||||
condition.Unpublish();
|
||||
if (status != B_OK)
|
||||
ERROR("exec_command timed out waiting for read\n");
|
||||
uint32 rxBytes = read32(bus->registers + PCH_IC_RXFLR);
|
||||
if (rxBytes == 0) {
|
||||
ERROR("exec_command timed out reading %" B_PRIuSIZE " bytes\n",
|
||||
dataLength - readPos);
|
||||
bus->busy = 0;
|
||||
return B_ERROR;
|
||||
}
|
||||
for (; rxBytes > 0; rxBytes--) {
|
||||
uint32 read = read32(bus->registers + PCH_IC_DATA_CMD);
|
||||
if (readPos < dataLength)
|
||||
buffer[readPos++] = read;
|
||||
}
|
||||
|
||||
if (IS_BLOCK_OP(op) && readPos > 0 && dataLength > buffer[0])
|
||||
dataLength = buffer[0] + 1;
|
||||
if (readPos >= dataLength)
|
||||
break;
|
||||
|
||||
TRACE("exec_command %" B_PRIuSIZE" bytes to be read\n",
|
||||
dataLength - readPos);
|
||||
txLimit = bus->tx_fifo_depth
|
||||
- read32(bus->registers + PCH_IC_TXFLR);
|
||||
}
|
||||
}
|
||||
|
||||
status_t err = B_OK;
|
||||
if (IS_STOP_OP(op) && IS_WRITE_OP(op)) {
|
||||
TRACE("exec_command: waiting busy condition\n");
|
||||
while (bus->busy == 1) {
|
||||
write32(bus->registers + PCH_IC_INTR_MASK,
|
||||
PCH_IC_INTR_STAT_STOP_DET);
|
||||
|
||||
// sleep until wake up by intr handler
|
||||
struct ConditionVariable condition;
|
||||
condition.Publish(&bus->busy, "pch_i2c");
|
||||
ConditionVariableEntry variableEntry;
|
||||
err = variableEntry.Wait(&bus->busy, B_RELATIVE_TIMEOUT,
|
||||
500000L);
|
||||
condition.Unpublish();
|
||||
if (err != B_OK)
|
||||
ERROR("exec_command timed out waiting for busy\n");
|
||||
}
|
||||
}
|
||||
TRACE("exec_command: processing done\n");
|
||||
|
||||
bus->busy = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static acpi_status
|
||||
pch_i2c_scan_parse_callback(ACPI_RESOURCE *res, void *context)
|
||||
{
|
||||
struct pch_i2c_crs* crs = (struct pch_i2c_crs*)context;
|
||||
|
||||
if (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
|
||||
res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||
crs->i2c_addr = B_LENDIAN_TO_HOST_INT16(
|
||||
res->Data.I2cSerialBus.SlaveAddress);
|
||||
return AE_CTRL_TERMINATE;
|
||||
} else if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
|
||||
crs->irq = res->Data.Irq.Interrupts[0];
|
||||
crs->irq_triggering = res->Data.Irq.Triggering;
|
||||
crs->irq_polarity = res->Data.Irq.Polarity;
|
||||
crs->irq_sharable = res->Data.Irq.Sharable;
|
||||
} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
|
||||
crs->irq = res->Data.ExtendedIrq.Interrupts[0];
|
||||
crs->irq_triggering = res->Data.ExtendedIrq.Triggering;
|
||||
crs->irq_polarity = res->Data.ExtendedIrq.Polarity;
|
||||
crs->irq_sharable = res->Data.ExtendedIrq.Sharable;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
acpi_GetInteger(acpi_handle acpiCookie,
|
||||
const char* path, int64* number)
|
||||
{
|
||||
acpi_data buf;
|
||||
acpi_object_type object;
|
||||
buf.pointer = &object;
|
||||
buf.length = sizeof(acpi_object_type);
|
||||
|
||||
// Assume that what we've been pointed at is an Integer object, or
|
||||
// a method that will return an Integer.
|
||||
status_t status = gACPI->evaluate_method(acpiCookie, path, NULL, &buf);
|
||||
if (status == B_OK) {
|
||||
if (object.object_type == ACPI_TYPE_INTEGER)
|
||||
*number = object.integer.integer;
|
||||
else
|
||||
status = B_BAD_VALUE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
acpi_status
|
||||
pch_i2c_scan_bus_callback(acpi_handle object, uint32 nestingLevel,
|
||||
void *context, void** returnValue)
|
||||
{
|
||||
pch_i2c_sim_info* bus = (pch_i2c_sim_info*)context;
|
||||
TRACE("pch_i2c_scan_bus_callback %p\n", object);
|
||||
|
||||
// skip absent devices
|
||||
int64 sta;
|
||||
status_t status = acpi_GetInteger(object, "_STA", &sta);
|
||||
if (status == B_OK && (sta & ACPI_STA_DEVICE_PRESENT) == 0)
|
||||
return B_OK;
|
||||
|
||||
// Attach devices for I2C resources
|
||||
struct pch_i2c_crs crs;
|
||||
status = gACPI->walk_resources(object, (ACPI_STRING)"_CRS",
|
||||
pch_i2c_scan_parse_callback, &crs);
|
||||
if (status != B_OK) {
|
||||
ERROR("Error while getting I2C devices\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
TRACE("pch_i2c_scan_bus_callback deviceAddress %x\n", crs.i2c_addr);
|
||||
|
||||
acpi_data buffer;
|
||||
buffer.pointer = NULL;
|
||||
buffer.length = ACPI_ALLOCATE_BUFFER;
|
||||
status = gACPI->ns_handle_to_pathname(object, &buffer);
|
||||
if (status != B_OK) {
|
||||
ERROR("pch_i2c_scan_bus_callback ns_handle_to_pathname failed\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
char* hid = NULL;
|
||||
char* cidList[8] = { NULL };
|
||||
status = gACPI->get_device_info((const char*)buffer.pointer, &hid,
|
||||
(char**)&cidList, 8);
|
||||
if (status != B_OK) {
|
||||
ERROR("pch_i2c_scan_bus_callback get_device_info failed\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
device_node* deviceNode;
|
||||
status = gI2c->register_device(bus->sim, crs.i2c_addr, hid, cidList,
|
||||
object);
|
||||
free(hid);
|
||||
for (int i = 0; cidList[i] != NULL; i++)
|
||||
free(cidList[i]);
|
||||
free(buffer.pointer);
|
||||
|
||||
TRACE("pch_i2c_scan_bus_callback registered device: %s\n", strerror(status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
scan_bus(i2c_bus_cookie cookie)
|
||||
{
|
||||
CALLED();
|
||||
pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
|
||||
if (bus->scan_bus != NULL)
|
||||
return bus->scan_bus(bus);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
init_bus(device_node* node, void** bus_cookie)
|
||||
{
|
||||
CALLED();
|
||||
status_t status = B_OK;
|
||||
|
||||
driver_module_info* driver;
|
||||
pch_i2c_sim_info* bus;
|
||||
device_node* parent = gDeviceManager->get_parent_node(node);
|
||||
gDeviceManager->get_driver(parent, &driver, (void**)&bus);
|
||||
gDeviceManager->put_node(parent);
|
||||
|
||||
TRACE_ALWAYS("init_bus() addr 0x%" B_PRIxPHYSADDR " size 0x%" B_PRIxSIZE
|
||||
" irq 0x%x\n", bus->base_addr, bus->map_size, bus->irq);
|
||||
|
||||
bus->registersArea = map_physical_memory("PCHI2C memory mapped registers",
|
||||
bus->base_addr, bus->map_size, B_ANY_KERNEL_ADDRESS,
|
||||
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
|
||||
(void **)&bus->registers);
|
||||
// init bus
|
||||
bus->capabilities = read32(bus->registers + PCH_SUP_CAPABLITIES);
|
||||
TRACE_ALWAYS("init_bus() 0x%x (0x%" B_PRIx32 ")\n",
|
||||
(bus->capabilities >> PCH_SUP_CAPABLITIES_TYPE_SHIFT)
|
||||
& PCH_SUP_CAPABLITIES_TYPE_MASK,
|
||||
bus->capabilities);
|
||||
if (((bus->capabilities >> PCH_SUP_CAPABLITIES_TYPE_SHIFT)
|
||||
& PCH_SUP_CAPABLITIES_TYPE_MASK) != 0) {
|
||||
status = B_ERROR;
|
||||
ERROR("init_bus() device type not supported\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
write32(bus->registers + PCH_SUP_RESETS, 0);
|
||||
write32(bus->registers + PCH_SUP_RESETS,
|
||||
PCH_SUP_RESETS_FUNC | PCH_SUP_RESETS_IDMA);
|
||||
|
||||
if (bus->ss_hcnt == 0)
|
||||
bus->ss_hcnt = read32(bus->registers + PCH_IC_SS_SCL_HCNT);
|
||||
if (bus->ss_lcnt == 0)
|
||||
bus->ss_lcnt = read32(bus->registers + PCH_IC_SS_SCL_LCNT);
|
||||
if (bus->fs_hcnt == 0)
|
||||
bus->fs_hcnt = read32(bus->registers + PCH_IC_FS_SCL_HCNT);
|
||||
if (bus->fs_lcnt == 0)
|
||||
bus->fs_lcnt = read32(bus->registers + PCH_IC_FS_SCL_LCNT);
|
||||
if (bus->sda_hold_time == 0)
|
||||
bus->sda_hold_time = read32(bus->registers + PCH_IC_SDA_HOLD);
|
||||
TRACE_ALWAYS("init_bus() 0x%04" B_PRIx16 " 0x%04" B_PRIx16 " 0x%04" B_PRIx16
|
||||
" 0x%04" B_PRIx16 " 0x%08" B_PRIx32 "\n", bus->ss_hcnt, bus->ss_lcnt,
|
||||
bus->fs_hcnt, bus->fs_lcnt, bus->sda_hold_time);
|
||||
|
||||
enable_device(bus, false);
|
||||
|
||||
write32(bus->registers + PCH_IC_SS_SCL_HCNT, bus->ss_hcnt);
|
||||
write32(bus->registers + PCH_IC_SS_SCL_LCNT, bus->ss_lcnt);
|
||||
write32(bus->registers + PCH_IC_FS_SCL_HCNT, bus->fs_hcnt);
|
||||
write32(bus->registers + PCH_IC_FS_SCL_LCNT, bus->fs_lcnt);
|
||||
if (bus->hs_hcnt > 0)
|
||||
write32(bus->registers + PCH_IC_HS_SCL_HCNT, bus->hs_hcnt);
|
||||
if (bus->hs_lcnt > 0)
|
||||
write32(bus->registers + PCH_IC_HS_SCL_LCNT, bus->hs_lcnt);
|
||||
{
|
||||
uint32 reg = read32(bus->registers + PCH_IC_COMP_VERSION);
|
||||
if (reg >= PCH_IC_COMP_VERSION_MIN)
|
||||
write32(bus->registers + PCH_IC_SDA_HOLD, bus->sda_hold_time);
|
||||
}
|
||||
|
||||
{
|
||||
bus->tx_fifo_depth = 32;
|
||||
bus->rx_fifo_depth = 32;
|
||||
uint32 reg = read32(bus->registers + PCH_IC_COMP_PARAM1);
|
||||
uint8 rx_fifo_depth = PCH_IC_COMP_PARAM1_RX(reg);
|
||||
uint8 tx_fifo_depth = PCH_IC_COMP_PARAM1_TX(reg);
|
||||
if (rx_fifo_depth > 1 && rx_fifo_depth < bus->rx_fifo_depth)
|
||||
bus->rx_fifo_depth = rx_fifo_depth;
|
||||
if (tx_fifo_depth > 1 && tx_fifo_depth < bus->tx_fifo_depth)
|
||||
bus->tx_fifo_depth = tx_fifo_depth;
|
||||
write32(bus->registers + PCH_IC_RX_TL, 0);
|
||||
write32(bus->registers + PCH_IC_TX_TL, bus->tx_fifo_depth / 2);
|
||||
}
|
||||
|
||||
bus->masterConfig = PCH_IC_CON_MASTER | PCH_IC_CON_SLAVE_DISABLE |
|
||||
PCH_IC_CON_RESTART_EN | PCH_IC_CON_SPEED_FAST;
|
||||
write32(bus->registers + PCH_IC_CON, bus->masterConfig);
|
||||
|
||||
write32(bus->registers + PCH_IC_INTR_MASK, 0);
|
||||
read32(bus->registers + PCH_IC_CLR_INTR);
|
||||
|
||||
status = install_io_interrupt_handler(bus->irq,
|
||||
(interrupt_handler)pch_i2c_interrupt_handler, bus, 0);
|
||||
if (status != B_OK) {
|
||||
ERROR("install interrupt handler failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
*bus_cookie = bus;
|
||||
return status;
|
||||
|
||||
err:
|
||||
if (bus->registersArea >= 0)
|
||||
delete_area(bus->registersArea);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
uninit_bus(void* bus_cookie)
|
||||
{
|
||||
pch_i2c_sim_info* bus = (pch_i2c_sim_info*)bus_cookie;
|
||||
|
||||
remove_io_interrupt_handler(bus->irq,
|
||||
(interrupt_handler)pch_i2c_interrupt_handler, bus);
|
||||
if (bus->registersArea >= 0)
|
||||
delete_area(bus->registersArea);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
module_dependency module_dependencies[] = {
|
||||
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
|
||||
{ B_ACPI_MODULE_NAME, (module_info**)&gACPI },
|
||||
{ I2C_FOR_CONTROLLER_MODULE_NAME, (module_info**)&gI2c },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
static i2c_sim_interface sPchI2cDeviceModule = {
|
||||
{
|
||||
{
|
||||
PCH_I2C_SIM_MODULE_NAME,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
NULL, // supports device
|
||||
NULL, // register device
|
||||
init_bus,
|
||||
uninit_bus,
|
||||
NULL, // register child devices
|
||||
NULL, // rescan
|
||||
NULL, // device removed
|
||||
},
|
||||
|
||||
set_sim,
|
||||
exec_command,
|
||||
scan_bus,
|
||||
};
|
||||
|
||||
|
||||
module_info* modules[] = {
|
||||
(module_info* )&gPchI2cAcpiDevice,
|
||||
(module_info* )&gPchI2cPciDevice,
|
||||
(module_info* )&sPchI2cDeviceModule,
|
||||
NULL
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, [email protected].
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _PCH_I2C_H
|
||||
#define _PCH_I2C_H
|
||||
|
||||
|
||||
#include "pch_i2c_hardware.h"
|
||||
|
||||
extern "C" {
|
||||
# include "acpi.h"
|
||||
}
|
||||
|
||||
#include <i2c.h>
|
||||
|
||||
|
||||
//#define TRACE_PCH_I2C
|
||||
#ifdef TRACE_PCH_I2C
|
||||
# define TRACE(x...) dprintf("\33[33mpch_i2c_pci:\33[0m " x)
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
#endif
|
||||
#define TRACE_ALWAYS(x...) dprintf("\33[33mpch_i2c_pci:\33[0m " x)
|
||||
#define ERROR(x...) dprintf("\33[33mpch_i2c_pci:\33[0m " x)
|
||||
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
||||
|
||||
|
||||
#define PCH_I2C_ACPI_DEVICE_MODULE_NAME "busses/i2c/pch_i2c/acpi/driver_v1"
|
||||
#define PCH_I2C_PCI_DEVICE_MODULE_NAME "busses/i2c/pch_i2c/pci/driver_v1"
|
||||
#define PCH_I2C_SIM_MODULE_NAME "busses/i2c/pch_i2c/device/v1"
|
||||
|
||||
|
||||
#define write32(address, data) \
|
||||
(*((volatile uint32*)(address)) = (data))
|
||||
#define read32(address) \
|
||||
(*((volatile uint32*)(address)))
|
||||
|
||||
|
||||
|
||||
extern device_manager_info* gDeviceManager;
|
||||
extern i2c_for_controller_interface* gI2c;
|
||||
extern acpi_module_info* gACPI;
|
||||
extern driver_module_info gPchI2cAcpiDevice;
|
||||
extern driver_module_info gPchI2cPciDevice;
|
||||
|
||||
|
||||
acpi_status pch_i2c_scan_bus_callback(acpi_handle object, uint32 nestingLevel,
|
||||
void *context, void** returnValue);
|
||||
|
||||
|
||||
struct pch_i2c_crs {
|
||||
uint16 i2c_addr;
|
||||
uint8 irq;
|
||||
uint8 irq_triggering;
|
||||
uint8 irq_polarity;
|
||||
uint8 irq_sharable;
|
||||
|
||||
uint32 addr_bas;
|
||||
uint32 addr_len;
|
||||
};
|
||||
|
||||
|
||||
typedef enum {
|
||||
PCH_I2C_IRQ_LEGACY,
|
||||
PCH_I2C_IRQ_MSI,
|
||||
PCH_I2C_IRQ_MSI_X_SHARED
|
||||
} pch_i2c_irq_type;
|
||||
|
||||
|
||||
typedef struct {
|
||||
phys_addr_t base_addr;
|
||||
size_t map_size;
|
||||
uint8 irq;
|
||||
i2c_bus sim;
|
||||
|
||||
device_node* node;
|
||||
device_node* driver_node;
|
||||
|
||||
area_id registersArea;
|
||||
addr_t registers;
|
||||
uint32 capabilities;
|
||||
|
||||
uint16 ss_hcnt;
|
||||
uint16 ss_lcnt;
|
||||
uint16 fs_hcnt;
|
||||
uint16 fs_lcnt;
|
||||
uint16 hs_hcnt;
|
||||
uint16 hs_lcnt;
|
||||
uint32 sda_hold_time;
|
||||
|
||||
uint8 tx_fifo_depth;
|
||||
uint8 rx_fifo_depth;
|
||||
|
||||
uint32 masterConfig;
|
||||
|
||||
// transfer
|
||||
int32 busy;
|
||||
bool readwait;
|
||||
bool writewait;
|
||||
i2c_op op;
|
||||
void* buffer;
|
||||
size_t length;
|
||||
uint32 flags;
|
||||
int32 error;
|
||||
|
||||
status_t (*scan_bus)(i2c_bus_cookie cookie);
|
||||
} pch_i2c_sim_info;
|
||||
|
||||
|
||||
#endif // _PCH_I2C_H
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, jerome.duval@gmail.com.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ACPI.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <condition_variable.h>
|
||||
|
||||
#include "pch_i2c.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
pch_i2c_sim_info info;
|
||||
acpi_device_module_info* acpi;
|
||||
acpi_device device;
|
||||
|
||||
} pch_i2c_acpi_sim_info;
|
||||
|
||||
|
||||
static status_t
|
||||
pch_i2c_acpi_set_powerstate(pch_i2c_acpi_sim_info* info, uint8 power)
|
||||
{
|
||||
status_t status = info->acpi->evaluate_method(info->device,
|
||||
power == 1 ? "_PS0" : "_PS3", NULL, NULL);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static acpi_status
|
||||
pch_i2c_scan_parse_callback(ACPI_RESOURCE *res, void *context)
|
||||
{
|
||||
struct pch_i2c_crs* crs = (struct pch_i2c_crs*)context;
|
||||
|
||||
if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
|
||||
crs->irq = res->Data.Irq.Interrupts[0];
|
||||
crs->irq_triggering = res->Data.Irq.Triggering;
|
||||
crs->irq_polarity = res->Data.Irq.Polarity;
|
||||
crs->irq_sharable = res->Data.Irq.Sharable;
|
||||
} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
|
||||
crs->irq = res->Data.ExtendedIrq.Interrupts[0];
|
||||
crs->irq_triggering = res->Data.ExtendedIrq.Triggering;
|
||||
crs->irq_polarity = res->Data.ExtendedIrq.Polarity;
|
||||
crs->irq_sharable = res->Data.ExtendedIrq.Sharable;
|
||||
} else if (res->Type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
|
||||
crs->addr_bas = res->Data.FixedMemory32.Address;
|
||||
crs->addr_len = res->Data.FixedMemory32.AddressLength;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
register_child_devices(void* cookie)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
pch_i2c_acpi_sim_info* bus = (pch_i2c_acpi_sim_info*)cookie;
|
||||
device_node* node = bus->info.driver_node;
|
||||
|
||||
char prettyName[25];
|
||||
sprintf(prettyName, "PCH I2C Device");
|
||||
|
||||
device_attr attrs[] = {
|
||||
// properties of this controller for i2c bus manager
|
||||
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
||||
{ string: prettyName }},
|
||||
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
|
||||
{ string: I2C_FOR_CONTROLLER_MODULE_NAME }},
|
||||
|
||||
// private data to identify the device
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
return gDeviceManager->register_node(node, PCH_I2C_SIM_MODULE_NAME,
|
||||
attrs, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
init_device(device_node* node, void** device_cookie)
|
||||
{
|
||||
CALLED();
|
||||
status_t status = B_OK;
|
||||
|
||||
pch_i2c_acpi_sim_info* bus = (pch_i2c_acpi_sim_info*)calloc(1,
|
||||
sizeof(pch_i2c_acpi_sim_info));
|
||||
if (bus == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
acpi_device_module_info* acpi;
|
||||
acpi_device device;
|
||||
{
|
||||
device_node* acpiParent = gDeviceManager->get_parent_node(node);
|
||||
gDeviceManager->get_driver(acpiParent, (driver_module_info**)&acpi,
|
||||
(void**)&device);
|
||||
gDeviceManager->put_node(acpiParent);
|
||||
}
|
||||
|
||||
bus->acpi = acpi;
|
||||
bus->device = device;
|
||||
bus->info.driver_node = node;
|
||||
//bus->info.scan_bus = acpi_scan_bus;
|
||||
|
||||
// Attach devices for I2C resources
|
||||
struct pch_i2c_crs crs;
|
||||
status = acpi->walk_resources(device, (ACPI_STRING)"_CRS",
|
||||
pch_i2c_scan_parse_callback, &crs);
|
||||
if (status != B_OK) {
|
||||
ERROR("Error while getting I2C devices\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
bus->info.base_addr = crs.addr_bas;
|
||||
bus->info.map_size = crs.addr_len;
|
||||
bus->info.irq = crs.irq;
|
||||
|
||||
pch_i2c_acpi_set_powerstate(bus, 1);
|
||||
|
||||
*device_cookie = bus;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
uninit_device(void* device_cookie)
|
||||
{
|
||||
pch_i2c_acpi_sim_info* bus = (pch_i2c_acpi_sim_info*)device_cookie;
|
||||
free(bus);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
register_device(device_node* parent)
|
||||
{
|
||||
device_attr attrs[] = {
|
||||
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "PCH I2C ACPI"}},
|
||||
{}
|
||||
};
|
||||
|
||||
return gDeviceManager->register_node(parent,
|
||||
PCH_I2C_ACPI_DEVICE_MODULE_NAME, attrs, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static float
|
||||
supports_device(device_node* parent)
|
||||
{
|
||||
CALLED();
|
||||
const char* bus;
|
||||
|
||||
// make sure parent is a PCH I2C ACPI device node
|
||||
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
|
||||
< B_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(bus, "acpi") != 0)
|
||||
return 0.0f;
|
||||
|
||||
TRACE("found an acpi node\n");
|
||||
|
||||
// check whether it's really a device
|
||||
uint32 device_type;
|
||||
if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
|
||||
&device_type, false) != B_OK
|
||||
|| device_type != ACPI_TYPE_DEVICE) {
|
||||
return 0.0;
|
||||
}
|
||||
TRACE("found an acpi device\n");
|
||||
|
||||
// check whether it's a PCH I2C device
|
||||
const char *name;
|
||||
if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name,
|
||||
false) != B_OK) {
|
||||
return 0.0;
|
||||
}
|
||||
TRACE("found an acpi device hid %s\n", name);
|
||||
|
||||
if (strcmp(name, "INT3442") == 0
|
||||
|| strcmp(name, "INT3443") == 0
|
||||
|| strcmp(name, "INT3444") == 0
|
||||
|| strcmp(name, "INT3445") == 0
|
||||
|| strcmp(name, "INT3446") == 0
|
||||
|| strcmp(name, "INT3447") == 0
|
||||
|| strcmp(name, "80860AAC") == 0
|
||||
|| strcmp(name, "80865AAC") == 0) {
|
||||
TRACE("PCH I2C device found! name %s\n", name);
|
||||
return 0.6f;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
driver_module_info gPchI2cAcpiDevice = {
|
||||
{
|
||||
PCH_I2C_ACPI_DEVICE_MODULE_NAME,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
supports_device,
|
||||
register_device,
|
||||
init_device,
|
||||
uninit_device,
|
||||
register_child_devices,
|
||||
NULL, // rescan
|
||||
NULL, // device removed
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, jerome.duval@gmail.com.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _PCH_I2C_HARDWARE_H
|
||||
#define _PCH_I2C_HARDWARE_H
|
||||
|
||||
|
||||
#define PCH_IC_CON 0x00
|
||||
#define PCH_IC_CON_MASTER 0x1
|
||||
#define PCH_IC_CON_SPEED_STD 0x2
|
||||
#define PCH_IC_CON_SPEED_FAST 0x4
|
||||
#define PCH_IC_CON_SPEED_HIGH 0x6
|
||||
#define PCH_IC_CON_10BIT_ADDR_MASTER 0x10
|
||||
#define PCH_IC_CON_RESTART_EN 0x20
|
||||
#define PCH_IC_CON_SLAVE_DISABLE 0x40
|
||||
#define PCH_IC_CON_TX_EMPTY_CTRL 0x100
|
||||
#define PCH_IC_TAR 0x04
|
||||
#define PCH_IC_HS_MADDR 0x08
|
||||
#define PCH_IC_DATA_CMD 0x10
|
||||
#define PCH_IC_DATA_CMD_READ (1 << 8)
|
||||
#define PCH_IC_DATA_CMD_STOP (1 << 9)
|
||||
#define PCH_IC_DATA_CMD_RESTART (1 << 10)
|
||||
#define PCH_IC_SS_SCL_HCNT 0x14
|
||||
#define PCH_IC_SS_SCL_LCNT 0x18
|
||||
#define PCH_IC_FS_SCL_HCNT 0x1c
|
||||
#define PCH_IC_FS_SCL_LCNT 0x20
|
||||
#define PCH_IC_HS_SCL_HCNT 0x24
|
||||
#define PCH_IC_HS_SCL_LCNT 0x28
|
||||
#define PCH_IC_INTR_STAT 0x2c
|
||||
#define PCH_IC_INTR_STAT_RX_UNDER (1 << 0)
|
||||
#define PCH_IC_INTR_STAT_RX_OVER (1 << 1)
|
||||
#define PCH_IC_INTR_STAT_RX_FULL (1 << 2)
|
||||
#define PCH_IC_INTR_STAT_TX_OVER (1 << 3)
|
||||
#define PCH_IC_INTR_STAT_TX_EMPTY (1 << 4)
|
||||
#define PCH_IC_INTR_STAT_RD_REQ (1 << 5)
|
||||
#define PCH_IC_INTR_STAT_TX_ABRT (1 << 6)
|
||||
#define PCH_IC_INTR_STAT_RX_DONE (1 << 7)
|
||||
#define PCH_IC_INTR_STAT_ACTIVITY (1 << 8)
|
||||
#define PCH_IC_INTR_STAT_STOP_DET (1 << 9)
|
||||
#define PCH_IC_INTR_STAT_START_DET (1 << 10)
|
||||
#define PCH_IC_INTR_STAT_GEN_CALL (1 << 11)
|
||||
#define PCH_IC_INTR_STAT_MST_ON_HOLD (1 << 13)
|
||||
|
||||
#define PCH_IC_INTR_MASK 0x30
|
||||
#define PCH_IC_RAW_INTR_STAT 0x34
|
||||
#define PCH_IC_RX_TL 0x38
|
||||
#define PCH_IC_TX_TL 0x3c
|
||||
#define PCH_IC_CLR_INTR 0x40
|
||||
#define PCH_IC_CLR_RX_UNDER 0x44
|
||||
#define PCH_IC_CLR_RX_OVER 0x48
|
||||
#define PCH_IC_CLR_TX_OVER 0x4c
|
||||
#define PCH_IC_CLR_RD_REQ 0x50
|
||||
#define PCH_IC_CLR_TX_ABRT 0x54
|
||||
#define PCH_IC_CLR_RX_DONE 0x58
|
||||
#define PCH_IC_CLR_ACTIVITY 0x5c
|
||||
#define PCH_IC_CLR_STOP_DET 0x60
|
||||
#define PCH_IC_CLR_START_DET 0x64
|
||||
#define PCH_IC_CLR_GEN_CALL 0x68
|
||||
#define PCH_IC_ENABLE 0x6c
|
||||
#define PCH_IC_STATUS 0x70
|
||||
#define PCH_IC_STATUS_ACTIVITY 0x1
|
||||
#define PCH_IC_TXFLR 0x74
|
||||
#define PCH_IC_RXFLR 0x78
|
||||
#define PCH_IC_SDA_HOLD 0x7c
|
||||
#define PCH_IC_TX_ABRT_SOURCE 0x80
|
||||
#define PCH_IC_DMA_CR 0x88
|
||||
#define PCH_IC_DMA_TDLR 0x8c
|
||||
#define PCH_IC_DMA_RDLR 0x90
|
||||
#define PCH_IC_ACK_GENERAL_CALL 0x98
|
||||
#define PCH_IC_ENABLE_STATUS 0x9c
|
||||
#define PCH_IC_FS_SPKLEN 0xa0
|
||||
#define PCH_IC_CLR_RESTRART_DET 0xa8
|
||||
#define PCH_IC_COMP_PARAM1 0xf4
|
||||
#define PCH_IC_COMP_PARAM1_RX(x) (1 + (((x) >> 8) & 0xff))
|
||||
#define PCH_IC_COMP_PARAM1_TX(x) (1 + (((x) >> 16) & 0xff))
|
||||
#define PCH_IC_COMP_VERSION 0xf8
|
||||
#define PCH_IC_COMP_VERSION_MIN 0x3131312a
|
||||
|
||||
#define PCH_SUP_RESETS 0x204
|
||||
#define PCH_SUP_RESETS_FUNC 0x3
|
||||
#define PCH_SUP_RESETS_IDMA 0x4
|
||||
#define PCH_SUP_ACTIVELTR_VALUE 0x210
|
||||
#define PCH_SUP_IDLELTR_VALUE 0x214
|
||||
#define PCH_SUP_TX_ACK_COUNT 0x218
|
||||
#define PCH_SUP_RX_BYTE_COUNT 0x21c
|
||||
#define PCH_SUP_TX_COMPLETE_INTR_STAT 0x220
|
||||
#define PCH_SUP_TX_COMPLETE_INTR_CLR 0x224
|
||||
#define PCH_SUP_SW_SCRATCH_0 0x228
|
||||
#define PCH_SUP_SW_SCRATCH_1 0x22c
|
||||
#define PCH_SUP_SW_SCRATCH_2 0x230
|
||||
#define PCH_SUP_SW_SCRATCH_3 0x234
|
||||
#define PCH_SUP_CLOCK_GATE 0x238
|
||||
#define PCH_SUP_REMAP_ADDR_LO 0x240
|
||||
#define PCH_SUP_REMAP_ADDR_HI 0x244
|
||||
#define PCH_SUP_DEVIDLE_CONTROL 0x24c
|
||||
#define PCH_SUP_CAPABLITIES 0x2fc
|
||||
#define PCH_SUP_CAPABLITIES_TYPE_MASK 0xf
|
||||
#define PCH_SUP_CAPABLITIES_TYPE_SHIFT 4
|
||||
|
||||
|
||||
#endif // _PCH_I2C_HARDWARE_H
|
||||
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, jerome.duval@gmail.com.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ACPI.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <condition_variable.h>
|
||||
#include <bus/PCI.h>
|
||||
#include <PCI_x86.h>
|
||||
|
||||
|
||||
#include "pch_i2c.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
pch_i2c_sim_info info;
|
||||
pci_device_module_info* pci;
|
||||
pci_device* device;
|
||||
pch_i2c_irq_type irq_type;
|
||||
|
||||
pci_info pciinfo;
|
||||
} pch_i2c_pci_sim_info;
|
||||
|
||||
|
||||
static pci_x86_module_info* sPCIx86Module;
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
pci_scan_bus(i2c_bus_cookie cookie)
|
||||
{
|
||||
CALLED();
|
||||
pch_i2c_pci_sim_info* bus = (pch_i2c_pci_sim_info*)cookie;
|
||||
device_node *acpiNode = NULL;
|
||||
|
||||
pci_info *pciInfo = &bus->pciinfo;
|
||||
|
||||
// search ACPI I2C nodes for this device
|
||||
{
|
||||
device_node* deviceRoot = gDeviceManager->get_root_node();
|
||||
uint32 addr = (pciInfo->device << 16) | pciInfo->function;
|
||||
device_attr acpiAttrs[] = {
|
||||
{ B_DEVICE_BUS, B_STRING_TYPE, { string: "acpi" }},
|
||||
{ ACPI_DEVICE_ADDR_ITEM, B_UINT32_TYPE, {ui32: addr}},
|
||||
{ NULL }
|
||||
};
|
||||
if (addr != 0 && gDeviceManager->find_child_node(deviceRoot, acpiAttrs,
|
||||
&acpiNode) != B_OK) {
|
||||
ERROR("init_bus() acpi device not found\n");
|
||||
return B_DEV_CONFIGURATION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("init_bus() find_child_node() found %x %x %p\n",
|
||||
pciInfo->device, pciInfo->function, acpiNode);
|
||||
// TODO eventually check timings on acpi
|
||||
acpi_device_module_info *acpi;
|
||||
acpi_device acpiDevice;
|
||||
if (gDeviceManager->get_driver(acpiNode, (driver_module_info **)&acpi,
|
||||
(void **)&acpiDevice) == B_OK) {
|
||||
// find out I2C device nodes
|
||||
acpi->walk_namespace(acpiDevice, ACPI_TYPE_DEVICE, 1,
|
||||
pch_i2c_scan_bus_callback, NULL, bus, NULL);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
register_child_devices(void* cookie)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
pch_i2c_pci_sim_info* bus = (pch_i2c_pci_sim_info*)cookie;
|
||||
device_node* node = bus->info.driver_node;
|
||||
|
||||
char prettyName[25];
|
||||
sprintf(prettyName, "PCH I2C Device %" B_PRIu16, 0);
|
||||
|
||||
device_attr attrs[] = {
|
||||
// properties of this controller for i2c bus manager
|
||||
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
||||
{ string: prettyName }},
|
||||
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
|
||||
{ string: I2C_FOR_CONTROLLER_MODULE_NAME }},
|
||||
|
||||
// private data to identify the device
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
return gDeviceManager->register_node(node, PCH_I2C_SIM_MODULE_NAME,
|
||||
attrs, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
init_device(device_node* node, void** device_cookie)
|
||||
{
|
||||
CALLED();
|
||||
status_t status = B_OK;
|
||||
|
||||
pch_i2c_pci_sim_info* bus = (pch_i2c_pci_sim_info*)calloc(1,
|
||||
sizeof(pch_i2c_pci_sim_info));
|
||||
if (bus == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
pci_device_module_info* pci;
|
||||
pci_device* device;
|
||||
{
|
||||
device_node* pciParent = gDeviceManager->get_parent_node(node);
|
||||
gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci,
|
||||
(void**)&device);
|
||||
gDeviceManager->put_node(pciParent);
|
||||
}
|
||||
|
||||
bus->pci = pci;
|
||||
bus->device = device;
|
||||
bus->info.driver_node = node;
|
||||
bus->info.scan_bus = pci_scan_bus;
|
||||
|
||||
pci_info *pciInfo = &bus->pciinfo;
|
||||
pci->get_pci_info(device, pciInfo);
|
||||
|
||||
bus->info.base_addr = pciInfo->u.h0.base_registers[0];
|
||||
bus->info.base_addr &= PCI_address_memory_32_mask;
|
||||
if ((pciInfo->u.h0.base_register_flags[0] & 0xc) == PCI_address_type_64)
|
||||
bus->info.base_addr += (phys_addr_t)pciInfo->u.h0.base_registers[1] << 32;
|
||||
bus->info.map_size = pciInfo->u.h0.base_register_sizes[0];
|
||||
|
||||
// enable power
|
||||
pci->set_powerstate(device, PCI_pm_state_d0);
|
||||
|
||||
// enable bus master and memory
|
||||
uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
|
||||
pcicmd |= PCI_command_master | PCI_command_memory;
|
||||
pci->write_pci_config(device, PCI_command, 2, pcicmd);
|
||||
|
||||
if (get_module(B_PCI_X86_MODULE_NAME, (module_info**)&sPCIx86Module)
|
||||
!= B_OK) {
|
||||
sPCIx86Module = NULL;
|
||||
}
|
||||
|
||||
if (sPCIx86Module != NULL) {
|
||||
// try MSI-X
|
||||
uint8 msixCount = sPCIx86Module->get_msix_count(
|
||||
pciInfo->bus, pciInfo->device, pciInfo->function);
|
||||
if (msixCount >= 1) {
|
||||
uint8 vector;
|
||||
if (sPCIx86Module->configure_msix(pciInfo->bus, pciInfo->device,
|
||||
pciInfo->function, 1, &vector) == B_OK
|
||||
&& sPCIx86Module->enable_msix(pciInfo->bus, pciInfo->device,
|
||||
pciInfo->function) == B_OK) {
|
||||
TRACE_ALWAYS("using MSI-X vector %u\n", vector);
|
||||
bus->info.irq = vector;
|
||||
bus->irq_type = PCH_I2C_IRQ_MSI_X_SHARED;
|
||||
} else {
|
||||
ERROR("couldn't use MSI-X SHARED\n");
|
||||
}
|
||||
} else if (sPCIx86Module->get_msi_count(
|
||||
pciInfo->bus, pciInfo->device, pciInfo->function) >= 1) {
|
||||
// try MSI
|
||||
uint8 vector;
|
||||
if (sPCIx86Module->configure_msi(pciInfo->bus, pciInfo->device,
|
||||
pciInfo->function, 1, &vector) == B_OK
|
||||
&& sPCIx86Module->enable_msi(pciInfo->bus, pciInfo->device,
|
||||
pciInfo->function) == B_OK) {
|
||||
TRACE_ALWAYS("using MSI vector %u\n", vector);
|
||||
bus->info.irq = vector;
|
||||
bus->irq_type = PCH_I2C_IRQ_MSI;
|
||||
} else {
|
||||
ERROR("couldn't use MSI\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bus->irq_type == PCH_I2C_IRQ_LEGACY) {
|
||||
bus->info.irq = pciInfo->u.h0.interrupt_line;
|
||||
TRACE_ALWAYS("using legacy interrupt %u\n", bus->info.irq);
|
||||
}
|
||||
if (bus->info.irq == 0 || bus->info.irq == 0xff) {
|
||||
ERROR("PCI IRQ not assigned\n");
|
||||
status = B_ERROR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
*device_cookie = bus;
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
free(bus);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
uninit_device(void* device_cookie)
|
||||
{
|
||||
pch_i2c_pci_sim_info* bus = (pch_i2c_pci_sim_info*)device_cookie;
|
||||
if (bus->irq_type != PCH_I2C_IRQ_LEGACY) {
|
||||
if (sPCIx86Module != NULL) {
|
||||
sPCIx86Module->disable_msi(bus->pciinfo.bus,
|
||||
bus->pciinfo.device, bus->pciinfo.function);
|
||||
sPCIx86Module->unconfigure_msi(bus->pciinfo.bus,
|
||||
bus->pciinfo.device, bus->pciinfo.function);
|
||||
}
|
||||
}
|
||||
if (sPCIx86Module != NULL) {
|
||||
put_module(B_PCI_X86_MODULE_NAME);
|
||||
sPCIx86Module = NULL;
|
||||
}
|
||||
free(bus);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
register_device(device_node* parent)
|
||||
{
|
||||
device_attr attrs[] = {
|
||||
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "PCH I2C PCI"}},
|
||||
{}
|
||||
};
|
||||
|
||||
return gDeviceManager->register_node(parent,
|
||||
PCH_I2C_PCI_DEVICE_MODULE_NAME, attrs, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static float
|
||||
supports_device(device_node* parent)
|
||||
{
|
||||
CALLED();
|
||||
const char* bus;
|
||||
uint16 vendorID, deviceID;
|
||||
|
||||
// make sure parent is a PCH I2C PCI device node
|
||||
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
|
||||
< B_OK || gDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID,
|
||||
&vendorID, false) < B_OK
|
||||
|| gDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, &deviceID,
|
||||
false) < B_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(bus, "pci") != 0)
|
||||
return 0.0f;
|
||||
|
||||
if (vendorID == 0x8086) {
|
||||
switch (deviceID) {
|
||||
case 0x02c5:
|
||||
case 0x02c6:
|
||||
case 0x02e8:
|
||||
case 0x02e9:
|
||||
case 0x02ea:
|
||||
case 0x02eb:
|
||||
case 0x06e8:
|
||||
case 0x06e9:
|
||||
case 0x06ea:
|
||||
case 0x06eb:
|
||||
case 0x0aac:
|
||||
case 0x0aae:
|
||||
case 0x0ab0:
|
||||
case 0x0ab2:
|
||||
case 0x0ab4:
|
||||
case 0x0ab6:
|
||||
case 0x0ab8:
|
||||
case 0x0aba:
|
||||
case 0x1aac:
|
||||
case 0x1aae:
|
||||
|
||||
case 0x1ab0:
|
||||
case 0x1ab2:
|
||||
case 0x1ab4:
|
||||
case 0x1ab6:
|
||||
case 0x1ab8:
|
||||
case 0x1aba:
|
||||
|
||||
case 0x31ac:
|
||||
case 0x31ae:
|
||||
case 0x31b0:
|
||||
case 0x31b2:
|
||||
case 0x31b4:
|
||||
case 0x31b6:
|
||||
case 0x31b8:
|
||||
case 0x31ba:
|
||||
|
||||
case 0x34c5:
|
||||
case 0x34c6:
|
||||
case 0x34e8:
|
||||
case 0x34e9:
|
||||
case 0x34ea:
|
||||
case 0x34eb:
|
||||
|
||||
case 0x4b44:
|
||||
case 0x4b45:
|
||||
case 0x4b4b:
|
||||
case 0x4b4c:
|
||||
case 0x4b78:
|
||||
case 0x4b79:
|
||||
case 0x4b7a:
|
||||
case 0x4b7b:
|
||||
|
||||
case 0x4dc5:
|
||||
case 0x4dc6:
|
||||
case 0x4de8:
|
||||
case 0x4de9:
|
||||
case 0x4dea:
|
||||
case 0x4deb:
|
||||
|
||||
case 0x5aac:
|
||||
case 0x5aae:
|
||||
case 0x5ab0:
|
||||
case 0x5ab2:
|
||||
case 0x5ab4:
|
||||
case 0x5ab6:
|
||||
case 0x5ab8:
|
||||
case 0x5aba:
|
||||
|
||||
case 0x9d60:
|
||||
case 0x9d61:
|
||||
case 0x9d62:
|
||||
case 0x9d63:
|
||||
case 0x9d64:
|
||||
case 0x9d65:
|
||||
|
||||
case 0x9dc5:
|
||||
case 0x9dc6:
|
||||
case 0x9de8:
|
||||
case 0x9de9:
|
||||
case 0x9dea:
|
||||
case 0x9deb:
|
||||
|
||||
case 0xa0c5:
|
||||
case 0xa0c6:
|
||||
case 0xa0d8:
|
||||
case 0xa0d9:
|
||||
case 0xa0e8:
|
||||
case 0xa0e9:
|
||||
case 0xa0ea:
|
||||
case 0xa0eb:
|
||||
|
||||
case 0xa160:
|
||||
case 0xa161:
|
||||
case 0xa162:
|
||||
|
||||
case 0xa2e0:
|
||||
case 0xa2e1:
|
||||
case 0xa2e2:
|
||||
case 0xa2e3:
|
||||
|
||||
case 0xa368:
|
||||
case 0xa369:
|
||||
case 0xa36a:
|
||||
case 0xa36b:
|
||||
break;
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
pci_device_module_info* pci;
|
||||
pci_device* device;
|
||||
gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
|
||||
(void**)&device);
|
||||
uint8 pciSubDeviceId = pci->read_pci_config(device, PCI_revision,
|
||||
1);
|
||||
|
||||
TRACE("PCH I2C device found! vendor 0x%04x, device 0x%04x\n", vendorID,
|
||||
deviceID);
|
||||
return 0.8f;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
driver_module_info gPchI2cPciDevice = {
|
||||
{
|
||||
PCH_I2C_PCI_DEVICE_MODULE_NAME,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
supports_device,
|
||||
register_device,
|
||||
init_device,
|
||||
uninit_device,
|
||||
register_child_devices,
|
||||
NULL, // rescan
|
||||
NULL, // device removed
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user