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;
 }
 

Reply via email to