This is found on the XPI-3128
---
 sys/arch/armv7/conf/GENERIC |   1 +
 sys/dev/fdt/ambrtc.c        | 252 ++++++++++++++++++++++++++++++++++++
 sys/dev/fdt/files.fdt       |   5 +
 3 files changed, 258 insertions(+)
 create mode 100644 sys/dev/fdt/ambrtc.c

diff --git a/sys/arch/armv7/conf/GENERIC b/sys/arch/armv7/conf/GENERIC
index c84962ed183..994fa565976 100644
--- a/sys/arch/armv7/conf/GENERIC
+++ b/sys/arch/armv7/conf/GENERIC
@@ -219,6 +219,7 @@ rkiic*              at fdt?
 iic*           at rkiic?
 rktemp*                at fdt?
 dwdog*         at fdt?
+ambrtc*                at iic?                 # Ambiq Artasie AM18X5 RTC
 
 # Xilinx Zynq-7000
 cad*           at fdt?                 # Ethernet controller
diff --git a/sys/dev/fdt/ambrtc.c b/sys/dev/fdt/ambrtc.c
new file mode 100644
index 00000000000..b5c14dc97e1
--- /dev/null
+++ b/sys/dev/fdt/ambrtc.c
@@ -0,0 +1,252 @@
+/*     $OpenBSD$ */
+/*
+ * Ambiq Artasie AM18X5 RTC
+ * Copyright (c) 2025 joshua stein <j...@jcs.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/i2c/i2cvar.h>
+
+#include <dev/clock_subr.h>
+
+#define AMBRTC_SECONDS_HUNDREDTHS      0x00
+#define  AMBRTC_SECONDS_HUNDREDTHS_MASK                0x0f
+#define  AMBRTC_SECONDS_HUNDREDTHS_TENTHS_MASK 0xf0
+#define  AMBRTC_SECONDS_HUNDREDTHS_TENTHS_SHIFT        4
+#define AMBRTC_SECONDS                 0x01
+#define  AMBRTC_SECONDS_ONES_MASK              0x0f
+#define  AMBRTC_SECONDS_TENS_MASK              0x70
+#define  AMBRTC_SECONDS_TENS_SHIFT             4
+#define AMBRTC_MINUTES                 0x02
+#define  AMBRTC_MINUTES_ONES_MASK              0x0f
+#define  AMBRTC_MINUTES_TENS_MASK              0x70
+#define  AMBRTC_MINUTES_TENS_SHIFT             4
+#define AMBRTC_HOURS                   0x03
+#define  AMBRTC_HOURS_ONES_MASK                        0x0f
+#define  AMBRTC_HOURS_TENS_MASK                        0x10
+#define  AMBRTC_HOURS_TENS_SHIFT               4
+#define  AMBRTC_HOURS_AMPM_MASK                        0x20
+#define  AMBRTC_HOURS_AMPM_SHIFT               5
+#define AMBRTC_MDAY                    0x04
+#define  AMBRTC_MDAY_ONES_MASK                 0x0f
+#define  AMBRTC_MDAY_TENS_MASK                 0x30
+#define  AMBRTC_MDAY_TENS_SHIFT                        4
+#define AMBRTC_MONTH                   0x05
+#define  AMBRTC_MONTH_ONES_MASK                        0x0f
+#define  AMBRTC_MONTH_TENS_MASK                        0x10
+#define  AMBRTC_MONTH_TENS_SHIFT               4
+#define AMBRTC_YEAR                    0x06
+#define  AMBRTC_YEAR_ONES_MASK                 0x0f
+#define  AMBRTC_YEAR_TENS_MASK                 0xf0
+#define  AMBRTC_YEAR_TENS_SHIFT                        4
+#define AMBRTC_WDAY                    0x07
+#define AMBRTC_CONTROL1                        0x10
+#define  AMBRTC_CONTROL1_WRTC_BIT              0
+#define  AMBRTC_CONTROL1_1224_BIT              6
+#define AMBRTC_ID0                     0x28
+#define AMBRTC_ID1                     0x29
+
+struct ambrtc_softc {
+       struct device   sc_dev;
+       i2c_tag_t       sc_tag;
+       i2c_addr_t      sc_addr;
+
+       struct todr_chip_handle sc_todr;
+};
+
+int    ambrtc_match(struct device *, void *, void *);
+void   ambrtc_attach(struct device *, struct device *, void *);
+uint8_t ambrtc_read(struct ambrtc_softc *, uint8_t);
+int    ambrtc_write(struct ambrtc_softc *, uint8_t, uint8_t);
+int    ambrtc_clock_read(struct ambrtc_softc *sc, struct clock_ymdhms *dt);
+int    ambrtc_gettime(struct todr_chip_handle *, struct timeval *);
+int    ambrtc_settime(struct todr_chip_handle *, struct timeval *);
+
+const struct cfattach ambrtc_ca = {
+       sizeof(struct ambrtc_softc), ambrtc_match, ambrtc_attach
+};
+
+struct cfdriver ambrtc_cd = {
+       NULL, "ambrtc", DV_DULL
+};
+
+int
+ambrtc_match(struct device *parent, void *match, void *aux)
+{
+       struct i2c_attach_args *ia = aux;
+
+       return (strcmp(ia->ia_name, "geniatech,rtc_am1805") == 0);
+}
+
+void
+ambrtc_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct ambrtc_softc *sc = (struct ambrtc_softc *)self;
+       struct i2c_attach_args *ia = aux;
+       uint8_t val;
+
+       sc->sc_tag = ia->ia_tag;
+       sc->sc_addr = ia->ia_addr;
+
+       val = ambrtc_read(sc, AMBRTC_ID0);
+       if (val != 0x18) {
+               printf("; unknown device\n");
+               return;
+       }
+
+       val = ambrtc_read(sc, AMBRTC_ID1);
+       printf(": AM18%02X", val);
+
+       /* put in 24 hour mode and allow updates */
+       val = ambrtc_read(sc, AMBRTC_CONTROL1);
+       val |= (1 << AMBRTC_CONTROL1_1224_BIT) |
+           (1 << AMBRTC_CONTROL1_WRTC_BIT);
+       ambrtc_write(sc, AMBRTC_CONTROL1, val);
+
+       printf("\n");
+
+       sc->sc_todr.cookie = sc;
+       sc->sc_todr.todr_gettime = ambrtc_gettime;
+       sc->sc_todr.todr_settime = ambrtc_settime;
+       sc->sc_todr.todr_quality = 0;
+       todr_attach(&sc->sc_todr);
+}
+
+uint8_t
+ambrtc_read(struct ambrtc_softc *sc, uint8_t reg)
+{
+       uint8_t cmd = reg, 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: failed reading register 0x%02x: %d\n",
+                   sc->sc_dev.dv_xname, reg, error);
+               val = 0xff;
+       }
+
+       return val;
+}
+
+int
+ambrtc_write(struct ambrtc_softc *sc, uint8_t 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: failed writing register 0x%02x: %d\n",
+                   sc->sc_dev.dv_xname, reg, error);
+               return 1;
+       }
+
+       return 0;
+}
+
+int
+ambrtc_clock_read(struct ambrtc_softc *sc, struct clock_ymdhms *dt)
+{
+       uint8_t val;
+
+       val = ambrtc_read(sc, AMBRTC_YEAR);
+       if (val == 0xff)
+               return EINVAL;
+       dt->dt_year = 2000 + FROMBCD(val);
+
+       val = ambrtc_read(sc, AMBRTC_MONTH);
+       if (val == 0xff)
+               return EINVAL;
+       dt->dt_mon = FROMBCD(val);
+
+       val = ambrtc_read(sc, AMBRTC_MDAY);
+       if (val == 0xff)
+               return EINVAL;
+       dt->dt_day = FROMBCD(val);
+
+       val = ambrtc_read(sc, AMBRTC_HOURS);
+       if (val == 0xff)
+               return EINVAL;
+       dt->dt_hour = FROMBCD(val);
+
+       val = ambrtc_read(sc, AMBRTC_MINUTES);
+       if (val == 0xff)
+               return EINVAL;
+       dt->dt_min = FROMBCD(val);
+
+       val = ambrtc_read(sc, AMBRTC_SECONDS);
+       if (val == 0xff)
+               return EINVAL;
+       dt->dt_sec = FROMBCD(val);
+
+       return 0;
+}
+
+int
+ambrtc_clock_write(struct ambrtc_softc *sc, struct clock_ymdhms *dt)
+{
+       if (ambrtc_write(sc, AMBRTC_YEAR, TOBCD(dt->dt_year % 1000)) != 0)
+               return EINVAL;
+       if (ambrtc_write(sc, AMBRTC_MONTH, TOBCD(dt->dt_mon)) != 0)
+               return EINVAL;
+       if (ambrtc_write(sc, AMBRTC_MDAY, TOBCD(dt->dt_day)) != 0)
+               return EINVAL;
+       if (ambrtc_write(sc, AMBRTC_HOURS, TOBCD(dt->dt_hour)) != 0)
+               return EINVAL;
+       if (ambrtc_write(sc, AMBRTC_MINUTES, TOBCD(dt->dt_min)) != 0)
+               return EINVAL;
+
+       return 0;
+}
+
+int
+ambrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
+{
+       struct ambrtc_softc *sc = handle->cookie;
+       struct clock_ymdhms dt;
+
+       if (ambrtc_clock_read(sc, &dt) != 0)
+               return EINVAL;
+
+       tv->tv_sec = clock_ymdhms_to_secs(&dt);
+       tv->tv_usec = 0;
+
+       return 0;
+}
+
+int
+ambrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
+{
+       struct ambrtc_softc *sc = handle->cookie;
+        struct clock_ymdhms dt;
+
+       clock_secs_to_ymdhms(tv->tv_sec, &dt);
+
+       return ambrtc_clock_write(sc, &dt);
+}
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt
index 3da5446db9a..8d21a7e56dd 100644
--- a/sys/dev/fdt/files.fdt
+++ b/sys/dev/fdt/files.fdt
@@ -797,3 +797,8 @@ file        dev/fdt/qcsdam.c                qcsdam
 device tipd
 attach tipd at i2c
 file   dev/fdt/tipd.c          tipd
+
+# Ambiq Artasie AM18X5 RTC
+device ambrtc
+attach ambrtc at i2c
+file   dev/fdt/ambrtc.c                ambrtc
-- 
2.47.1

Reply via email to