#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/errno.h>
#include <sys/lock.h>
#include <sys/bus.h>
#include "opt_acpi.h"
#include "acpi.h"
#include <dev/acpica/acpivar.h>
#include <contrib/dev/acpica/source/include/amlcode.h>
#include <bus/smbus/smbconf.h>
#include "smbus_if.h"
struct gsb_buffer {
UINT8 status;
UINT8 len;
UINT8 data[];
} __packed;
struct acpi_i2c_handler_data {
struct acpi_connection_info info;
device_t dev;
};
struct smbus_acpi_softc {
device_t dev;
device_t parent;
struct acpi_i2c_handler_data space_handler_data;
};
static int smbus_acpi_probe(device_t dev);
static int smbus_acpi_attach(device_t dev);
static int smbus_acpi_detach(device_t dev);
static void smbus_acpi_install_address_space_handler(
struct smbus_acpi_softc *sc);
static void smbus_acpi_remove_address_space_handler(
struct smbus_acpi_softc *sc);
static ACPI_STATUS smbus_acpi_space_handler(UINT32 Function,
ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
UINT64 *Value, void *HandlerContext,
void *RegionContext);
static void
smbus_acpi_install_address_space_handler(struct smbus_acpi_softc *sc)
{
ACPI_HANDLE handle;
ACPI_STATUS s;
handle = acpi_get_handle(sc->parent);
sc->space_handler_data.dev = sc->parent;
s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
&smbus_acpi_space_handler, NULL, &sc->space_handler_data);
if (ACPI_FAILURE(s)) {
device_printf(sc->dev,
"Failed to install GSBUS Address Space Handler in ACPI\n");
}
}
static void
smbus_acpi_remove_address_space_handler(struct smbus_acpi_softc *sc)
{
ACPI_HANDLE handle;
ACPI_STATUS s;
handle = acpi_get_handle(sc->parent);
s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
&smbus_acpi_space_handler);
if (ACPI_FAILURE(s)) {
device_printf(sc->dev,
"Failed to remove GSBUS Address Space Handler from ACPI\n");
}
}
static ACPI_STATUS
smbus_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
{
struct gsb_buffer *gsb = (struct gsb_buffer *)Value;
struct acpi_i2c_handler_data *data = HandlerContext;
device_t dev = data->dev;
struct acpi_connection_info *info = &data->info;
struct acpi_resource_i2c_serialbus *sb;
ACPI_RESOURCE *Resource;
UINT32 accessor_type = Function >> 16;
UINT8 action = Function & ACPI_IO_MASK;
ACPI_STATUS s = AE_OK;
int cnt, val = 0;
uint16_t *wdata;
short word;
char byte;
char buf[32];
u_char count;
if (Value == NULL)
return (AE_BAD_PARAMETER);
s = AcpiBufferToResource(info->Connection, info->Length,
&Resource);
if (ACPI_FAILURE(s))
return s;
if (Resource->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
s = AE_BAD_PARAMETER;
goto err;
}
sb = &Resource->Data.I2cSerialBus;
if (sb->Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
s = AE_BAD_PARAMETER;
goto err;
}
if (sb->AccessMode == ACPI_I2C_10BIT_MODE) {
s = AE_BAD_PARAMETER;
goto err;
}
switch (accessor_type) {
case AML_FIELD_ATTRIB_SEND_RECEIVE:
if (action == ACPI_READ) {
val = SMBUS_RECVB(dev, sb->SlaveAddress, &byte);
if (val == 0)
gsb->data[0] = byte;
} else {
val = SMBUS_SENDB(dev, sb->SlaveAddress,
gsb->data[0]);
}
break;
case AML_FIELD_ATTRIB_BYTE:
if (action == ACPI_READ) {
val = SMBUS_READB(dev, sb->SlaveAddress, Address,
&byte);
if (val == 0)
gsb->data[0] = byte;
} else {
val = SMBUS_WRITEB(dev, sb->SlaveAddress, Address,
gsb->data[0]);
}
break;
case AML_FIELD_ATTRIB_WORD:
wdata = (uint16_t *)gsb->data;
if (action == ACPI_READ) {
val = SMBUS_READW(dev, sb->SlaveAddress, Address,
&word);
if (val == 0)
wdata[0] = word;
} else {
val = SMBUS_WRITEW(dev, sb->SlaveAddress, Address,
wdata[0]);
}
break;
case AML_FIELD_ATTRIB_BLOCK:
if (action == ACPI_READ) {
count = 32;
val = SMBUS_BREAD(dev, sb->SlaveAddress, Address,
&count, buf);
if (val == 0) {
gsb->len = count;
memcpy(gsb->data, buf, count);
}
} else {
memcpy(buf, gsb->data, gsb->len);
count = gsb->len;
val = SMBUS_BWRITE(dev, sb->SlaveAddress, Address,
count, buf);
}
break;
case AML_FIELD_ATTRIB_BYTES:
if (action == ACPI_READ) {
cnt = 0;
val = SMBUS_TRANS(dev, sb->SlaveAddress, Address,
SMB_TRANS_NOCNT | SMB_TRANS_7BIT, NULL, 0,
buf, info->AccessLength, &cnt);
if (val == 0)
memcpy(gsb->data, buf, cnt);
} else {
memcpy(buf, gsb->data, info->AccessLength);
val = SMBUS_TRANS(dev, sb->SlaveAddress, Address,
SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
buf, info->AccessLength, NULL, 0, NULL);
}
break;
default:
device_printf(dev, "protocol(0x%02x) is not supported.\n",
accessor_type);
s = AE_BAD_PARAMETER;
goto err;
}
gsb->status = val;
err:
ACPI_FREE(Resource);
return (s);
}
static int
smbus_acpi_probe(device_t dev)
{
if (acpi_get_handle(device_get_parent(dev)) == NULL)
return (ENXIO);
device_set_desc(dev, "ACPI I2cSerialBus backend");
return (0);
}
static int
smbus_acpi_attach(device_t dev)
{
struct smbus_acpi_softc *sc = device_get_softc(dev);
sc->dev = dev;
sc->parent = device_get_parent(dev);
smbus_acpi_install_address_space_handler(sc);
return (0);
}
static int
smbus_acpi_detach(device_t dev)
{
struct smbus_acpi_softc *sc = device_get_softc(dev);
smbus_acpi_remove_address_space_handler(sc);
return (0);
}
static device_method_t smbacpi_methods[] = {
DEVMETHOD(device_probe, smbus_acpi_probe),
DEVMETHOD(device_attach, smbus_acpi_attach),
DEVMETHOD(device_detach, smbus_acpi_detach),
DEVMETHOD_END
};
static driver_t smbacpi_driver = {
"smbacpi",
smbacpi_methods,
sizeof(struct smbus_acpi_softc)
};
static devclass_t smbacpi_devclass;
DRIVER_MODULE(smbacpi, ig4iic, smbacpi_driver, smbacpi_devclass,
NULL, NULL);
MODULE_DEPEND(smbacpi, acpi, 1, 1, 1);
MODULE_DEPEND(smbacpi, smbus, 1, 1, 1);
MODULE_VERSION(smbacpi, 1);