summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2021-05-26 20:52:22 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2021-05-26 20:52:22 +0000
commitf60cd3dfa26afb75eb626b5b26d9fbcba9e855b9 (patch)
tree474e9da036f4911995eb72f0c9a1d5429d3377d4 /sys
parent50faa6c0e29001d95b26f66f3d106383de7c8f60 (diff)
Add aplspmi(4), a driver for the Apple SPMI controller, and aplpmu(4)
a driver for the Apple "sera" SPMI power management unit that contains the RTC on Apple M1 systems. ok patrick@
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/arm64/conf/GENERIC4
-rw-r--r--sys/arch/arm64/conf/RAMDISK4
-rw-r--r--sys/arch/arm64/conf/files.arm6411
-rw-r--r--sys/arch/arm64/dev/aplpmu.c147
-rw-r--r--sys/arch/arm64/dev/aplspmi.c215
-rw-r--r--sys/dev/fdt/spmivar.h39
6 files changed, 417 insertions, 3 deletions
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC
index d40e65471d9..bb564c97b68 100644
--- a/sys/arch/arm64/conf/GENERIC
+++ b/sys/arch/arm64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.198 2021/05/24 18:40:19 kettenis Exp $
+# $OpenBSD: GENERIC,v 1.199 2021/05/26 20:52:21 kettenis Exp $
#
# GENERIC machine description file
#
@@ -138,6 +138,8 @@ apldwusb* at fdt?
aplintc* at fdt? early 1
aplpcie* at fdt?
pci* at aplpcie?
+aplspmi* at fdt?
+aplpmu* at aplspmi?
exuart* at fdt?
# iMX
diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK
index e68e04b570a..c9bf95cb635 100644
--- a/sys/arch/arm64/conf/RAMDISK
+++ b/sys/arch/arm64/conf/RAMDISK
@@ -1,4 +1,4 @@
-# $OpenBSD: RAMDISK,v 1.150 2021/05/24 18:40:19 kettenis Exp $
+# $OpenBSD: RAMDISK,v 1.151 2021/05/26 20:52:21 kettenis Exp $
machine arm64
maxusers 4
@@ -102,6 +102,8 @@ apldwusb* at fdt?
aplintc* at fdt? early 1
aplpcie* at fdt?
pci* at aplpcie?
+aplspmi* at fdt?
+aplpmu* at aplspmi?
exuart* at fdt?
# iMX
diff --git a/sys/arch/arm64/conf/files.arm64 b/sys/arch/arm64/conf/files.arm64
index 77fa39b9b9a..0f01611f98a 100644
--- a/sys/arch/arm64/conf/files.arm64
+++ b/sys/arch/arm64/conf/files.arm64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.arm64,v 1.41 2021/05/24 18:40:19 kettenis Exp $
+# $OpenBSD: files.arm64,v 1.42 2021/05/26 20:52:21 kettenis Exp $
maxpartitions 16
maxusers 2 8 128
@@ -156,6 +156,15 @@ device aplpcie: pcibus
attach aplpcie at fdt
file arch/arm64/dev/aplpcie.c aplpcie
+define spmi {}
+device aplpmu
+attach aplpmu at spmi
+file arch/arm64/dev/aplpmu.c aplpmu
+
+device aplspmi: spmi
+attach aplspmi at fdt
+file arch/arm64/dev/aplspmi.c aplspmi
+
device bcmintc
attach bcmintc at fdt
file arch/arm64/dev/bcm2836_intr.c bcmintc
diff --git a/sys/arch/arm64/dev/aplpmu.c b/sys/arch/arm64/dev/aplpmu.c
new file mode 100644
index 00000000000..5515a8797f8
--- /dev/null
+++ b/sys/arch/arm64/dev/aplpmu.c
@@ -0,0 +1,147 @@
+/* $OpenBSD: aplpmu.c,v 1.1 2021/05/26 20:52:21 kettenis Exp $ */
+/*
+ * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/clock_subr.h>
+#include <dev/fdt/spmivar.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/fdt.h>
+
+extern todr_chip_handle_t todr_handle;
+
+/*
+ * This driver is based on preliminary device tree bindings and will
+ * almost certainly need changes once the official bindings land in
+ * mainline Linux. Support for these preliminary bindings will be
+ * dropped as soon as official bindings are available.
+ */
+
+/*
+ * Apple's "sera" PMU contains an RTC that provides time in 32.16
+ * fixed-point format as well as a time offset in 33.15 fixed-point
+ * format. The sum of the two gives us a standard Unix timestamp with
+ * sub-second resolution. The time itself is read-only. To set the
+ * time we need to adjust the time offset.
+ */
+#define SERA_TIME 0xd002
+#define SERA_TIME_OFFSET 0xd100
+#define SERA_TIME_LEN 6
+
+struct aplpmu_softc {
+ struct device sc_dev;
+ spmi_tag_t sc_tag;
+ int8_t sc_sid;
+
+ struct todr_chip_handle sc_todr;
+ uint64_t sc_offset;
+};
+
+int aplpmu_match(struct device *, void *, void *);
+void aplpmu_attach(struct device *, struct device *, void *);
+
+struct cfattach aplpmu_ca = {
+ sizeof (struct aplpmu_softc), aplpmu_match, aplpmu_attach
+};
+
+struct cfdriver aplpmu_cd = {
+ NULL, "aplpmu", DV_DULL
+};
+
+int aplpmu_gettime(struct todr_chip_handle *, struct timeval *);
+int aplpmu_settime(struct todr_chip_handle *, struct timeval *);
+
+int
+aplpmu_match(struct device *parent, void *match, void *aux)
+{
+ struct spmi_attach_args *sa = aux;
+
+ return OF_is_compatible(sa->sa_node, "apple,sera-pmu");
+}
+
+void
+aplpmu_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct aplpmu_softc *sc = (struct aplpmu_softc *)self;
+ struct spmi_attach_args *sa = aux;
+ uint8_t data[8] = {};
+ int error;
+
+ sc->sc_tag = sa->sa_tag;
+ sc->sc_sid = sa->sa_sid;
+
+ error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
+ SERA_TIME_OFFSET, &data, SERA_TIME_LEN);
+ if (error) {
+ printf(": can't read offset\n");
+ return;
+ }
+ sc->sc_offset = lemtoh64(data);
+
+ printf("\n");
+
+ sc->sc_todr.cookie = sc;
+ sc->sc_todr.todr_gettime = aplpmu_gettime;
+ sc->sc_todr.todr_settime = aplpmu_settime;
+ todr_handle = &sc->sc_todr;
+}
+
+int
+aplpmu_gettime(struct todr_chip_handle *handle, struct timeval *tv)
+{
+ struct aplpmu_softc *sc = handle->cookie;
+ uint8_t data[8] = {};
+ uint64_t time;
+ int error;
+
+ error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
+ SERA_TIME, &data, SERA_TIME_LEN);
+ if (error)
+ return error;
+ time = lemtoh64(data) + (sc->sc_offset << 1);
+
+ tv->tv_sec = (time >> 16);
+ tv->tv_usec = (((time & 0xffff) * 1000000) >> 16);
+ return 0;
+}
+
+int
+aplpmu_settime(struct todr_chip_handle *handle, struct timeval *tv)
+{
+ struct aplpmu_softc *sc = handle->cookie;
+ uint8_t data[8] = {};
+ uint64_t time;
+ int error;
+
+ error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
+ SERA_TIME, &data, SERA_TIME_LEN);
+ if (error)
+ return error;
+
+ time = ((uint64_t)tv->tv_sec << 16);
+ time |= ((uint64_t)tv->tv_usec << 16) / 1000000;
+ sc->sc_offset = ((time - lemtoh64(data)) >> 1);
+
+ htolem64(data, sc->sc_offset);
+ return spmi_cmd_write(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_WRITEL,
+ SERA_TIME_OFFSET, &data, SERA_TIME_LEN);
+}
diff --git a/sys/arch/arm64/dev/aplspmi.c b/sys/arch/arm64/dev/aplspmi.c
new file mode 100644
index 00000000000..67854244fc9
--- /dev/null
+++ b/sys/arch/arm64/dev/aplspmi.c
@@ -0,0 +1,215 @@
+/* $OpenBSD: aplspmi.c,v 1.1 2021/05/26 20:52:21 kettenis Exp $ */
+/*
+ * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/fdt.h>
+
+#include <dev/fdt/spmivar.h>
+
+/*
+ * This driver is based on preliminary device tree bindings and will
+ * almost certainly need changes once the official bindings land in
+ * mainline Linux. Support for these preliminary bindings will be
+ * dropped as soon as official bindings are available.
+ */
+
+#define SPMI_STAT 0x00
+#define SPMI_STAT_RXEMPTY (1 << 24)
+#define SPMI_STAT_TXEMPTY (1 << 8)
+#define SPMI_CMD 0x04
+#define SPMI_CMD_ADDR(x) ((x) << 16)
+#define SPMI_CMD_LAST (1 << 15)
+#define SPMI_CMD_SID(x) ((x) << 8)
+#define SPMI_RESP 0x08
+#define SPMI_INTEN(i) (0x20 + (i) * 4)
+#define SPMI_INTSTAT(i) (0x60 + (i) * 4)
+
+#define HREAD4(sc, reg) \
+ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
+#define HWRITE4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+
+struct aplspmi_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ struct spmi_controller sc_tag;
+};
+
+int aplspmi_match(struct device *, void *, void *);
+void aplspmi_attach(struct device *, struct device *, void *);
+
+struct cfattach aplspmi_ca = {
+ sizeof (struct aplspmi_softc), aplspmi_match, aplspmi_attach
+};
+
+struct cfdriver aplspmi_cd = {
+ NULL, "aplspmi", DV_DULL
+};
+
+int aplspmi_print(void *, const char *);
+int aplspmi_cmd_read(void *, uint8_t, uint8_t, uint16_t, void *, size_t);
+int aplspmi_cmd_write(void *, uint8_t, uint8_t, uint16_t,
+ const void *, size_t);
+
+int
+aplspmi_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "apple,spmi");
+}
+
+void
+aplspmi_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct aplspmi_softc *sc = (struct aplspmi_softc *)self;
+ struct fdt_attach_args *faa = aux;
+ struct spmi_attach_args sa;
+ char name[32];
+ uint32_t reg[2];
+ int node;
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+
+ sc->sc_iot = faa->fa_iot;
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ printf("\n");
+
+ sc->sc_tag.sc_cookie = sc;
+ sc->sc_tag.sc_cmd_read = aplspmi_cmd_read;
+ sc->sc_tag.sc_cmd_write = aplspmi_cmd_write;
+
+ for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
+ if (OF_getpropintarray(node, "reg", reg,
+ sizeof(reg)) != sizeof(reg))
+ continue;
+
+ memset(name, 0, sizeof(name));
+ if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
+ continue;
+ if (name[0] == '\0')
+ continue;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_tag = &sc->sc_tag;
+ sa.sa_sid = reg[0];
+ sa.sa_name = name;
+ sa.sa_node = node;
+ config_found(self, &sa, aplspmi_print);
+ }
+}
+
+int
+aplspmi_print(void *aux, const char *pnp)
+{
+ struct spmi_attach_args *sa = aux;
+
+ if (pnp != NULL)
+ printf("\"%s\" at %s", sa->sa_name, pnp);
+ printf(" sid 0x%x", sa->sa_sid);
+
+ return UNCONF;
+}
+
+int
+aplspmi_read_resp(struct aplspmi_softc *sc, uint32_t *resp)
+{
+ int retry;
+
+ for (retry = 1000; retry > 0; retry--) {
+ if ((HREAD4(sc, SPMI_STAT) & SPMI_STAT_RXEMPTY) == 0)
+ break;
+ delay(1);
+ }
+ if (retry == 0)
+ return ETIMEDOUT;
+
+ *resp = HREAD4(sc, SPMI_RESP);
+ return 0;
+}
+
+int
+aplspmi_cmd_read(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr,
+ void *buf, size_t len)
+{
+ struct aplspmi_softc *sc = cookie;
+ uint8_t *cbuf = buf;
+ uint32_t resp;
+ int error;
+
+ if (len == 0 || len > 8)
+ return EINVAL;
+
+ HWRITE4(sc, SPMI_CMD, SPMI_CMD_SID(sid) | cmd | SPMI_CMD_ADDR(addr) |
+ (len - 1) | SPMI_CMD_LAST);
+
+ error = aplspmi_read_resp(sc, &resp);
+ if (error)
+ return error;
+
+ while (len > 0) {
+ error = aplspmi_read_resp(sc, &resp);
+ if (error)
+ return error;
+ memcpy(cbuf, &resp, MIN(len, 4));
+ cbuf += MIN(len, 4);
+ len -= MIN(len, 4);
+ }
+
+ return 0;
+}
+
+int
+aplspmi_cmd_write(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr,
+ const void *buf, size_t len)
+{
+ struct aplspmi_softc *sc = cookie;
+ const uint8_t *cbuf = buf;
+ uint32_t data, resp;
+
+ if (len == 0 || len > 8)
+ return EINVAL;
+
+ HWRITE4(sc, SPMI_CMD, SPMI_CMD_SID(sid) | cmd | SPMI_CMD_ADDR(addr) |
+ (len - 1) | SPMI_CMD_LAST);
+
+ while (len > 0) {
+ memcpy(&data, cbuf, MIN(len, 4));
+ HWRITE4(sc, SPMI_CMD, data);
+ cbuf += MIN(len, 4);
+ len -= MIN(len, 4);
+ }
+
+ return aplspmi_read_resp(sc, &resp);
+}
diff --git a/sys/dev/fdt/spmivar.h b/sys/dev/fdt/spmivar.h
new file mode 100644
index 00000000000..5602c78215c
--- /dev/null
+++ b/sys/dev/fdt/spmivar.h
@@ -0,0 +1,39 @@
+/* $OpenBSD: spmivar.h,v 1.1 2021/05/26 20:52:21 kettenis Exp $ */
+/*
+ * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define SPMI_CMD_EXT_WRITEL 0x30
+#define SPMI_CMD_EXT_READL 0x38
+
+typedef struct spmi_controller {
+ void *sc_cookie;
+ int (*sc_cmd_read)(void *, uint8_t, uint8_t, uint16_t,
+ void *, size_t);
+ int (*sc_cmd_write)(void *, uint8_t, uint8_t, uint16_t,
+ const void *, size_t);
+} *spmi_tag_t;
+
+struct spmi_attach_args {
+ spmi_tag_t sa_tag;
+ uint8_t sa_sid;
+ char *sa_name;
+ int sa_node;
+};
+
+#define spmi_cmd_read(sc, sid, cmd, addr, buf, len) \
+ (*(sc)->sc_cmd_read)((sc)->sc_cookie, (sid), (cmd), (addr), (buf), (len))
+#define spmi_cmd_write(sc, sid, cmd, addr, buf, len) \
+ (*(sc)->sc_cmd_write)((sc)->sc_cookie, (sid), (cmd), (addr), (buf), (len))