From: Michael Davidsaver <mdavidsaver@gmail.com>
To: Alexander Graf <agraf@suse.de>,
David Gibson <david@gibson.dropbear.id.au>,
qemu-ppc@nongnu.org
Cc: qemu-devel@nongnu.org, Michael Davidsaver <mdavidsaver@gmail.com>
Subject: [Qemu-devel] [PATCH 10/12] timer: add ds1375 RTC
Date: Sun, 19 Nov 2017 21:24:18 -0600 [thread overview]
Message-ID: <20171120032420.9134-11-mdavidsaver@gmail.com> (raw)
In-Reply-To: <20171120032420.9134-1-mdavidsaver@gmail.com>
only basic functionality implemented (read time and sram).
no set time or alarms.
Signed-off-by: Michael Davidsaver <mdavidsaver@gmail.com>
---
default-configs/ppc-softmmu.mak | 1 +
hw/timer/Makefile.objs | 1 +
hw/timer/ds1375-i2c.c | 293 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 295 insertions(+)
create mode 100644 hw/timer/ds1375-i2c.c
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index bb225c6e46..04bfa79154 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -52,3 +52,4 @@ CONFIG_SERIAL_ISA=y
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
CONFIG_RS6000_MC=y
+CONFIG_DS1375=y
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 8c19eac3b6..6521d47367 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -4,6 +4,7 @@ common-obj-$(CONFIG_ARM_V7M) += armv7m_systick.o
common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o
common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
common-obj-$(CONFIG_DS1338) += ds1338.o
+common-obj-$(CONFIG_DS1375) += ds1375-i2c.o
common-obj-$(CONFIG_HPET) += hpet.o
common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
common-obj-$(CONFIG_M48T59) += m48t59.o
diff --git a/hw/timer/ds1375-i2c.c b/hw/timer/ds1375-i2c.c
new file mode 100644
index 0000000000..dba9cc05c4
--- /dev/null
+++ b/hw/timer/ds1375-i2c.c
@@ -0,0 +1,293 @@
+/*
+ * Dallas/Maxim ds1375 I2C RTC w/ SRAM
+ *
+ * Copyright (c) 2017 Michael Davidsaver
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the LICENSE file in the top-level directory.
+ *
+ * Only basic functionality is modeled (time and user SRAM).
+ * Alarms not modeled.
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+#include "qemu/bcd.h"
+#include "hw/hw.h"
+#include "hw/registerfields.h"
+#include "hw/i2c/i2c.h"
+
+#define DEBUG_DS1375
+
+#ifdef DEBUG_DS1375
+#define DPRINTK(FMT, ...) printf(TYPE_DS1375 " : " FMT, ## __VA_ARGS__)
+#else
+#define DPRINTK(FMT, ...) do {} while (0)
+#endif
+
+#define LOG(MSK, FMT, ...) qemu_log_mask(MSK, TYPE_DS1375 " : " FMT, \
+ ## __VA_ARGS__)
+
+#define TYPE_DS1375 "ds1375"
+#define DS1375(obj) OBJECT_CHECK(DS1375State, (obj), TYPE_DS1375)
+
+#define DS1375_REGSIZE 0x20
+
+#define R_SEC (0x0)
+#define R_MIN (0x1)
+#define R_HOUR (0x2)
+#define R_WDAY (0x3)
+#define R_DATE (0x4)
+#define R_MONTH (0x5)
+#define R_YEAR (0x6)
+#define R_A1SEC (0x7)
+#define R_A1MIN (0x8)
+#define R_A1HOUR (0x9)
+#define R_A1DAY (0xa)
+#define R_A2SEC (0xb)
+#define R_A2MIN (0xc)
+#define R_A2HOUR (0xd)
+#define R_CTRL (0xe)
+#define R_STS (0xf)
+
+FIELD(HOUR, SET12, 6, 1)
+FIELD(HOUR, HOUR24, 0, 6)
+FIELD(HOUR, AMPM, 5, 1)
+FIELD(HOUR, HOUR12, 0, 5)
+
+FIELD(MONTH, MONTH, 0, 5)
+FIELD(MONTH, CENTURY, 7, 1)
+
+FIELD(CTRL, ECLK, 7, 1)
+FIELD(CTRL, CLKSEL, 5, 2)
+FIELD(CTRL, RS, 3, 2)
+FIELD(CTRL, INTCN, 2, 1)
+FIELD(CTRL, A2IE, 1, 1)
+FIELD(CTRL, A1IE, 0, 1)
+
+typedef struct DS1375State {
+ I2CSlave parent_obj;
+
+ /* register address counter */
+ uint8_t addr;
+ /* when writing, whether the address has been sent */
+ bool addrd;
+
+ int time_offset;
+
+ uint8_t regs[DS1375_REGSIZE];
+} DS1375State;
+
+/* update current time register if clock enabled */
+static
+void ds1375_latch(DS1375State *ds)
+{
+ struct tm now;
+
+ if (!ARRAY_FIELD_EX32(ds->regs, CTRL, ECLK)) {
+ return;
+ }
+
+ qemu_get_timedate(&now, ds->time_offset);
+
+ DPRINTK("Current Time %3u/%2u/%u %2u:%2u:%2u (wday %u)\n",
+ now.tm_year, now.tm_mon, now.tm_mday,
+ now.tm_hour, now.tm_min, now.tm_sec,
+ now.tm_wday);
+
+ /* ensure unused bits are zero */
+ memset(ds->regs, 0, R_YEAR + 1);
+
+ ds->regs[R_SEC] = to_bcd(now.tm_sec);
+ ds->regs[R_MIN] = to_bcd(now.tm_min);
+
+ if (ARRAY_FIELD_EX32(ds->regs, HOUR, SET12) == 0) {
+ /* 24 hour */
+ ARRAY_FIELD_DP32(ds->regs, HOUR, HOUR24, to_bcd(now.tm_hour));
+ } else {
+ /* 12 hour am/pm */
+ ARRAY_FIELD_DP32(ds->regs, HOUR, AMPM, now.tm_hour >= 12);
+ ARRAY_FIELD_DP32(ds->regs, HOUR, HOUR12, to_bcd(now.tm_hour % 12u));
+ }
+
+ ds->regs[R_WDAY] = now.tm_wday; /* day of the week */
+ ds->regs[R_DATE] = to_bcd(now.tm_mday);
+
+ ARRAY_FIELD_DP32(ds->regs, MONTH, MONTH, to_bcd(now.tm_mon + 1));
+ ARRAY_FIELD_DP32(ds->regs, MONTH, CENTURY, now.tm_year > 99);
+
+ ds->regs[R_YEAR] = to_bcd(now.tm_year % 100u);
+
+ DPRINTK("Latched time\n");
+}
+
+static
+void ds1375_update(DS1375State *ds)
+{
+ struct tm now;
+
+ now.tm_sec = from_bcd(ds->regs[R_SEC]);
+ now.tm_min = from_bcd(ds->regs[R_MIN]);
+
+ if (ARRAY_FIELD_EX32(ds->regs, HOUR, SET12)) {
+ now.tm_hour = from_bcd(ARRAY_FIELD_EX32(ds->regs, HOUR, HOUR12));
+ if (ARRAY_FIELD_EX32(ds->regs, HOUR, AMPM)) {
+ now.tm_hour += 12;
+ }
+
+ } else {
+ now.tm_hour = from_bcd(ARRAY_FIELD_EX32(ds->regs, HOUR, HOUR24));
+ }
+
+ now.tm_wday = from_bcd(ds->regs[R_WDAY]);
+ now.tm_mday = from_bcd(ds->regs[R_DATE]);
+ now.tm_mon = from_bcd(ARRAY_FIELD_EX32(ds->regs, MONTH, MONTH)) - 1;
+
+ now.tm_year = from_bcd(ds->regs[R_YEAR]) % 100u;
+ if (ARRAY_FIELD_EX32(ds->regs, MONTH, CENTURY)) {
+ now.tm_year += 100;
+ }
+
+ DPRINTK("New Time %3u/%2u/%u %2u:%2u:%2u (wday %u)\n",
+ now.tm_year, now.tm_mon, now.tm_mday,
+ now.tm_hour, now.tm_min, now.tm_sec,
+ now.tm_wday);
+
+ ds->time_offset = qemu_timedate_diff(&now);
+ DPRINTK("Update offset = %d\n", ds->time_offset);
+}
+
+static
+int ds1375_event(I2CSlave *s, enum i2c_event event)
+{
+ DS1375State *ds = container_of(s, DS1375State, parent_obj);
+
+ switch (event) {
+ case I2C_START_SEND:
+ ds->addrd = false;
+ case I2C_START_RECV:
+ ds1375_latch(ds);
+ case I2C_FINISH:
+ DPRINTK("Event %d\n", (int)event);
+ case I2C_NACK:
+ break;
+ }
+ return 0;
+}
+
+static
+int ds1375_recv(I2CSlave *s)
+{
+ DS1375State *ds = container_of(s, DS1375State, parent_obj);
+ int ret = 0;
+
+ switch (ds->addr) {
+ case R_SEC ... R_YEAR:
+ case R_CTRL:
+ case R_STS:
+ case 0x10 ... 0x1f:
+ ret = ds->regs[ds->addr];
+ break;
+ default:
+ LOG(LOG_UNIMP, "Read from unimplemented (%02x) %02x\n", ds->addr, ret);
+ }
+
+ DPRINTK("Recv (%02x) %02x\n", ds->addr, ret);
+
+ ds->addr++;
+ ds->addr &= 0x1f;
+ if (ds->addr == 0) {
+ ds1375_latch(ds);
+ }
+
+ return ret;
+}
+
+static
+int ds1375_send(I2CSlave *s, uint8_t data)
+{
+ DS1375State *ds = container_of(s, DS1375State, parent_obj);
+
+ if (!ds->addrd) {
+ data &= 0x1f;
+ ds->addr = data;
+ DPRINTK("Set address pointer %02x\n", data);
+ ds->addrd = true;
+ return 0;
+
+ } else {
+ DPRINTK("Send (%02x) %02x\n", ds->addr, data);
+ switch (ds->addr) {
+ case R_SEC ... R_YEAR:
+ ds->regs[ds->addr] = data;
+ ds1375_update(ds);
+ break;
+ case R_CTRL:
+ if (data & 0x7) {
+ LOG(LOG_UNIMP, "Alarm interrupt/output not modeled\n");
+ }
+ ds->regs[ds->addr] = data;
+ break;
+ case 0x10 ... 0x1f:
+ ds->regs[ds->addr] = data;
+ break;
+ default:
+ LOG(LOG_UNIMP, "Write to unimplemented (%02x) %02x\n",
+ ds->addr, data);
+ }
+
+ ds->addr++;
+ ds->addr &= 0x1f;
+ if (ds->addr == 0) {
+ ds1375_latch(ds);
+ }
+
+ return 0;
+ }
+}
+
+static
+void ds1375_reset(DeviceState *device)
+{
+ DS1375State *ds = DS1375(device);
+
+ memset(ds->regs, 0, sizeof(ds->regs));
+ /* TODO: not clear SRAM? */
+
+ /* Default to 12-hour mode */
+ ARRAY_FIELD_DP32(ds->regs, CTRL, ECLK, 1);
+
+ ds->addr = 0;
+
+ /* do not re-zero time offset */
+}
+
+static
+void ds1375_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->event = &ds1375_event;
+ k->recv = &ds1375_recv;
+ k->send = &ds1375_send;
+
+ dc->reset = &ds1375_reset;
+}
+
+static
+const TypeInfo ds1375_type = {
+ .name = TYPE_DS1375,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(DS1375State),
+ .class_size = sizeof(I2CSlaveClass),
+ .class_init = ds1375_class_init,
+};
+
+static void ds1375_register(void)
+{
+ type_register_static(&ds1375_type);
+}
+
+type_init(ds1375_register)
--
2.11.0
next prev parent reply other threads:[~2017-11-20 3:25 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-11-20 3:24 [Qemu-devel] [PATCH 00/12] Add MVME3100 PPC SBC Michael Davidsaver
2017-11-20 3:24 ` [Qemu-devel] [PATCH 01/12] e500: add board config options Michael Davidsaver
2017-11-22 3:28 ` David Gibson
2017-11-22 17:55 ` Michael Davidsaver
2017-11-23 16:07 ` [Qemu-devel] [Qemu-ppc] " Cédric Le Goater
2017-11-24 0:21 ` [Qemu-devel] " David Gibson
2017-11-20 3:24 ` [Qemu-devel] [PATCH 03/12] e500: note possible bug with host bridge Michael Davidsaver
2017-11-22 3:46 ` David Gibson
2017-11-22 4:57 ` Michael Davidsaver
2017-11-20 3:24 ` [Qemu-devel] [PATCH 04/12] e500: additional CCSR registers Michael Davidsaver
2017-11-22 3:57 ` David Gibson
2017-11-20 3:24 ` [Qemu-devel] [PATCH 05/12] e500: name openpic and pci host bridge Michael Davidsaver
2017-11-22 3:58 ` David Gibson
2017-11-20 3:24 ` [Qemu-devel] [PATCH 06/12] i2c: add mpc8540 i2c controller Michael Davidsaver
2017-11-22 4:06 ` David Gibson
2017-11-23 15:39 ` [Qemu-devel] [Qemu-ppc] " Cédric Le Goater
2017-11-24 0:13 ` David Gibson
2017-11-20 3:24 ` [Qemu-devel] [PATCH 07/12] qtest: add e500_i2c_create() Michael Davidsaver
2017-11-20 3:24 ` [Qemu-devel] [PATCH 08/12] e500: add mpc8540 i2c controller to ccsr Michael Davidsaver
2017-11-22 4:08 ` David Gibson
2017-11-22 16:46 ` Michael Davidsaver
2017-11-20 3:24 ` [Qemu-devel] [PATCH 09/12] nvram: add AT24Cx i2c eeprom Michael Davidsaver
2017-11-22 4:10 ` David Gibson
2017-11-20 3:24 ` Michael Davidsaver [this message]
2017-11-22 4:11 ` [Qemu-devel] [PATCH 10/12] timer: add ds1375 RTC David Gibson
2017-11-20 3:24 ` [Qemu-devel] [PATCH 11/12] ppc: add mvme3100 machine Michael Davidsaver
2017-11-20 3:24 ` [Qemu-devel] [PATCH 12/12] tests: add mvme3100-test Michael Davidsaver
[not found] ` <20171120032420.9134-3-mdavidsaver@gmail.com>
2017-11-22 3:36 ` [Qemu-devel] [PATCH 02/12] e500: consolidate mpc8540 guts with e500-ccsr David Gibson
2017-12-06 3:12 ` David Gibson
2017-12-06 3:18 ` Michael Davidsaver
2017-12-06 4:23 ` David Gibson
2017-11-22 4:12 ` [Qemu-devel] [PATCH 00/12] Add MVME3100 PPC SBC David Gibson
2017-11-22 4:58 ` Michael Davidsaver
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20171120032420.9134-11-mdavidsaver@gmail.com \
--to=mdavidsaver@gmail.com \
--cc=agraf@suse.de \
--cc=david@gibson.dropbear.id.au \
--cc=qemu-devel@nongnu.org \
--cc=qemu-ppc@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.