1 /* $OpenBSD: mbg.c,v 1.37 2024/05/24 06:02:58 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org> 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/device.h> 21 #include <sys/timeout.h> 22 #include <sys/systm.h> 23 #include <sys/sensors.h> 24 #include <sys/time.h> 25 26 #include <machine/bus.h> 27 28 #include <dev/pci/pcivar.h> 29 #include <dev/pci/pcireg.h> 30 #include <dev/pci/pcidevs.h> 31 32 struct mbg_softc { 33 struct device sc_dev; 34 bus_space_tag_t sc_iot; 35 bus_space_handle_t sc_ioh; 36 37 /* 38 * I/O region used by the AMCC S5920 found on the PCI509 card 39 * used to access the data. 40 */ 41 bus_space_tag_t sc_iot_s5920; 42 bus_space_handle_t sc_ioh_s5920; 43 44 struct ksensor sc_timedelta; 45 struct ksensor sc_signal; 46 struct ksensordev sc_sensordev; 47 struct timeout sc_timeout; /* invalidate sensor */ 48 int sc_trust; /* trust time in seconds */ 49 50 int (*sc_read)(struct mbg_softc *, int cmd, 51 char *buf, size_t len, 52 struct timespec *tstamp); 53 }; 54 55 struct mbg_time { 56 u_int8_t hundreds; 57 u_int8_t sec; 58 u_int8_t min; 59 u_int8_t hour; 60 u_int8_t mday; 61 u_int8_t wday; /* 1 (monday) - 7 (sunday) */ 62 u_int8_t mon; 63 u_int8_t year; /* 0 - 99 */ 64 u_int8_t status; 65 u_int8_t signal; 66 int8_t utc_off; 67 }; 68 69 struct mbg_time_hr { 70 u_int32_t sec; /* always UTC */ 71 u_int32_t frac; /* fractions of second */ 72 int32_t utc_off; /* informal only, in seconds */ 73 u_int16_t status; 74 u_int8_t signal; 75 }; 76 77 /* mbg_time.status bits */ 78 #define MBG_FREERUN 0x01 /* clock running on xtal */ 79 #define MBG_DST_ENA 0x02 /* DST enabled */ 80 #define MBG_SYNC 0x04 /* clock synced at least once */ 81 #define MBG_DST_CHG 0x08 /* DST change announcement */ 82 #define MBG_UTC 0x10 /* special UTC firmware is installed */ 83 #define MBG_LEAP 0x20 /* announcement of a leap second */ 84 #define MBG_IFTM 0x40 /* current time was set from host */ 85 #define MBG_INVALID 0x80 /* time invalid, batt. was disconn. */ 86 87 /* AMCC S5920 registers */ 88 #define AMCC_DATA 0x00 /* data register, on 2nd IO region */ 89 #define AMCC_OMB 0x0c /* outgoing mailbox */ 90 #define AMCC_IMB 0x1c /* incoming mailbox */ 91 92 /* AMCC S5933 registers */ 93 #define AMCC_OMB1 0x00 /* outgoing mailbox 1 */ 94 #define AMCC_IMB4 0x1c /* incoming mailbox 4 */ 95 #define AMCC_FIFO 0x20 /* FIFO register */ 96 #define AMCC_INTCSR 0x38 /* interrupt control/status register */ 97 #define AMCC_MCSR 0x3c /* master control/status register */ 98 99 /* ASIC registers */ 100 #define ASIC_CFG 0x00 101 #define ASIC_FEATURES 0x08 /* r/o */ 102 #define ASIC_STATUS 0x10 103 #define ASIC_CTLSTATUS 0x14 104 #define ASIC_DATA 0x18 105 #define ASIC_RES1 0x1c 106 #define ASIC_ADDON 0x20 107 108 /* commands */ 109 #define MBG_GET_TIME 0x00 110 #define MBG_GET_SYNC_TIME 0x02 111 #define MBG_GET_TIME_HR 0x03 112 #define MBG_SET_TIME 0x10 113 #define MBG_GET_TZCODE 0x32 114 #define MBG_SET_TZCODE 0x33 115 #define MBG_GET_FW_ID_1 0x40 116 #define MBG_GET_FW_ID_2 0x41 117 #define MBG_GET_SERNUM 0x42 118 119 /* timezone codes (for MBG_{GET|SET}_TZCODE) */ 120 #define MBG_TZCODE_CET_CEST 0x00 121 #define MBG_TZCODE_CET 0x01 122 #define MBG_TZCODE_UTC 0x02 123 #define MBG_TZCODE_EET_EEST 0x03 124 125 /* misc. constants */ 126 #define MBG_FIFO_LEN 16 127 #define MBG_ID_LEN (2 * MBG_FIFO_LEN + 1) 128 #define MBG_BUSY 0x01 129 #define MBG_SIG_BIAS 55 130 #define MBG_SIG_MAX 68 131 #define NSECPERSEC 1000000000LL /* nanoseconds per second */ 132 #define HRDIVISOR 0x100000000LL /* for hi-res timestamp */ 133 134 int mbg_probe(struct device *, void *, void *); 135 void mbg_attach(struct device *, struct device *, void *); 136 void mbg_task(void *); 137 void mbg_task_hr(void *); 138 void mbg_update_sensor(struct mbg_softc *sc, struct timespec *tstamp, 139 int64_t timedelta, u_int8_t rsignal, u_int16_t status); 140 int mbg_read_amcc_s5920(struct mbg_softc *, int cmd, char *buf, size_t len, 141 struct timespec *tstamp); 142 int mbg_read_amcc_s5933(struct mbg_softc *, int cmd, char *buf, size_t len, 143 struct timespec *tstamp); 144 int mbg_read_asic(struct mbg_softc *, int cmd, char *buf, size_t len, 145 struct timespec *tstamp); 146 void mbg_timeout(void *); 147 148 const struct cfattach mbg_ca = { 149 sizeof(struct mbg_softc), mbg_probe, mbg_attach 150 }; 151 152 struct cfdriver mbg_cd = { 153 NULL, "mbg", DV_DULL 154 }; 155 156 const struct pci_matchid mbg_devices[] = { 157 { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_GPS170PCI }, 158 { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI32 }, 159 { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI509 }, 160 { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI510 }, 161 { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI511 }, 162 { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PEX511 }, 163 { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PZF180PEX } 164 }; 165 166 int 167 mbg_probe(struct device *parent, void *match, void *aux) 168 { 169 return pci_matchbyid((struct pci_attach_args *)aux, mbg_devices, 170 nitems(mbg_devices)); 171 } 172 173 void 174 mbg_attach(struct device *parent, struct device *self, void *aux) 175 { 176 struct mbg_softc *sc = (struct mbg_softc *)self; 177 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 178 struct mbg_time tframe; 179 pcireg_t memtype; 180 bus_size_t iosize, iosize2; 181 int bar = PCI_MAPREG_START, signal, t_trust; 182 const char *desc; 183 #ifdef MBG_DEBUG 184 char fw_id[MBG_ID_LEN]; 185 #endif 186 187 timeout_set(&sc->sc_timeout, mbg_timeout, sc); 188 189 /* for the PEX511 use BAR2 instead of BAR0*/ 190 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_MEINBERG_PEX511) 191 bar += 0x08; 192 193 memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, bar); 194 if (pci_mapreg_map(pa, bar, memtype, 0, &sc->sc_iot, 195 &sc->sc_ioh, NULL, &iosize, 0)) { 196 printf(": PCI %s region not found\n", 197 memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory"); 198 return; 199 } 200 201 if ((desc = pci_findproduct(pa->pa_id)) == NULL) 202 desc = "Radio clock"; 203 strlcpy(sc->sc_timedelta.desc, desc, sizeof(sc->sc_timedelta.desc)); 204 205 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 206 sizeof(sc->sc_sensordev.xname)); 207 208 sc->sc_timedelta.type = SENSOR_TIMEDELTA; 209 sc->sc_timedelta.status = SENSOR_S_UNKNOWN; 210 sensor_attach(&sc->sc_sensordev, &sc->sc_timedelta); 211 212 sc->sc_signal.type = SENSOR_PERCENT; 213 sc->sc_signal.status = SENSOR_S_UNKNOWN; 214 strlcpy(sc->sc_signal.desc, "Signal", sizeof(sc->sc_signal.desc)); 215 sensor_attach(&sc->sc_sensordev, &sc->sc_signal); 216 217 t_trust = 12 * 60 * 60; /* twelve hours */ 218 219 switch (PCI_PRODUCT(pa->pa_id)) { 220 case PCI_PRODUCT_MEINBERG_PCI32: 221 sc->sc_read = mbg_read_amcc_s5933; 222 sensor_task_register(sc, mbg_task, 10); 223 break; 224 case PCI_PRODUCT_MEINBERG_PCI509: 225 /* 226 * map the second I/O region needed in addition to the first 227 * to get at the actual data. 228 */ 229 memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, 230 PCI_MAPREG_START + 0x04); 231 if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x04, memtype, 0, 232 &sc->sc_iot_s5920, &sc->sc_ioh_s5920, NULL, &iosize2, 0)) { 233 printf(": PCI2 %s region not found\n", 234 memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory"); 235 236 /* unmap first mapped region as well if we fail */ 237 bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize); 238 return; 239 } 240 sc->sc_read = mbg_read_amcc_s5920; 241 sensor_task_register(sc, mbg_task, 10); 242 break; 243 case PCI_PRODUCT_MEINBERG_PCI510: 244 case PCI_PRODUCT_MEINBERG_PCI511: 245 case PCI_PRODUCT_MEINBERG_PEX511: 246 sc->sc_read = mbg_read_asic; 247 sensor_task_register(sc, mbg_task, 10); 248 break; 249 case PCI_PRODUCT_MEINBERG_GPS170PCI: 250 case PCI_PRODUCT_MEINBERG_PZF180PEX: 251 t_trust = 4 * 24 * 60 * 60; /* four days */ 252 sc->sc_read = mbg_read_asic; 253 sensor_task_register(sc, mbg_task_hr, 1); 254 break; 255 default: 256 /* this can not normally happen, but then there is murphy */ 257 panic(": unsupported product 0x%04x", PCI_PRODUCT(pa->pa_id)); 258 break; 259 } 260 261 sc->sc_trust = t_trust; 262 263 if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe, 264 sizeof(struct mbg_time), NULL)) { 265 printf(": unknown status"); 266 sc->sc_signal.status = SENSOR_S_CRIT; 267 } else { 268 sc->sc_signal.status = SENSOR_S_OK; 269 signal = tframe.signal - MBG_SIG_BIAS; 270 if (signal < 0) 271 signal = 0; 272 else if (signal > MBG_SIG_MAX) 273 signal = MBG_SIG_MAX; 274 sc->sc_signal.value = signal; 275 276 if (tframe.status & MBG_SYNC) 277 printf(": synchronized"); 278 else 279 printf(": not synchronized"); 280 if (tframe.status & MBG_FREERUN) { 281 sc->sc_signal.status = SENSOR_S_WARN; 282 printf(", free running"); 283 } 284 if (tframe.status & MBG_IFTM) 285 printf(", time set from host"); 286 } 287 #ifdef MBG_DEBUG 288 if (sc->sc_read(sc, MBG_GET_FW_ID_1, fw_id, MBG_FIFO_LEN, NULL) || 289 sc->sc_read(sc, MBG_GET_FW_ID_2, &fw_id[MBG_FIFO_LEN], MBG_FIFO_LEN, 290 NULL)) 291 printf(", firmware unknown"); 292 else { 293 fw_id[MBG_ID_LEN - 1] = '\0'; 294 printf(", firmware %s", fw_id); 295 } 296 #endif 297 printf("\n"); 298 sensordev_install(&sc->sc_sensordev); 299 timeout_add_sec(&sc->sc_timeout, sc->sc_trust); 300 } 301 302 /* 303 * mbg_task() reads a timestamp from cards that to not provide a high 304 * resolution timestamp. The precision is limited to 1/100 sec. 305 */ 306 void 307 mbg_task(void *arg) 308 { 309 struct mbg_softc *sc = (struct mbg_softc *)arg; 310 struct mbg_time tframe; 311 struct clock_ymdhms ymdhms; 312 struct timespec tstamp; 313 int64_t timedelta; 314 time_t trecv; 315 316 if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe, sizeof(tframe), 317 &tstamp)) { 318 sc->sc_signal.status = SENSOR_S_CRIT; 319 return; 320 } 321 if (tframe.status & MBG_INVALID) { 322 sc->sc_signal.status = SENSOR_S_CRIT; 323 return; 324 } 325 ymdhms.dt_year = tframe.year + 2000; 326 ymdhms.dt_mon = tframe.mon; 327 ymdhms.dt_day = tframe.mday; 328 ymdhms.dt_hour = tframe.hour; 329 ymdhms.dt_min = tframe.min; 330 ymdhms.dt_sec = tframe.sec; 331 trecv = clock_ymdhms_to_secs(&ymdhms) - tframe.utc_off * 3600; 332 333 timedelta = (int64_t)((tstamp.tv_sec - trecv) * 100 334 - tframe.hundreds) * 10000000LL + tstamp.tv_nsec; 335 336 mbg_update_sensor(sc, &tstamp, timedelta, tframe.signal, 337 (u_int16_t)tframe.status); 338 } 339 340 /* 341 * mbg_task_hr() reads a timestamp from cards that do provide a high 342 * resolution timestamp. 343 */ 344 void 345 mbg_task_hr(void *arg) 346 { 347 struct mbg_softc *sc = (struct mbg_softc *)arg; 348 struct mbg_time_hr tframe; 349 struct timespec tstamp; 350 int64_t tlocal, trecv; 351 352 if (sc->sc_read(sc, MBG_GET_TIME_HR, (char *)&tframe, sizeof(tframe), 353 &tstamp)) { 354 sc->sc_signal.status = SENSOR_S_CRIT; 355 return; 356 } 357 if (tframe.status & MBG_INVALID) { 358 sc->sc_signal.status = SENSOR_S_CRIT; 359 return; 360 } 361 362 tlocal = tstamp.tv_sec * NSECPERSEC + tstamp.tv_nsec; 363 trecv = letoh32(tframe.sec) * NSECPERSEC + 364 (letoh32(tframe.frac) * NSECPERSEC >> 32); 365 366 mbg_update_sensor(sc, &tstamp, tlocal - trecv, tframe.signal, 367 letoh16(tframe.status)); 368 } 369 370 /* update the sensor value, common to all cards */ 371 void 372 mbg_update_sensor(struct mbg_softc *sc, struct timespec *tstamp, 373 int64_t timedelta, u_int8_t rsignal, u_int16_t status) 374 { 375 int signal; 376 377 sc->sc_timedelta.value = timedelta; 378 sc->sc_timedelta.tv.tv_sec = tstamp->tv_sec; 379 sc->sc_timedelta.tv.tv_usec = tstamp->tv_nsec / 1000; 380 381 signal = rsignal - MBG_SIG_BIAS; 382 if (signal < 0) 383 signal = 0; 384 else if (signal > MBG_SIG_MAX) 385 signal = MBG_SIG_MAX; 386 387 sc->sc_signal.value = signal * 100000 / MBG_SIG_MAX; 388 sc->sc_signal.status = status & MBG_FREERUN ? 389 SENSOR_S_WARN : SENSOR_S_OK; 390 sc->sc_signal.tv.tv_sec = sc->sc_timedelta.tv.tv_sec; 391 sc->sc_signal.tv.tv_usec = sc->sc_timedelta.tv.tv_usec; 392 if (!(status & MBG_FREERUN)) { 393 sc->sc_timedelta.status = SENSOR_S_OK; 394 timeout_add_sec(&sc->sc_timeout, sc->sc_trust); 395 } 396 } 397 398 /* 399 * send a command and read back results to an AMCC S5920 based card 400 * (e.g. the PCI509 DCF77 radio clock) 401 */ 402 int 403 mbg_read_amcc_s5920(struct mbg_softc *sc, int cmd, char *buf, size_t len, 404 struct timespec *tstamp) 405 { 406 long timer, tmax; 407 size_t quot, rem; 408 u_int32_t ul; 409 int n; 410 u_int8_t status; 411 412 quot = len / 4; 413 rem = len % 4; 414 415 /* write the command, optionally taking a timestamp */ 416 if (tstamp) 417 nanotime(tstamp); 418 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_OMB, cmd); 419 420 /* wait for the BUSY flag to go low (approx 70 us on i386) */ 421 timer = 0; 422 tmax = cold ? 50 : 10; 423 do { 424 if (cold) 425 delay(20); 426 else 427 tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1)); 428 status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 429 AMCC_IMB4 + 3); 430 } while ((status & MBG_BUSY) && timer++ < tmax); 431 432 if (status & MBG_BUSY) 433 return -1; 434 435 /* read data from the device */ 436 if (len) { 437 for (n = 0; n < quot; n++) { 438 *(u_int32_t *)buf = bus_space_read_4(sc->sc_iot_s5920, 439 sc->sc_ioh_s5920, AMCC_DATA); 440 buf += sizeof(u_int32_t); 441 } 442 if (rem) { 443 ul = bus_space_read_4(sc->sc_iot_s5920, 444 sc->sc_ioh_s5920, AMCC_DATA); 445 for (n = 0; n < rem; n++) 446 *buf++ = *((char *)&ul + n); 447 } 448 } else 449 bus_space_read_4(sc->sc_iot_s5920, sc->sc_ioh_s5920, AMCC_DATA); 450 return 0; 451 } 452 453 /* 454 * send a command and read back results to an AMCC S5933 based card 455 * (e.g. the PCI32 DCF77 radio clock) 456 */ 457 int 458 mbg_read_amcc_s5933(struct mbg_softc *sc, int cmd, char *buf, size_t len, 459 struct timespec *tstamp) 460 { 461 long timer, tmax; 462 size_t n; 463 u_int8_t status; 464 465 /* reset inbound mailbox and clear FIFO status */ 466 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_MCSR + 3, 0x0c); 467 468 /* set FIFO */ 469 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_INTCSR + 3, 0x3c); 470 471 /* write the command, optionally taking a timestamp */ 472 if (tstamp) 473 nanotime(tstamp); 474 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_OMB1, cmd); 475 476 /* wait for the BUSY flag to go low (approx 70 us on i386) */ 477 timer = 0; 478 tmax = cold ? 50 : 10; 479 do { 480 if (cold) 481 delay(20); 482 else 483 tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1)); 484 status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 485 AMCC_IMB4 + 3); 486 } while ((status & MBG_BUSY) && timer++ < tmax); 487 488 if (status & MBG_BUSY) 489 return -1; 490 491 /* read data from the device FIFO */ 492 for (n = 0; n < len; n++) { 493 if (bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMCC_MCSR) 494 & 0x20) { 495 return -1; 496 } 497 buf[n] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 498 AMCC_FIFO + (n % 4)); 499 } 500 return 0; 501 } 502 503 /* 504 * send a command and read back results to an ASIC based card 505 * (e.g. the PCI511 DCF77 radio clock) 506 */ 507 int 508 mbg_read_asic(struct mbg_softc *sc, int cmd, char *buf, size_t len, 509 struct timespec *tstamp) 510 { 511 long timer, tmax; 512 size_t n; 513 u_int32_t data; 514 u_int16_t port; 515 char *p = buf; 516 u_int8_t status; 517 int s; 518 519 /* write the command, optionally taking a timestamp */ 520 if (tstamp) { 521 s = splhigh(); 522 nanotime(tstamp); 523 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd); 524 splx(s); 525 } else 526 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd); 527 528 /* wait for the BUSY flag to go low */ 529 timer = 0; 530 tmax = cold ? 50 : 10; 531 do { 532 if (cold) 533 delay(20); 534 else 535 tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1)); 536 status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASIC_STATUS); 537 } while ((status & MBG_BUSY) && timer++ < tmax); 538 539 if (status & MBG_BUSY) 540 return -1; 541 542 /* read data from the device FIFO */ 543 port = ASIC_ADDON; 544 for (n = 0; n < len / 4; n++) { 545 data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port); 546 *(u_int32_t *)p = data; 547 p += sizeof(data); 548 port += sizeof(data); 549 } 550 551 if (len % 4) { 552 data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port); 553 for (n = 0; n < len % 4; n++) { 554 *p++ = data & 0xff; 555 data >>= 8; 556 } 557 } 558 return 0; 559 } 560 561 /* 562 * degrade the sensor state if we are freerunning for more than 563 * sc->sc_trust seconds. 564 */ 565 void 566 mbg_timeout(void *xsc) 567 { 568 struct mbg_softc *sc = xsc; 569 570 if (sc->sc_timedelta.status == SENSOR_S_OK) { 571 sc->sc_timedelta.status = SENSOR_S_WARN; 572 /* 573 * further degrade in sc->sc_trust seconds if no new valid 574 * time data can be read from the device. 575 */ 576 timeout_add_sec(&sc->sc_timeout, sc->sc_trust); 577 } else 578 sc->sc_timedelta.status = SENSOR_S_CRIT; 579 } 580