1 /* $OpenBSD: octrtc.c,v 1.15 2022/10/12 13:39:50 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2013, 2014 Paul Irofti. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/proc.h> 23 24 #include <dev/clock_subr.h> 25 26 #include <machine/bus.h> 27 #include <machine/autoconf.h> 28 #include <machine/octeonvar.h> 29 30 #ifdef OCTRTC_DEBUG 31 #define DPRINTF(x) printf x 32 #else 33 #define DPRINTF(x) 34 #endif 35 36 #define MIO_TWS_SW_TWSI 0x0001180000001000ULL 37 #define MIO_TWS_SW_TWSI_EXT 0x0001180000001018ULL 38 #define OCTRTC_REG 0x68 39 40 struct octrtc_softc { 41 struct device sc_dev; 42 struct todr_chip_handle sc_todr; 43 }; 44 45 struct cfdriver octrtc_cd = { 46 NULL, "octrtc", DV_DULL 47 }; 48 49 int octrtc_match(struct device *, void *, void *); 50 void octrtc_attach(struct device *, struct device *, void *); 51 52 int octrtc_gettime(struct todr_chip_handle *, struct timeval *); 53 int octrtc_read(uint8_t *, char); 54 55 int octrtc_settime(struct todr_chip_handle *, struct timeval *); 56 int octrtc_write(uint8_t); 57 58 59 const struct cfattach octrtc_ca = { 60 sizeof(struct octrtc_softc), octrtc_match, octrtc_attach, 61 }; 62 63 64 union mio_tws_sw_twsi_reg { 65 uint64_t reg; 66 struct cvmx_mio_twsx_sw_twsi_s { 67 uint64_t v:1; /* Valid bit */ 68 uint64_t slonly:1; /* Slave Only Mode */ 69 uint64_t eia:1; /* Extended Internal Address */ 70 uint64_t op:4; /* Opcode field */ 71 uint64_t r:1; /* Read bit or result */ 72 uint64_t sovr:1; /* Size Override */ 73 uint64_t size:3; /* Size in bytes */ 74 uint64_t scr:2; /* Scratch, unused */ 75 uint64_t a:10; /* Address field */ 76 uint64_t ia:5; /* Internal Address */ 77 uint64_t eop_ia:3; /* Extra opcode */ 78 uint64_t d:32; /* Data Field */ 79 } field; 80 }; 81 82 83 static const enum octeon_board no_rtc_boards[] = { 84 BOARD_UBIQUITI_E100, 85 BOARD_UBIQUITI_E120, 86 BOARD_UBIQUITI_E200, 87 BOARD_UBIQUITI_E220, 88 BOARD_UBIQUITI_E300, 89 BOARD_UBIQUITI_E1000, 90 BOARD_RHINOLABS_UTM8, 91 }; 92 93 int 94 octrtc_match(struct device *parent, void *match, void *aux) 95 { 96 struct mainbus_attach_args *maa = aux; 97 struct cfdata *cf = match; 98 int i; 99 100 if (strcmp(maa->maa_name, cf->cf_driver->cd_name) != 0) 101 return 0; 102 for (i = 0; i < nitems(no_rtc_boards); i++) 103 if (octeon_board == no_rtc_boards[i]) 104 return 0; 105 return 1; 106 } 107 108 void 109 octrtc_attach(struct device *parent, struct device *self, void *aux) 110 { 111 struct octrtc_softc *sc = (struct octrtc_softc *)self; 112 113 sc->sc_todr.cookie = sc; 114 sc->sc_todr.todr_gettime = octrtc_gettime; 115 sc->sc_todr.todr_settime = octrtc_settime; 116 sc->sc_todr.todr_quality = 0; 117 todr_attach(&sc->sc_todr); 118 119 printf(": DS1337\n"); 120 } 121 122 int 123 octrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 124 { 125 struct clock_ymdhms dt; 126 uint8_t tod[8]; 127 uint8_t check; 128 int i, rc; 129 130 int nretries = 2; 131 132 DPRINTF(("\nTOD: ")); 133 while (nretries--) { 134 rc = octrtc_read(&tod[0], 1); /* ia read */ 135 if (rc) { 136 DPRINTF(("octrtc_read(0) failed %d", rc)); 137 return EIO; 138 } 139 140 for (i = 1; i < 8; i++) { 141 rc = octrtc_read(&tod[i], 0); /* current address */ 142 if (rc) { 143 DPRINTF(("octrtc_read(%d) failed %d", i, rc)); 144 return EIO; 145 } 146 DPRINTF(("%#X ", tod[i])); 147 } 148 149 /* Check against time-wrap */ 150 rc = octrtc_read(&check, 1); /* ia read */ 151 if (rc) { 152 DPRINTF(("octrtc_read(check) failed %d", rc)); 153 return EIO; 154 } 155 if ((check & 0xf) == (tod[0] & 0xf)) 156 break; 157 } 158 DPRINTF(("\n")); 159 160 DPRINTF(("Time: %d %d %d (%d) %02d:%02d:%02d\n", 161 ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]), /* year */ 162 FROMBCD(tod[5] & 0x1f), /* month */ 163 FROMBCD(tod[4] & 0x3f), /* day */ 164 (tod[3] & 0x7), /* day of the week */ 165 FROMBCD(tod[2] & 0x3f), /* hour */ 166 FROMBCD(tod[1] & 0x7f), /* minute */ 167 FROMBCD(tod[0] & 0x7f))); /* second */ 168 169 dt.dt_year = ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]); 170 dt.dt_mon = FROMBCD(tod[5] & 0x1f); 171 dt.dt_day = FROMBCD(tod[4] & 0x3f); 172 dt.dt_hour = FROMBCD(tod[2] & 0x3f); 173 if ((tod[2] & 0x40) && (tod[2] & 0x20)) /* adjust AM/PM format */ 174 dt.dt_hour = (dt.dt_hour + 12) % 24; 175 dt.dt_min = FROMBCD(tod[1] & 0x7f); 176 dt.dt_sec = FROMBCD(tod[0] & 0x7f); 177 178 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 || 179 dt.dt_day > 31 || dt.dt_day == 0 || 180 dt.dt_mon > 12 || dt.dt_mon == 0 || 181 dt.dt_year < POSIX_BASE_YEAR) 182 return EINVAL; 183 184 tv->tv_sec = clock_ymdhms_to_secs(&dt); 185 tv->tv_usec = 0; 186 return 0; 187 } 188 189 int 190 octrtc_read(uint8_t *data, char ia) 191 { 192 int nretries = 5; 193 union mio_tws_sw_twsi_reg twsi; 194 195 again: 196 twsi.reg = 0; 197 twsi.field.v = 1; 198 twsi.field.r = 1; 199 twsi.field.sovr = 1; 200 twsi.field.a = OCTRTC_REG; 201 if (ia) { 202 twsi.field.op = 1; 203 } 204 205 octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg); 206 /* The 1st bit is cleared when the operation is complete */ 207 do { 208 delay(1000); 209 twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI); 210 } while (twsi.field.v); 211 DPRINTF(("%#llX ", twsi.reg)); 212 213 /* 214 * The data field is in the upper 32 bits and we're only 215 * interested in the first byte. 216 */ 217 *data = twsi.field.d & 0xff; 218 219 /* 8th bit is the read result: 1 = success, 0 = failure */ 220 if (twsi.field.r == 0) { 221 /* 222 * Lost arbitration : 0x38, 0x68, 0xB0, 0x78 223 * Core busy as slave: 0x80, 0x88, 0xA0, 0xA8, 0xB8, 0xC0, 0xC8 224 */ 225 if (*data == 0x38 || *data == 0x68 || *data == 0xB0 || 226 *data == 0x78 || *data == 0x80 || *data == 0x88 || 227 *data == 0xA0 || *data == 0xA8 || *data == 0xB8 || 228 *data == 0xC0 || *data == 0xC8) 229 if (nretries--) 230 goto again; 231 return EIO; 232 } 233 234 return 0; 235 } 236 237 int 238 octrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 239 { 240 struct clock_ymdhms dt; 241 int nretries = 2; 242 int rc, i; 243 uint8_t tod[8]; 244 uint8_t check; 245 246 clock_secs_to_ymdhms(tv->tv_sec, &dt); 247 248 DPRINTF(("settime: %d %d %d (%d) %02d:%02d:%02d\n", 249 dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_wday, 250 dt.dt_hour, dt.dt_min, dt.dt_sec)); 251 252 tod[0] = TOBCD(dt.dt_sec); 253 tod[1] = TOBCD(dt.dt_min); 254 tod[2] = TOBCD(dt.dt_hour); 255 tod[3] = TOBCD(dt.dt_wday + 1); 256 tod[4] = TOBCD(dt.dt_day); 257 tod[5] = TOBCD(dt.dt_mon); 258 if (dt.dt_year >= 2000) 259 tod[5] |= 0x80; 260 tod[6] = TOBCD(dt.dt_year % 100); 261 262 while (nretries--) { 263 for (i = 0; i < 7; i++) { 264 rc = octrtc_write(tod[i]); 265 if (rc) { 266 DPRINTF(("octrtc_write(%d) failed %d", i, rc)); 267 return EIO; 268 } 269 } 270 271 rc = octrtc_read(&check, 1); 272 if (rc) { 273 DPRINTF(("octrtc_read(check) failed %d", rc)); 274 return EIO; 275 } 276 277 if ((check & 0xf) == (tod[0] & 0xf)) 278 break; 279 } 280 281 return 0; 282 } 283 284 int 285 octrtc_write(uint8_t data) 286 { 287 union mio_tws_sw_twsi_reg twsi; 288 int npoll = 128; 289 290 twsi.reg = 0; 291 twsi.field.v = 1; 292 twsi.field.sovr = 1; 293 twsi.field.op = 1; 294 twsi.field.a = OCTRTC_REG; 295 twsi.field.d = data & 0xffffffff; 296 297 octeon_xkphys_write_8(MIO_TWS_SW_TWSI_EXT, 0); 298 octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg); 299 do { 300 delay(1000); 301 twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI); 302 } while (twsi.field.v); 303 304 /* Try to read back */ 305 while (npoll-- && octrtc_read(&data, 0)); 306 307 return npoll ? 0 : EIO; 308 } 309