On Wed, Nov 29, 2017 at 04:30:01AM +0100, Mark Kettenis wrote:
> Diff below revises the OperationRegion support to allow chvgpio(4) to
> register its own OEM defined regions. This prevents a panic on a
> Lenovo 2-in-1 that mlarkin@ carried all the way to Elk Lakes cabin.
> It even makes the lid switch work somewhat.
> 
> This will also make it easier to add CMOS support and other
> OEM-specific regions in the future.
> 
> ok?
> 

Looks ok, no objections here.

-ml

> Index: dev/acpi/chvgpio.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/acpi/chvgpio.c,v
> retrieving revision 1.6
> diff -u -p -r1.6 chvgpio.c
> --- dev/acpi/chvgpio.c        25 Oct 2016 06:48:58 -0000      1.6
> +++ dev/acpi/chvgpio.c        29 Nov 2017 03:18:06 -0000
> @@ -43,6 +43,9 @@
>  #define CHVGPIO_PAD_CFG1_INVRXTX_MASK                0x000000f0
>  #define CHVGPIO_PAD_CFG1_INVRXTX_RXDATA              0x00000040
>  
> +/* OEM defined RegionSpace. */
> +#define CHVGPIO_REGIONSPACE_BASE     0x90
> +
>  struct chvgpio_intrhand {
>       int (*ih_func)(void *);
>       void *ih_arg;
> @@ -149,6 +152,7 @@ int       chvgpio_read_pin(void *, int);
>  void chvgpio_write_pin(void *, int, int);
>  void chvgpio_intr_establish(void *, int, int, int (*)(), void *);
>  int  chvgpio_intr(void *);
> +int  chvgpio_opreg_handler(void *, int, uint64_t, int, uint64_t *);
>  
>  int
>  chvgpio_match(struct device *parent, void *match, void *aux)
> @@ -247,7 +251,7 @@ chvgpio_attach(struct device *parent, st
>  
>       printf(", %d pins\n", sc->sc_npins);
>  
> -     /* Register address space. */
> +     /* Register GeneralPurposeIO address space. */
>       memset(&arg, 0, sizeof(arg));
>       arg[0].type = AML_OBJTYPE_INTEGER;
>       arg[0].v_integer = ACPI_OPREG_GPIO;
> @@ -257,6 +261,9 @@ chvgpio_attach(struct device *parent, st
>       if (node && aml_evalnode(sc->sc_acpi, node, 2, arg, NULL))
>               printf("%s: _REG failed\n", sc->sc_dev.dv_xname);
>  
> +     /* Register OEM defined address space. */
> +     aml_register_regionspace(sc->sc_node, CHVGPIO_REGIONSPACE_BASE + uid,
> +         sc, chvgpio_opreg_handler);
>       return;
>  
>  unmap:
> @@ -398,4 +405,22 @@ chvgpio_intr(void *arg)
>       }
>  
>       return rc;
> +}
> +
> +int
> +chvgpio_opreg_handler(void *cookie, int iodir, uint64_t address, int size,
> +    uint64_t *value)
> +{
> +     struct chvgpio_softc *sc = cookie;
> +
> +     /* Only allow 32-bit access. */
> +     if (size != 4 || address > sc->sc_size - size)
> +             return -1;
> +
> +     if (iodir == ACPI_IOREAD)
> +             *value = bus_space_read_4(sc->sc_memt, sc->sc_memh, address);
> +     else
> +             bus_space_write_4(sc->sc_memt, sc->sc_memh, address, *value);
> +
> +     return 0;
>  }
> Index: dev/acpi/dsdt.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/acpi/dsdt.c,v
> retrieving revision 1.235
> diff -u -p -r1.235 dsdt.c
> --- dev/acpi/dsdt.c   12 Oct 2017 07:24:46 -0000      1.235
> +++ dev/acpi/dsdt.c   29 Nov 2017 03:08:06 -0000
> @@ -2208,8 +2208,6 @@ void aml_createfield(struct aml_value *,
>  void aml_parsefieldlist(struct aml_scope *, int, int,
>      struct aml_value *, struct aml_value *, int);
>  
> -#define GAS_PCI_CFG_SPACE_UNEVAL  0xCC
> -
>  int
>  aml_evalhid(struct aml_node *node, struct aml_value *val)
>  {
> @@ -2222,7 +2220,73 @@ aml_evalhid(struct aml_node *node, struc
>       return (0);
>  }
>  
> -void aml_rwgas(struct aml_value *, int, int, struct aml_value *, int, int);
> +int
> +aml_opreg_sysmem_handler(void *cookie, int iodir, uint64_t address, int size,
> +    uint64_t *value)
> +{
> +     return acpi_gasio(acpi_softc, iodir, GAS_SYSTEM_MEMORY,
> +         address, size, size, value);
> +}
> +
> +int
> +aml_opreg_sysio_handler(void *cookie, int iodir, uint64_t address, int size,
> +    uint64_t *value)
> +{
> +     return acpi_gasio(acpi_softc, iodir, GAS_SYSTEM_IOSPACE,
> +         address, size, size, value);
> +}
> +
> +int
> +aml_opreg_pcicfg_handler(void *cookie, int iodir, uint64_t address, int size,
> +    uint64_t *value)
> +{
> +     return acpi_gasio(acpi_softc, iodir, GAS_PCI_CFG_SPACE,
> +         address, size, size, value);
> +}
> +
> +int
> +aml_opreg_ec_handler(void *cookie, int iodir, uint64_t address, int size,
> +    uint64_t *value)
> +{
> +     return acpi_gasio(acpi_softc, iodir, GAS_EMBEDDED,
> +         address, size, size, value);
> +}
> +
> +struct aml_regionspace {
> +     void *cookie;
> +     int (*handler)(void *, int, uint64_t, int, uint64_t *);
> +};
> +
> +struct aml_regionspace aml_regionspace[256] = {
> +     [ACPI_OPREG_SYSMEM] = { NULL, aml_opreg_sysmem_handler },
> +     [ACPI_OPREG_SYSIO] = { NULL, aml_opreg_sysio_handler },
> +     [ACPI_OPREG_PCICFG] = { NULL, aml_opreg_pcicfg_handler },
> +     [ACPI_OPREG_EC] = { NULL, aml_opreg_ec_handler },
> +};
> +
> +void
> +aml_register_regionspace(struct aml_node *node, int iospace, void *cookie,
> +    int (*handler)(void *, int, uint64_t, int, uint64_t *))
> +{
> +     struct aml_value arg[2];
> +
> +     KASSERT(iospace >= 0 && iospace < 256);
> +
> +     aml_regionspace[iospace].cookie = cookie;
> +     aml_regionspace[iospace].handler = handler;
> +
> +     /* Register address space. */
> +     memset(&arg, 0, sizeof(arg));
> +     arg[0].type = AML_OBJTYPE_INTEGER;
> +     arg[0].v_integer = iospace;
> +     arg[1].type = AML_OBJTYPE_INTEGER;
> +     arg[1].v_integer = 1;
> +     node = aml_searchname(node, "_REG");
> +     if (node)
> +             aml_evalnode(acpi_softc, node, 2, arg, NULL);
> +}
> +
> +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_rwindexfield(struct aml_value *, struct aml_value *val, int);
>  void aml_rwfield(struct aml_value *, int, int, struct aml_value *, int);
> @@ -2250,9 +2314,73 @@ aml_rdpciaddr(struct aml_node *pcidev, u
>       return (0);
>  }
>  
> +int
> +acpi_genio(struct acpi_softc *sc, int iodir, int iospace, uint64_t address,
> +    int access_size, int len, void *buffer)
> +{
> +     struct aml_regionspace *region = &aml_regionspace[iospace];
> +     u_int8_t *pb;
> +     int reg;
> +
> +     dnprintf(50, "genio: %.2x 0x%.8llx %s\n",
> +         iospace, address, (iodir == ACPI_IOWRITE) ? "write" : "read");
> +
> +     KASSERT((len % access_size) == 0);
> +
> +     pb = (u_int8_t *)buffer;
> +     for (reg = 0; reg < len; reg += access_size) {
> +             uint64_t value;
> +             int err;
> +
> +             if (iodir == ACPI_IOREAD) {
> +                     err = region->handler(region->cookie, iodir,
> +                         address + reg, access_size, &value);
> +                     if (err)
> +                             return err;
> +                     switch (access_size) {
> +                     case 1:
> +                             *(uint8_t *)(pb + reg) = value;
> +                             break;
> +                     case 2:
> +                             *(uint16_t *)(pb + reg) = value;
> +                             break;
> +                     case 4:
> +                             *(uint32_t *)(pb + reg) = value;
> +                             break;
> +                     default:
> +                             printf("%s: invalid access size %d on read\n",
> +                                 __func__, access_size);
> +                             return -1;
> +                     }
> +             } else {
> +                     switch (access_size) {
> +                     case 1:
> +                             value = *(uint8_t *)(pb + reg);
> +                             break;
> +                     case 2:
> +                             value = *(uint16_t *)(pb + reg);
> +                             break;
> +                     case 4:
> +                             value = *(uint32_t *)(pb + reg);
> +                             break;
> +                     default:
> +                             printf("%s: invalid access size %d on write\n",
> +                                 __func__, access_size);
> +                             return -1;
> +                     }
> +                     err = region->handler(region->cookie, iodir,
> +                         address + reg, access_size, &value);
> +                     if (err)
> +                             return err;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
>  /* Read/Write from opregion object */
>  void
> -aml_rwgas(struct aml_value *rgn, int bpos, int blen, struct aml_value *val,
> +aml_rwgen(struct aml_value *rgn, int bpos, int blen, struct aml_value *val,
>      int mode, int flag)
>  {
>       struct aml_value tmp;
> @@ -2287,7 +2415,7 @@ aml_rwgas(struct aml_value *rgn, int bpo
>       bpos += ((rgn->v_opregion.iobase & (sz - 1)) << 3);
>       bpos &= ((sz << 3) - 1);
>  
> -     if (rgn->v_opregion.iospace == GAS_PCI_CFG_SPACE) {
> +     if (rgn->v_opregion.iospace == ACPI_OPREG_PCICFG) {
>               /* Get PCI Root Address for this opregion */
>               aml_rdpciaddr(rgn->node->parent, &pi);
>       }
> @@ -2297,6 +2425,9 @@ aml_rwgas(struct aml_value *rgn, int bpo
>       tlen = roundup(bpos + blen, sz << 3);
>       type = rgn->v_opregion.iospace;
>  
> +     if (aml_regionspace[type].handler == NULL)
> +             panic("%s: unregistered RegionSpace 0x%x\n", __func__, type);
> +
>       /* Allocate temporary storage */
>       if (tlen > aml_intlen) {
>               _aml_setvalue(&tmp, AML_OBJTYPE_BUFFER, tlen >> 3, 0);
> @@ -2327,21 +2458,21 @@ aml_rwgas(struct aml_value *rgn, int bpo
>  
>       if (mode == ACPI_IOREAD) {
>               /* Read bits from opregion */
> -             acpi_gasio(acpi_softc, ACPI_IOREAD, type, pi.addr,
> +             acpi_genio(acpi_softc, ACPI_IOREAD, type, pi.addr,
>                   sz, tlen >> 3, tbit);
>               aml_bufcpy(vbit, 0, tbit, bpos, blen);
>       } else {
>               /* Write bits to opregion */
>               if (AML_FIELD_UPDATE(flag) == AML_FIELD_PRESERVE &&
>                   (bpos != 0 || blen != tlen)) {
> -                     acpi_gasio(acpi_softc, ACPI_IOREAD, type, pi.addr,
> +                     acpi_genio(acpi_softc, ACPI_IOREAD, type, pi.addr,
>                           sz, tlen >> 3, tbit);
>               } else if (AML_FIELD_UPDATE(flag) == AML_FIELD_WRITEASONES) {
>                       memset(tbit, 0xff, tmp.length);
>               }
>               /* Copy target bits, then write to region */
>               aml_bufcpy(tbit, bpos, vbit, 0, blen);
> -             acpi_gasio(acpi_softc, ACPI_IOWRITE, type, pi.addr,
> +             acpi_genio(acpi_softc, ACPI_IOWRITE, type, pi.addr,
>                   sz, tlen >> 3, tbit);
>  
>               aml_delref(&val, "fld.write");
> @@ -2473,7 +2604,7 @@ aml_rwfield(struct aml_value *fld, int b
>       } else if (fld->v_field.type == AMLOP_BANKFIELD) {
>               _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, fld->v_field.ref3, 0);
>               aml_rwfield(ref2, 0, aml_intlen, &tmp, ACPI_IOWRITE);
> -             aml_rwgas(ref1, fld->v_field.bitpos, fld->v_field.bitlen,
> +             aml_rwgen(ref1, fld->v_field.bitpos, fld->v_field.bitlen,
>                   val, mode, fld->v_field.flags);
>       } else if (fld->v_field.type == AMLOP_FIELD) {
>               switch (ref1->v_opregion.iospace) {
> @@ -2481,16 +2612,9 @@ aml_rwfield(struct aml_value *fld, int b
>                       aml_rwgpio(ref2, bpos, blen, val, mode,
>                           fld->v_field.flags);
>                       break;
> -             case ACPI_OPREG_SYSMEM:
> -             case ACPI_OPREG_SYSIO:
> -             case ACPI_OPREG_PCICFG:
> -             case ACPI_OPREG_EC:
> -                     aml_rwgas(ref1, fld->v_field.bitpos + bpos, blen,
> -                         val, mode, fld->v_field.flags);
> -                     break;
>               default:
> -                     aml_die("Unsupported RegionSpace 0x%x",
> -                         ref1->v_opregion.iospace);
> +                     aml_rwgen(ref1, fld->v_field.bitpos + bpos, blen,
> +                         val, mode, fld->v_field.flags);
>                       break;
>               }
>       } else if (mode == ACPI_IOREAD) {
> Index: dev/acpi/dsdt.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/acpi/dsdt.h,v
> retrieving revision 1.73
> diff -u -p -r1.73 dsdt.h
> --- dev/acpi/dsdt.h   25 Oct 2016 06:48:58 -0000      1.73
> +++ dev/acpi/dsdt.h   29 Nov 2017 01:28:07 -0000
> @@ -60,6 +60,8 @@ int                 acpi_parse_aml(struct acpi_softc *
>  void                 aml_register_notify(struct aml_node *, const char *,
>                           int (*)(struct aml_node *, int, void *), void *,
>                           int);
> +void                 aml_register_regionspace(struct aml_node *, int, void *,
> +                         int (*)(void *, int, uint64_t, int, uint64_t *));
>  
>  int                  aml_evalnode(struct acpi_softc *, struct aml_node *,
>                           int, struct aml_value *, struct aml_value *);
> 

Reply via email to