On Mon, Dec 30, 2013 at 06:49:38PM +0200, Paul Irofti wrote: > On Mon, Dec 30, 2013 at 02:47:58PM +0200, Paul Irofti wrote: > > The following enables the DS1337 RTC clock found on octeon boards. > > > > I've tested it on my DSR-500 and would love to hear about tests on other > > machines. > > > > Comments? Okays? > > New diff after a helpful tip from miod
Better diff. There still seems to be problems with setting the time. I'll look into it in the morning. Index: conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/octeon/conf/GENERIC,v retrieving revision 1.11 diff -u -p -r1.11 GENERIC --- conf/GENERIC 24 Oct 2013 20:47:08 -0000 1.11 +++ conf/GENERIC 31 Dec 2013 00:21:31 -0000 @@ -31,6 +31,8 @@ clock0 at mainbus0 iobus0 at mainbus0 uartbus0 at mainbus0 +octrtc0 at mainbus0 + octcf0 at iobus0 octrng0 at iobus0 Index: conf/files.octeon =================================================================== RCS file: /cvs/src/sys/arch/octeon/conf/files.octeon,v retrieving revision 1.16 diff -u -p -r1.16 files.octeon --- conf/files.octeon 4 Nov 2013 14:07:16 -0000 1.16 +++ conf/files.octeon 31 Dec 2013 00:21:31 -0000 @@ -48,6 +48,11 @@ attach cpu at mainbus device clock attach clock at mainbus +# TOD clock +device octrtc +attach octrtc at mainbus +file arch/octeon/dev/octrtc.c octrtc + define iobus {[base = -1]} device iobus attach iobus at mainbus Index: dev/mainbus.c =================================================================== RCS file: /cvs/src/sys/arch/octeon/dev/mainbus.c,v retrieving revision 1.5 diff -u -p -r1.5 mainbus.c --- dev/mainbus.c 25 Jun 2011 19:39:32 -0000 1.5 +++ dev/mainbus.c 31 Dec 2013 00:21:31 -0000 @@ -86,6 +86,10 @@ mainbus_attach(struct device *parent, st /* on-board I/O */ caa.caa_maa.maa_name = "iobus"; config_found(self, &caa.caa_maa, mainbus_print); + + caa.caa_maa.maa_name = "octrtc"; + config_found(self, &caa.caa_maa, mainbus_print); + } int Index: dev/octrtc.c =================================================================== RCS file: dev/octrtc.c diff -N dev/octrtc.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/octrtc.c 31 Dec 2013 00:21:31 -0000 @@ -0,0 +1,272 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013 Paul Irofti. + * + * 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/proc.h> + +#include <mips64/dev/clockvar.h> + +#include <machine/bus.h> +#include <machine/autoconf.h> +#include <machine/octeonvar.h> + +#ifdef OCTRTC_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +#define MIO_TWS_SW_TWSI 0x0001180000001000ULL +#define MIO_TWS_SW_TWSI_EXT 0x0001180000001018ULL +#define OCTRTC_REG 0x68 + +struct cfdriver octrtc_cd = { + NULL, "octrtc", DV_DULL +}; + +int octrtc_match(struct device *, void *, void *); +void octrtc_attach(struct device *, struct device *, void *); + +void octrtc_gettime(void *, time_t, struct tod_time *); +int octrtc_read(uint8_t *, char); + +void octrtc_settime(void *, struct tod_time *); +int octrtc_write(uint8_t); + + +struct cfattach octrtc_ca = { + sizeof(struct device), octrtc_match, octrtc_attach, +}; + + +union mio_tws_sw_twsi_reg { + uint64_t reg; + struct cvmx_mio_twsx_sw_twsi_s { + uint64_t v:1; /* Valid bit */ + uint64_t slonly:1; /* Slave Only Mode */ + uint64_t eia:1; /* Extended Internal Address */ + uint64_t op:4; /* Opcode field */ + uint64_t r:1; /* Read bit or result */ + uint64_t sovr:1; /* Size Override */ + uint64_t size:3; /* Size in bytes */ + uint64_t scr:2; /* Scratch, unused */ + uint64_t a:10; /* Address field */ + uint64_t ia:5; /* Internal Address */ + uint64_t eop_ia:3; /* Extra opcode */ + uint64_t d:32; /* Data Field */ + } field; +}; + + +int +octrtc_match(struct device *parent, void *match, void *aux) +{ + struct mainbus_attach_args *maa = aux; + struct cfdata *cf = match; + + if (strcmp(maa->maa_name, cf->cf_driver->cd_name) != 0) + return 0; + return 1; +} + +void +octrtc_attach(struct device *parent, struct device *self, void *aux) +{ + struct octrtc_softc *sc = (struct octrtc_softc *)self; + + sys_tod.tod_cookie = sc; + sys_tod.tod_get = octrtc_gettime; + sys_tod.tod_set = octrtc_settime; + + printf(": DS1337\n"); +} + +void +octrtc_gettime(void *cookie, time_t unused, struct tod_time *tt) +{ + uint8_t tod[8]; + uint8_t check; + int i, rc; + + int nretries = 2; + + DPRINTF(("\nTOD: ")); + while (nretries--) { + rc = octrtc_read(&tod[0], 1); /* ia read */ + if (rc) { + DPRINTF(("octrtc_read(0) failed %d", rc)); + return; + } + + for (i = 1; i < 8; i++) { + rc = octrtc_read(&tod[i], 0); /* current address */ + if (rc) { + DPRINTF(("octrtc_read(%d) failed %d", i, rc)); + return; + } + DPRINTF(("%#X ", tod[i])); + } + + /* Check against time-wrap */ + rc = octrtc_read(&check, 1); /* ia read */ + if (rc) { + DPRINTF(("octrtc_read(check) failed %d", i, rc)); + return; + } + if ((check & 0xf) == (tod[0] & 0xf)) + break; + } + DPRINTF(("\n")); + + DPRINTF(("Time: %d %d %d (%d) %02d:%02d:%02d\n", + ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]), /* year */ + FROMBCD(tod[5] & 0x1f), /* month */ + FROMBCD(tod[4] & 0x3f), /* day */ + (tod[3] & 0x7), /* day of the week */ + FROMBCD(tod[2] & 0x3f), /* hour */ + FROMBCD(tod[1] & 0x7f), /* minute */ + FROMBCD(tod[0] & 0x7f))); /* second */ + + tt->year = ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]); + tt->mon = FROMBCD(tod[5] & 0x1f); + tt->day = FROMBCD(tod[4] & 0x3f); + tt->dow = (tod[3] & 0x7); + tt->hour = FROMBCD(tod[2] & 0x3f); + if ((tod[2] & 0x40) && (tod[2] & 0x20)) /* adjust AM/PM format */ + tt->hour = (tt->hour + 12) % 24; + tt->min = FROMBCD(tod[1] & 0x7f); + tt->sec = FROMBCD(tod[0] & 0x7f); +} + +int +octrtc_read(uint8_t *data, char ia) +{ + int nretries = 5; + union mio_tws_sw_twsi_reg twsi; + +again: + twsi.reg = 0; + twsi.field.v = 1; + twsi.field.r = 1; + twsi.field.sovr = 1; + twsi.field.a = OCTRTC_REG; + if (ia) { + twsi.field.op = 1; + } + + octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg); + /* The 1st bit is cleared when the operation is complete */ + do { + delay(1000); + twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI); + } while (twsi.field.v); + DPRINTF(("%#llX ", twsi.reg)); + + /* + * The data field is in the upper 32 bits and we're only + * interested in the first byte. + */ + *data = twsi.field.d & 0xff; + + /* 8th bit is the read result: 1 = success, 0 = failure */ + if (twsi.field.r == 0) { + /* + * Lost arbitration : 0x38, 0x68, 0xB0, 0x78 + * Core busy as slave: 0x80, 0x88, 0xA0, 0xA8, 0xB8, 0xC0, 0xC8 + */ + if (*data == 0x38 || *data == 0x68 || *data == 0xB0 || + *data == 0x78 || *data == 0x80 || *data == 0x88 || + *data == 0xA0 || *data == 0xA8 || *data == 0xB8 || + *data == 0xC0 || *data == 0xC8) + if (nretries--) + goto again; + return EIO; + } + + return 0; +} + +void +octrtc_settime(void *cookie, struct tod_time *tt) +{ + int nretries = 2; + int rc, i; + uint8_t tod[8]; + uint8_t check; + + DPRINTF(("settime: %d %d %d (%d) %02d:%02d:%02d\n", + tt->year, tt->mon, tt->day, tt->dow, + tt->hour, tt->min, tt->sec)); + + tod[0] = TOBCD(tt->sec); + tod[1] = TOBCD(tt->min); + tod[2] = TOBCD(tt->hour); + tod[3] = TOBCD(tt->dow); + tod[4] = TOBCD(tt->day); + tod[5] = TOBCD(tt->mon); + if (tt->year >= 2000) + tod[5] |= 0x80; + tod[6] = TOBCD(tt->year % 100); + + while (nretries--) { + for (i = 0; i < 7; i++) { + rc = octrtc_write(tod[i]); + if (rc) { + DPRINTF(("octrtc_write(%d) failed %d", i, rc)); + return; + } + } + + rc = octrtc_read(&check, 1); + if (rc) { + DPRINTF(("octrtc_read(check) failed %d", rc)); + return; + } + + if ((check & 0xf) == (tod[0] & 0xf)) + break; + } +} + +int +octrtc_write(uint8_t data) +{ + union mio_tws_sw_twsi_reg twsi; + int npoll = 128; + + twsi.reg = 0; + twsi.field.v = 1; + twsi.field.sovr = 1; + twsi.field.op = 1; + twsi.field.a = OCTRTC_REG; + twsi.field.d = data & 0xffffffff; + + octeon_xkphys_write_8(MIO_TWS_SW_TWSI_EXT, 0); + octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg); + do { + delay(1000); + twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI); + } while (twsi.field.v); + + /* Try to read back */ + while (npoll-- && octrtc_read(&data, 0)); + + return npoll ? 0 : EIO; +}