-/* $OpenBSD: chvgpio.c,v 1.6 2016/10/25 06:48:58 pirofti Exp $ */
+/* $OpenBSD: chvgpio.c,v 1.7 2017/11/29 15:22:22 kettenis Exp $ */
/*
* Copyright (c) 2016 Mark Kettenis
*
#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;
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)
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;
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:
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;
+}
-/* $OpenBSD: dsdt.c,v 1.235 2017/10/12 07:24:46 anton Exp $ */
+/* $OpenBSD: dsdt.c,v 1.236 2017/11/29 15:22:22 kettenis Exp $ */
/*
* Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
*
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)
{
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);
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;
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);
}
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);
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");
} 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) {
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) {