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 *);
>