--- /dev/null
+/* $OpenBSD: dapmic.c,v 1.1 2021/06/16 12:37:24 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 <sys/malloc.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_regulator.h>
+#include <dev/ofw/fdt.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <dev/clock_subr.h>
+
+extern void (*cpuresetfn)(void);
+extern void (*powerdownfn)(void);
+
+/* Registers */
+#define EVENT_A 0x06
+#define CONTROL_F 0x13
+#define CONTROL_F_WAKE_UP (1 << 2)
+#define CONTROL_F_SHUTDOWN (1 << 1)
+#define COUNT_S 0x40
+#define COUNT_S_COUNT_SEC 0x3f
+#define COUNT_MI 0x41
+#define COUNT_MI_COUNT_MIN 0x3f
+#define COUNT_H 0x42
+#define COUNT_H_COUNT_HOUR 0x1f
+#define COUNT_D 0x43
+#define COUNT_D_COUNT_DAY 0x1f
+#define COUNT_MO 0x44
+#define COUNT_MO_COUNT_MONTH 0x0f
+#define COUNT_Y 0x45
+#define COUNT_Y_MONITOR (1 << 6)
+#define COUNT_Y_COUNT_YEAR 0x3f
+#define ALARM_MO 0x4a
+#define ALARM_MO_TICK_WAKE (1 << 5)
+#define ALARM_MO_TICK_TYPE (1 << 4)
+#define ALARM_Y 0x4b
+#define ALARM_Y_TICK_ON (1 << 7)
+
+struct dapmic_softc {
+ struct device sc_dev;
+ i2c_tag_t sc_tag;
+ i2c_addr_t sc_addr;
+
+ struct todr_chip_handle sc_todr;
+};
+
+int dapmic_match(struct device *, void *, void *);
+void dapmic_attach(struct device *, struct device *, void *);
+
+struct cfattach dapmic_ca = {
+ sizeof(struct dapmic_softc), dapmic_match, dapmic_attach
+};
+
+struct cfdriver dapmic_cd = {
+ NULL, "dapmic", DV_DULL
+};
+
+uint8_t dapmic_reg_read(struct dapmic_softc *, int);
+void dapmic_reg_write(struct dapmic_softc *, int, uint8_t);
+int dapmic_clock_read(struct dapmic_softc *, struct clock_ymdhms *);
+int dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *);
+int dapmic_gettime(struct todr_chip_handle *, struct timeval *);
+int dapmic_settime(struct todr_chip_handle *, struct timeval *);
+void dapmic_reset(void);
+void dapmic_powerdown(void);
+
+int
+dapmic_match(struct device *parent, void *match, void *aux)
+{
+ struct i2c_attach_args *ia = aux;
+
+ return (strcmp(ia->ia_name, "dlg,da9063") == 0);
+}
+
+void
+dapmic_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct dapmic_softc *sc = (struct dapmic_softc *)self;
+ struct i2c_attach_args *ia = aux;
+
+ sc->sc_tag = ia->ia_tag;
+ sc->sc_addr = ia->ia_addr;
+
+ sc->sc_todr.cookie = sc;
+ sc->sc_todr.todr_gettime = dapmic_gettime;
+ sc->sc_todr.todr_settime = dapmic_settime;
+ todr_attach(&sc->sc_todr);
+
+ printf("\n");
+
+ if (cpuresetfn == NULL)
+ cpuresetfn = dapmic_reset;
+ if (powerdownfn == NULL)
+ powerdownfn = dapmic_powerdown;
+}
+
+uint8_t
+dapmic_reg_read(struct dapmic_softc *sc, int reg)
+{
+ uint8_t cmd = reg;
+ uint8_t val;
+ int error;
+
+ iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
+ error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+ &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
+ iic_release_bus(sc->sc_tag, I2C_F_POLL);
+
+ if (error) {
+ printf("%s: can't read register 0x%02x\n",
+ sc->sc_dev.dv_xname, reg);
+ val = 0xff;
+ }
+
+ return val;
+}
+
+void
+dapmic_reg_write(struct dapmic_softc *sc, int reg, uint8_t val)
+{
+ uint8_t cmd = reg;
+ int error;
+
+ iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
+ error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
+ &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
+ iic_release_bus(sc->sc_tag, I2C_F_POLL);
+
+ if (error) {
+ printf("%s: can't write register 0x%02x\n",
+ sc->sc_dev.dv_xname, reg);
+ }
+}
+
+int
+dapmic_clock_read(struct dapmic_softc *sc, struct clock_ymdhms *dt)
+{
+ uint8_t regs[6];
+ uint8_t cmd = COUNT_S;
+ int error;
+
+ iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
+ error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+ &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL);
+ iic_release_bus(sc->sc_tag, I2C_F_POLL);
+
+ if (error)
+ return error;
+
+ dt->dt_sec = (regs[0] & COUNT_S_COUNT_SEC);
+ dt->dt_min = (regs[1] & COUNT_MI_COUNT_MIN);
+ dt->dt_hour = (regs[2] & COUNT_H_COUNT_HOUR);
+ dt->dt_day = (regs[3] & COUNT_D_COUNT_DAY);
+ dt->dt_mon = (regs[4] & COUNT_MO_COUNT_MONTH);
+ dt->dt_year = (regs[5] & COUNT_Y_COUNT_YEAR) + 2000;
+
+ /* Consider the time to be invalid if the MONITOR bit isn't set. */
+ if ((regs[5] & COUNT_Y_MONITOR) == 0)
+ return EINVAL;
+
+ return 0;
+}
+
+int
+dapmic_clock_write(struct dapmic_softc *sc, struct clock_ymdhms *dt)
+{
+ uint8_t regs[6];
+ uint8_t cmd = COUNT_S;
+ int error;
+
+ regs[0] = dt->dt_sec;
+ regs[1] = dt->dt_min;
+ regs[2] = dt->dt_hour;
+ regs[3] = dt->dt_day;
+ regs[4] = dt->dt_mon;
+ regs[5] = (dt->dt_year - 2000) | COUNT_Y_MONITOR;
+
+ iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
+ error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
+ &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL);
+ iic_release_bus(sc->sc_tag, I2C_F_POLL);
+
+ if (error)
+ return error;
+
+ return 0;
+}
+
+int
+dapmic_gettime(struct todr_chip_handle *handle, struct timeval *tv)
+{
+ struct dapmic_softc *sc = handle->cookie;
+ struct clock_ymdhms dt;
+ int error;
+
+ error = dapmic_clock_read(sc, &dt);
+ if (error)
+ return error;
+
+ if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
+ dt.dt_day > 31 || dt.dt_day == 0 ||
+ dt.dt_mon > 12 || dt.dt_mon == 0 ||
+ dt.dt_year < POSIX_BASE_YEAR)
+ return EINVAL;
+
+ tv->tv_sec = clock_ymdhms_to_secs(&dt);
+ tv->tv_usec = 0;
+ return 0;
+}
+
+int
+dapmic_settime(struct todr_chip_handle *handle, struct timeval *tv)
+{
+ struct dapmic_softc *sc = handle->cookie;
+ struct clock_ymdhms dt;
+
+ clock_secs_to_ymdhms(tv->tv_sec, &dt);
+
+ return dapmic_clock_write(sc, &dt);
+}
+
+void
+dapmic_reset(void)
+{
+ struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
+ uint8_t reg;
+
+ /* Enable tick alarm wakeup with a one second interval. */
+ reg = dapmic_reg_read(sc, ALARM_MO);
+ reg &= ~ALARM_MO_TICK_TYPE;
+ reg |= ALARM_MO_TICK_WAKE;
+ dapmic_reg_write(sc, ALARM_MO, reg);
+
+ /* Enable tick function. */
+ reg = dapmic_reg_read(sc, ALARM_Y);
+ reg |= ALARM_Y_TICK_ON;
+ dapmic_reg_write(sc, ALARM_Y, reg);
+
+ /* Clear events such that we wake up again. */
+ dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A));
+ dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
+}
+
+void
+dapmic_powerdown(void)
+{
+ struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
+ uint8_t reg;
+
+ /* Disable tick function such that it doesn't wake us up. */
+ reg = dapmic_reg_read(sc, ALARM_Y);
+ reg &= ~ALARM_Y_TICK_ON;
+ dapmic_reg_write(sc, ALARM_Y, reg);
+
+ dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
+}
--- /dev/null
+/* $OpenBSD: ociic.c,v 1.1 2021/06/16 12:37:24 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/intr.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#define _I2C_PRIVATE
+#include <dev/i2c/i2cvar.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_pinctrl.h>
+#include <dev/ofw/fdt.h>
+
+/* Registers */
+#define I2C_PRER_LO 0x0000
+#define I2C_PRER_HI 0x0004
+#define I2C_CTR 0x0008
+#define I2C_CTR_EN (1 << 7)
+#define I2C_CTR_IEN (1 << 6)
+#define I2C_TXR 0x000C
+#define I2C_RXR 0x000C
+#define I2C_CR 0x0010
+#define I2C_CR_STA (1 << 7)
+#define I2C_CR_STO (1 << 6)
+#define I2C_CR_RD (1 << 5)
+#define I2C_CR_WR (1 << 4)
+#define I2C_CR_NACK (1 << 3)
+#define I2C_CR_IACK (1 << 0)
+#define I2C_SR 0x0010
+#define I2C_SR_RXNACK (1 << 7)
+#define I2C_SR_BUSY (1 << 6)
+#define I2C_SR_AL (1 << 5)
+#define I2C_SR_TIP (1 << 1)
+#define I2C_SR_IF (1 << 0)
+
+struct ociic_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ int sc_node;
+ struct i2c_controller sc_ic;
+};
+
+static inline uint8_t
+ociic_read(struct ociic_softc *sc, bus_size_t reg)
+{
+ return bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg);
+}
+
+static inline void
+ociic_write(struct ociic_softc *sc, bus_size_t reg, uint8_t value)
+{
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, value);
+}
+
+static inline void
+ociic_set(struct ociic_softc *sc, bus_size_t reg, uint8_t bits)
+{
+ ociic_write(sc, reg, ociic_read(sc, reg) | bits);
+}
+
+static inline void
+ociic_clr(struct ociic_softc *sc, bus_size_t reg, uint8_t bits)
+{
+ ociic_write(sc, reg, ociic_read(sc, reg) & ~bits);
+}
+
+int ociic_match(struct device *, void *, void *);
+void ociic_attach(struct device *, struct device *, void *);
+
+struct cfattach ociic_ca = {
+ sizeof (struct ociic_softc), ociic_match, ociic_attach
+};
+
+struct cfdriver ociic_cd = {
+ NULL, "ociic", DV_DULL
+};
+
+int ociic_acquire_bus(void *, int);
+void ociic_release_bus(void *, int);
+int ociic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
+ void *, size_t, int);
+
+void ociic_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
+
+int
+ociic_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "sifive,i2c0");
+}
+
+void
+ociic_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct ociic_softc *sc = (struct ociic_softc *)self;
+ struct fdt_attach_args *faa = aux;
+ struct i2cbus_attach_args iba;
+ uint32_t clock_speed, bus_speed;
+ uint32_t div;
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+
+ sc->sc_iot = faa->fa_iot;
+ sc->sc_node = faa->fa_node;
+
+ 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");
+
+ pinctrl_byname(sc->sc_node, "default");
+ clock_enable_all(sc->sc_node);
+
+ ociic_clr(sc, I2C_CTR, I2C_CTR_EN);
+
+ clock_speed = clock_get_frequency(sc->sc_node, NULL);
+ bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000);
+
+ if (clock_speed > 0) {
+ div = (clock_speed / (5 * bus_speed));
+ if (div > 0)
+ div -= 1;
+ if (div > 0xffff)
+ div = 0xffff;
+
+ ociic_write(sc, I2C_PRER_LO, div & 0xff);
+ ociic_write(sc, I2C_PRER_HI, div >> 8);
+ }
+
+ sc->sc_ic.ic_cookie = sc;
+ sc->sc_ic.ic_acquire_bus = ociic_acquire_bus;
+ sc->sc_ic.ic_release_bus = ociic_release_bus;
+ sc->sc_ic.ic_exec = ociic_exec;
+
+ /* Configure its children */
+ memset(&iba, 0, sizeof(iba));
+ iba.iba_name = "iic";
+ iba.iba_tag = &sc->sc_ic;
+ iba.iba_bus_scan = ociic_bus_scan;
+ iba.iba_bus_scan_arg = &sc->sc_node;
+
+ config_found(&sc->sc_dev, &iba, iicbus_print);
+}
+
+int
+ociic_acquire_bus(void *cookie, int flags)
+{
+ struct ociic_softc *sc = cookie;
+
+ ociic_set(sc, I2C_CTR, I2C_CTR_EN);
+ return 0;
+}
+
+void
+ociic_release_bus(void *cookie, int flags)
+{
+ struct ociic_softc *sc = cookie;
+
+ ociic_clr(sc, I2C_CTR, I2C_CTR_EN);
+}
+
+int
+ociic_wait(struct ociic_softc *sc, int ack)
+{
+ uint8_t stat;
+ int timo;
+
+ for (timo = 50000; timo > 0; timo--) {
+ stat = ociic_read(sc, I2C_SR);
+ if ((stat & I2C_SR_TIP) == 0)
+ break;
+ if ((stat & I2C_SR_AL))
+ break;
+ delay(10);
+ }
+ if (timo == 0) {
+ ociic_write(sc, I2C_CR, I2C_CR_STO);
+ return ETIMEDOUT;
+ }
+
+ if (stat & I2C_SR_AL) {
+ ociic_write(sc, I2C_CR, I2C_CR_STO);
+ return EIO;
+ }
+ if (ack && (stat & I2C_SR_RXNACK)) {
+ ociic_write(sc, I2C_CR, I2C_CR_STO);
+ return EIO;
+ }
+
+ return 0;
+}
+
+int
+ociic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
+ size_t cmdlen, void *buf, size_t buflen, int flags)
+{
+ struct ociic_softc *sc = cookie;
+ int error, i;
+
+ if (cmdlen > 0) {
+ ociic_write(sc, I2C_TXR, addr << 1);
+ ociic_write(sc, I2C_CR, I2C_CR_STA | I2C_CR_WR);
+ error = ociic_wait(sc, 1);
+ if (error)
+ return error;
+
+ for (i = 0; i < cmdlen; i++) {
+ ociic_write(sc, I2C_TXR, ((uint8_t *)cmd)[i]);
+ ociic_write(sc, I2C_CR, I2C_CR_WR);
+ error = ociic_wait(sc, 1);
+ if (error)
+ return error;
+ }
+ }
+
+ if (I2C_OP_READ_P(op)) {
+ ociic_write(sc, I2C_TXR, addr << 1 | 1);
+ ociic_write(sc, I2C_CR, I2C_CR_STA | I2C_CR_WR);
+ error = ociic_wait(sc, 1);
+ if (error)
+ return error;
+
+ for (i = 0; i < buflen; i++) {
+ ociic_write(sc, I2C_CR, I2C_CR_RD |
+ (i == (buflen - 1) ? I2C_CR_NACK : 0));
+ error = ociic_wait(sc, 0);
+ if (error)
+ return error;
+ ((uint8_t *)buf)[i] = ociic_read(sc, I2C_RXR);
+ }
+ } else {
+ if (cmdlen == 0) {
+ ociic_write(sc, I2C_TXR, addr << 1);
+ ociic_write(sc, I2C_CR, I2C_CR_STA | I2C_CR_WR);
+ }
+
+ for (i = 0; i < buflen; i++) {
+ ociic_write(sc, I2C_TXR, ((uint8_t *)buf)[i]);
+ ociic_write(sc, I2C_CR, I2C_CR_WR);
+ error = ociic_wait(sc, 1);
+ if (error)
+ return error;
+ }
+ }
+
+ if (I2C_OP_STOP_P(op))
+ ociic_write(sc, I2C_CR, I2C_CR_STO);
+
+ return 0;
+}
+
+void
+ociic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
+{
+ int iba_node = *(int *)arg;
+ struct i2c_attach_args ia;
+ char name[32], status[32];
+ uint32_t reg[1];
+ int node;
+
+ for (node = OF_child(iba_node); node; node = OF_peer(node)) {
+ memset(name, 0, sizeof(name));
+ memset(status, 0, sizeof(status));
+ memset(reg, 0, sizeof(reg));
+
+ if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
+ continue;
+ if (name[0] == '\0')
+ continue;
+
+ if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
+ strcmp(status, "disabled") == 0)
+ continue;
+
+ if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg))
+ continue;
+
+ memset(&ia, 0, sizeof(ia));
+ ia.ia_tag = iba->iba_tag;
+ ia.ia_addr = bemtoh32(®[0]);
+ ia.ia_name = name;
+ ia.ia_cookie = &node;
+ config_found(self, &ia, iic_print);
+ }
+}