The diff below implements functionality that allows AML access to devices that sit on an I2C bus. Only a subset of the various access methods is implemented; some of the missing ones are not a very good fit for our AML implementation. But this is enough to make reading the battery status on the little Lenovo that mlarkin@ handed me at Elk Lakes work.
Probably needs some wider testing. After that's done, ok? Index: dev/acpi/acpi.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpi.c,v retrieving revision 1.341 diff -u -p -r1.341 acpi.c --- dev/acpi/acpi.c 27 Mar 2018 21:11:16 -0000 1.341 +++ dev/acpi/acpi.c 13 May 2018 13:49:46 -0000 @@ -920,6 +920,23 @@ acpi_register_gpio(struct acpi_softc *sc } void +acpi_register_gsb(struct acpi_softc *sc, struct aml_node *devnode) +{ + struct aml_value arg[2]; + struct aml_node *node; + + /* Register GenericSerialBus address space. */ + memset(&arg, 0, sizeof(arg)); + arg[0].type = AML_OBJTYPE_INTEGER; + arg[0].v_integer = ACPI_OPREG_GSB; + arg[1].type = AML_OBJTYPE_INTEGER; + arg[1].v_integer = 1; + node = aml_searchname(devnode, "_REG"); + if (node && aml_evalnode(sc, node, 2, arg, NULL)) + printf("%s: _REG failed\n", node->name); +} + +void acpi_attach(struct device *parent, struct device *self, void *aux) { struct bios_attach_args *ba = aux; Index: dev/acpi/acpivar.h =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpivar.h,v retrieving revision 1.89 diff -u -p -r1.89 acpivar.h --- dev/acpi/acpivar.h 29 Nov 2017 22:51:01 -0000 1.89 +++ dev/acpi/acpivar.h 13 May 2018 13:49:46 -0000 @@ -333,6 +333,7 @@ void acpi_wakeup(void *); int acpi_gasio(struct acpi_softc *, int, int, uint64_t, int, int, void *); void acpi_register_gpio(struct acpi_softc *, struct aml_node *); +void acpi_register_gsb(struct acpi_softc *, struct aml_node *); int acpi_set_gpehandler(struct acpi_softc *, int, int (*)(struct acpi_softc *, int, void *), void *, int); Index: dev/acpi/amltypes.h =================================================================== RCS file: /cvs/src/sys/dev/acpi/amltypes.h,v retrieving revision 1.45 diff -u -p -r1.45 amltypes.h --- dev/acpi/amltypes.h 8 May 2016 11:08:01 -0000 1.45 +++ dev/acpi/amltypes.h 13 May 2018 13:49:46 -0000 @@ -371,6 +371,8 @@ struct acpi_gpio { void (*intr_establish)(void *, int, int, int (*)(void *), void *); }; +struct i2c_controller; + struct aml_node { struct aml_node *parent; @@ -385,8 +387,9 @@ struct aml_node { u_int8_t *end; struct aml_value *value; - struct acpi_pci *pci; + struct acpi_pci *pci; struct acpi_gpio *gpio; + struct i2c_controller *i2c; }; #define aml_bitmask(n) (1L << ((n) & 0x7)) Index: dev/acpi/dsdt.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/dsdt.c,v retrieving revision 1.236 diff -u -p -r1.236 dsdt.c --- dev/acpi/dsdt.c 29 Nov 2017 15:22:22 -0000 1.236 +++ dev/acpi/dsdt.c 13 May 2018 13:49:47 -0000 @@ -33,6 +33,8 @@ #include <dev/acpi/amltypes.h> #include <dev/acpi/dsdt.h> +#include <dev/i2c/i2cvar.h> + #ifdef SMALL_KERNEL #undef ACPI_DEBUG #endif @@ -2288,6 +2290,7 @@ aml_register_regionspace(struct aml_node void aml_rwgen(struct aml_value *, int, int, struct aml_value *, int, int); void aml_rwgpio(struct aml_value *, int, int, struct aml_value *, int, int); +void aml_rwgsb(struct aml_value *, int, int, struct aml_value *, int, int); void aml_rwindexfield(struct aml_value *, struct aml_value *val, int); void aml_rwfield(struct aml_value *, int, int, struct aml_value *, int); @@ -2512,6 +2515,96 @@ aml_rwgpio(struct aml_value *conn, int b } void +aml_rwgsb(struct aml_value *conn, int bpos, int blen, struct aml_value *val, + int mode, int flag) +{ + union acpi_resource *crs = (union acpi_resource *)conn->v_buffer; + struct aml_node *node; + i2c_tag_t tag; + i2c_op_t op; + i2c_addr_t addr; + int cmdlen, buflen; + uint8_t cmd; + uint8_t *buf; + int err; + + if (conn->type != AML_OBJTYPE_BUFFER || conn->length < 5 || + AML_CRSTYPE(crs) != LR_SERBUS || AML_CRSLEN(crs) > conn->length || + crs->lr_i2cbus.revid != 1 || crs->lr_i2cbus.type != LR_SERBUS_I2C) + aml_die("Invalid GenericSerialBus"); + if (AML_FIELD_ACCESS(flag) != AML_FIELD_BUFFERACC || + bpos & 0x3 || blen != 8) + aml_die("Invalid GenericSerialBus access"); + + node = aml_searchname(conn->node, + (char *)&crs->lr_i2cbus.vdata[crs->lr_i2cbus.tlength - 6]); + + if (node == NULL || node->i2c == NULL) + aml_die("Could not find GenericSerialBus controller"); + + switch (((flag >> 6) & 0x3)) { + case 0: /* Normal */ + switch (AML_FIELD_ATTR(flag)) { + case 0x02: /* AttribQuick */ + cmdlen = 0; + buflen = 0; + break; + case 0x04: /* AttribSendReceive */ + cmdlen = 0; + buflen = 1; + break; + case 0x06: /* AttribByte */ + cmdlen = 1; + buflen = 1; + break; + case 0x08: /* AttribWord */ + cmdlen = 1; + buflen = 2; + break; + default: + aml_die("unsupported access type 0x%x", flag); + break; + } + break; + case 1: /* AttribBytes */ + cmdlen = 1; + buflen = AML_FIELD_ATTR(flag); + break; + case 2: /* AttribRawBytes */ + cmdlen = 0; + buflen = AML_FIELD_ATTR(flag); + break; + default: + aml_die("unsupported access type 0x%x", flag); + break; + } + + if (mode == ACPI_IOREAD) { + _aml_setvalue(val, AML_OBJTYPE_BUFFER, buflen + 2, NULL); + op = I2C_OP_READ_WITH_STOP; + } else { + op = I2C_OP_WRITE_WITH_STOP; + } + + tag = node->i2c; + addr = crs->lr_i2cbus._adr; + cmd = bpos >> 3; + buf = val->v_buffer; + + iic_acquire_bus(tag, 0); + err = iic_exec(tag, op, addr, &cmd, cmdlen, &buf[2], buflen, 0); + iic_release_bus(tag, 0); + + /* + * The ACPI specification doesn't tell us what the status + * codes mean beyond implying that zero means success. So use + * the error returned from the transfer. All possible error + * numbers should fit in a single bit. + */ + buf[0] = err; +} + +void aml_rwindexfield(struct aml_value *fld, struct aml_value *val, int mode) { struct aml_value tmp, *ref1, *ref2; @@ -2612,6 +2705,10 @@ aml_rwfield(struct aml_value *fld, int b aml_rwgpio(ref2, bpos, blen, val, mode, fld->v_field.flags); break; + case ACPI_OPREG_GSB: + aml_rwgsb(ref2, fld->v_field.bitpos + bpos, blen, + val, mode, fld->v_field.flags); + break; default: aml_rwgen(ref1, fld->v_field.bitpos + bpos, blen, val, mode, fld->v_field.flags); @@ -2699,9 +2796,11 @@ aml_parsefieldlist(struct aml_scope *msc mscope->pos++; blen = aml_parselength(mscope); break; - case 0x01: /* flags */ - mscope->pos += 3; + case 0x01: /* attrib */ + mscope->pos++; blen = 0; + flags = aml_get8(mscope->pos++); + flags |= aml_get8(mscope->pos++) << 8; break; case 0x02: /* connection */ mscope->pos++; Index: dev/acpi/dwiic_acpi.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/dwiic_acpi.c,v retrieving revision 1.3 diff -u -p -r1.3 dwiic_acpi.c --- dev/acpi/dwiic_acpi.c 19 Jan 2018 18:20:38 -0000 1.3 +++ dev/acpi/dwiic_acpi.c 13 May 2018 13:49:47 -0000 @@ -193,6 +193,9 @@ dwiic_acpi_attach(struct device *parent, config_found((struct device *)sc, &sc->sc_iba, iicbus_print); + sc->sc_devnode->i2c = &sc->sc_i2c_tag; + acpi_register_gsb(sc->sc_acpi, sc->sc_devnode); + return; }