Annotation of sys/dev/pci/mbg.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: mbg.c,v 1.13 2007/03/22 16:55:31 deraadt Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2006 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/types.h>
! 20: #include <sys/param.h>
! 21: #include <sys/device.h>
! 22: #include <sys/kernel.h>
! 23: #include <sys/proc.h>
! 24: #include <sys/systm.h>
! 25: #include <sys/sensors.h>
! 26: #include <sys/syslog.h>
! 27: #include <sys/time.h>
! 28:
! 29: #include <machine/bus.h>
! 30:
! 31: #include <dev/pci/pcivar.h>
! 32: #include <dev/pci/pcireg.h>
! 33: #include <dev/pci/pcidevs.h>
! 34:
! 35: struct mbg_softc {
! 36: struct device sc_dev;
! 37: bus_space_tag_t sc_iot;
! 38: bus_space_handle_t sc_ioh;
! 39:
! 40: struct ksensor sc_timedelta;
! 41: struct ksensor sc_signal;
! 42: struct ksensordev sc_sensordev;
! 43: u_int8_t sc_status;
! 44:
! 45: int (*sc_read)(struct mbg_softc *, int cmd,
! 46: char *buf, size_t len,
! 47: struct timespec *tstamp);
! 48: };
! 49:
! 50: struct mbg_time {
! 51: u_int8_t hundreds;
! 52: u_int8_t sec;
! 53: u_int8_t min;
! 54: u_int8_t hour;
! 55: u_int8_t mday;
! 56: u_int8_t wday;
! 57: u_int8_t mon;
! 58: u_int8_t year;
! 59: u_int8_t status;
! 60: u_int8_t signal;
! 61: int8_t utc_off;
! 62: };
! 63:
! 64: /* mbg_time.status bits */
! 65: #define MBG_FREERUN 0x01 /* clock running on xtal */
! 66: #define MBG_DST_ENA 0x02 /* DST enabled */
! 67: #define MBG_SYNC 0x04 /* clock synced at least once */
! 68: #define MBG_DST_CHG 0x08 /* DST change announcement */
! 69: #define MBG_UTC 0x10 /* special UTC firmware is installed */
! 70: #define MBG_LEAP 0x20 /* announcement of a leap second */
! 71: #define MBG_IFTM 0x40 /* current time was set from host */
! 72: #define MBG_INVALID 0x80 /* time is invalid */
! 73:
! 74: /* AMCC S5933 registers */
! 75: #define AMCC_OMB1 0x00 /* outgoing mailbox 1 */
! 76: #define AMCC_IMB4 0x1c /* incoming mailbox 4 */
! 77: #define AMCC_FIFO 0x20 /* FIFO register */
! 78: #define AMCC_INTCSR 0x38 /* interrupt control/status register */
! 79: #define AMCC_MCSR 0x3c /* master control/status register */
! 80:
! 81: /* ASIC registers */
! 82: #define ASIC_CFG 0x00
! 83: #define ASIC_FEATURES 0x08 /* r/o */
! 84: #define ASIC_STATUS 0x10
! 85: #define ASIC_CTLSTATUS 0x14
! 86: #define ASIC_DATA 0x18
! 87: #define ASIC_RES1 0x1c
! 88: #define ASIC_ADDON 0x20
! 89:
! 90: /* commands */
! 91: #define MBG_GET_TIME 0x00
! 92: #define MBG_GET_SYNC_TIME 0x02
! 93: #define MBG_GET_HR_TIME 0x03
! 94: #define MBG_GET_FW_ID_1 0x40
! 95: #define MBG_GET_FW_ID_2 0x41
! 96: #define MBG_GET_SERNUM 0x42
! 97:
! 98: /* misc. constants */
! 99: #define MBG_FIFO_LEN 16
! 100: #define MBG_ID_LEN (2 * MBG_FIFO_LEN + 1)
! 101: #define MBG_BUSY 0x01
! 102: #define MBG_SIG_BIAS 55
! 103: #define MBG_SIG_MAX 68
! 104:
! 105: int mbg_probe(struct device *, void *, void *);
! 106: void mbg_attach(struct device *, struct device *, void *);
! 107: void mbg_task(void *);
! 108: int mbg_read_amcc_s5933(struct mbg_softc *, int cmd, char *buf, size_t len,
! 109: struct timespec *tstamp);
! 110: int mbg_read_asic(struct mbg_softc *, int cmd, char *buf, size_t len,
! 111: struct timespec *tstamp);
! 112:
! 113: struct cfattach mbg_ca = {
! 114: sizeof(struct mbg_softc), mbg_probe, mbg_attach
! 115: };
! 116:
! 117: struct cfdriver mbg_cd = {
! 118: NULL, "mbg", DV_DULL
! 119: };
! 120:
! 121: const struct pci_matchid mbg_devices[] = {
! 122: { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_GPS170 },
! 123: { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI32 },
! 124: { PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI511 }
! 125: };
! 126:
! 127: int
! 128: mbg_probe(struct device *parent, void *match, void *aux)
! 129: {
! 130: return pci_matchbyid((struct pci_attach_args *)aux, mbg_devices,
! 131: sizeof(mbg_devices) / sizeof(mbg_devices[0]));
! 132: }
! 133:
! 134: void
! 135: mbg_attach(struct device *parent, struct device *self, void *aux)
! 136: {
! 137: struct mbg_softc *sc = (struct mbg_softc *)self;
! 138: struct pci_attach_args *const pa = (struct pci_attach_args *)aux;
! 139: struct mbg_time tframe;
! 140: pcireg_t memtype;
! 141: bus_size_t iosize;
! 142: char fw_id[MBG_ID_LEN];
! 143: const char *desc;
! 144:
! 145: memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START);
! 146: if (pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, &sc->sc_iot,
! 147: &sc->sc_ioh, NULL, &iosize, 0)) {
! 148: printf(": PCI %s region not found\n",
! 149: memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory");
! 150: return;
! 151: }
! 152:
! 153: if ((desc = pci_findproduct(pa->pa_id)) == NULL)
! 154: desc = "Radio clock";
! 155: strlcpy(sc->sc_timedelta.desc, desc, sizeof(sc->sc_timedelta.desc));
! 156:
! 157: switch (PCI_PRODUCT(pa->pa_id)) {
! 158: case PCI_PRODUCT_MEINBERG_PCI32:
! 159: sc->sc_read = mbg_read_amcc_s5933;
! 160: break;
! 161: case PCI_PRODUCT_MEINBERG_PCI511:
! 162: /* FALLTHROUGH */
! 163: case PCI_PRODUCT_MEINBERG_GPS170:
! 164: sc->sc_read = mbg_read_asic;
! 165: break;
! 166: default:
! 167: /* this can not normally happen, but then there is murphy */
! 168: panic(": unsupported product 0x%04x", PCI_PRODUCT(pa->pa_id));
! 169: break;
! 170: }
! 171: if (sc->sc_read(sc, MBG_GET_FW_ID_1, fw_id, MBG_FIFO_LEN, NULL) ||
! 172: sc->sc_read(sc, MBG_GET_FW_ID_2, &fw_id[MBG_FIFO_LEN], MBG_FIFO_LEN,
! 173: NULL))
! 174: printf(": firmware unknown, ");
! 175: else {
! 176: fw_id[MBG_ID_LEN - 1] = '\0';
! 177: printf(": firmware %s, ", fw_id);
! 178: }
! 179:
! 180: if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe,
! 181: sizeof(struct mbg_time), NULL)) {
! 182: printf("unknown status\n");
! 183: sc->sc_status = 0;
! 184: } else {
! 185: if (tframe.status & MBG_FREERUN)
! 186: printf("free running on xtal\n");
! 187: else if (tframe.status & MBG_SYNC)
! 188: printf("synchronised\n");
! 189: else if (tframe.status & MBG_INVALID)
! 190: printf("invalid\n");
! 191: sc->sc_status = tframe.status;
! 192: }
! 193:
! 194: strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
! 195: sizeof(sc->sc_sensordev.xname));
! 196:
! 197: sc->sc_timedelta.type = SENSOR_TIMEDELTA;
! 198: sc->sc_timedelta.status = SENSOR_S_UNKNOWN;
! 199: sc->sc_timedelta.value = 0LL;
! 200: sc->sc_timedelta.flags = 0;
! 201: sensor_attach(&sc->sc_sensordev, &sc->sc_timedelta);
! 202:
! 203: sc->sc_signal.type = SENSOR_PERCENT;
! 204: sc->sc_signal.status = SENSOR_S_UNKNOWN;
! 205: sc->sc_signal.value = 0LL;
! 206: sc->sc_signal.flags = 0;
! 207: strlcpy(sc->sc_signal.desc, "Signal strength",
! 208: sizeof(sc->sc_signal.desc));
! 209: sensor_attach(&sc->sc_sensordev, &sc->sc_signal);
! 210:
! 211: sensor_task_register(sc, mbg_task, 10);
! 212: sensordev_install(&sc->sc_sensordev);
! 213: }
! 214:
! 215: void
! 216: mbg_task(void *arg)
! 217: {
! 218: struct mbg_softc *sc = (struct mbg_softc *)arg;
! 219: struct mbg_time tframe;
! 220: struct clock_ymdhms ymdhms;
! 221: struct timespec tstamp;
! 222: time_t trecv;
! 223: int signal;
! 224:
! 225: if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe, sizeof(tframe),
! 226: &tstamp)) {
! 227: log(LOG_ERR, "%s: error reading time\n", sc->sc_dev.dv_xname);
! 228: return;
! 229: }
! 230: if (tframe.status & MBG_INVALID) {
! 231: log(LOG_INFO, "%s: invalid time, battery was disconnected\n",
! 232: sc->sc_dev.dv_xname);
! 233: return;
! 234: }
! 235: ymdhms.dt_year = tframe.year + 2000;
! 236: ymdhms.dt_mon = tframe.mon;
! 237: ymdhms.dt_day = tframe.mday;
! 238: ymdhms.dt_hour = tframe.hour;
! 239: ymdhms.dt_min = tframe.min;
! 240: ymdhms.dt_sec = tframe.sec;
! 241: trecv = clock_ymdhms_to_secs(&ymdhms) - tframe.utc_off * 3600;
! 242:
! 243: sc->sc_timedelta.value = (int64_t)((tstamp.tv_sec - trecv) * 100
! 244: - tframe.hundreds) * 10000000LL + tstamp.tv_nsec;
! 245: sc->sc_timedelta.status = SENSOR_S_OK;
! 246: sc->sc_timedelta.tv.tv_sec = tstamp.tv_sec;
! 247: sc->sc_timedelta.tv.tv_usec = tstamp.tv_nsec / 1000;
! 248:
! 249: signal = tframe.signal - MBG_SIG_BIAS;
! 250: if (signal < 0)
! 251: signal = 0;
! 252: else if (signal > MBG_SIG_MAX)
! 253: signal = MBG_SIG_MAX;
! 254:
! 255: sc->sc_signal.value = signal * 100000 / MBG_SIG_MAX;
! 256: sc->sc_signal.status = SENSOR_S_OK;
! 257: sc->sc_signal.tv.tv_sec = sc->sc_timedelta.tv.tv_sec;
! 258: sc->sc_signal.tv.tv_usec = sc->sc_timedelta.tv.tv_usec;
! 259:
! 260: if (tframe.status != sc->sc_status) {
! 261: if (tframe.status & MBG_SYNC)
! 262: log(LOG_INFO, "%s: clock is synchronized",
! 263: sc->sc_dev.dv_xname);
! 264: else if (tframe.status & MBG_FREERUN)
! 265: log(LOG_INFO, "%s: clock is free running on xtal",
! 266: sc->sc_dev.dv_xname);
! 267: sc->sc_status = tframe.status;
! 268: }
! 269: }
! 270:
! 271: /*
! 272: * send a command and read back results to an AMCC S5933 based card
! 273: * (i.e. the PCI32 DCF77 radio clock)
! 274: */
! 275: int
! 276: mbg_read_amcc_s5933(struct mbg_softc *sc, int cmd, char *buf, size_t len,
! 277: struct timespec *tstamp)
! 278: {
! 279: long timer, tmax;
! 280: size_t n;
! 281: u_int8_t status;
! 282:
! 283: /* reset inbound mailbox and clear FIFO status */
! 284: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_MCSR + 3, 0x0c);
! 285:
! 286: /* set FIFO */
! 287: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_INTCSR + 3, 0x3c);
! 288:
! 289: /* write the command, optionally taking a timestamp */
! 290: if (tstamp)
! 291: nanotime(tstamp);
! 292: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_OMB1, cmd);
! 293:
! 294: /* wait for the BUSY flag to go low (approx 70 us on i386) */
! 295: timer = 0;
! 296: tmax = cold ? 50 : hz / 10;
! 297: do {
! 298: if (cold)
! 299: delay(20);
! 300: else
! 301: tsleep(tstamp, 0, "mbg", 1);
! 302: status = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 303: AMCC_IMB4 + 3);
! 304: } while ((status & MBG_BUSY) && timer++ < tmax);
! 305:
! 306: if (status & MBG_BUSY)
! 307: return -1;
! 308:
! 309: /* read data from the device FIFO */
! 310: for (n = 0; n < len; n++) {
! 311: if (bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMCC_MCSR)
! 312: & 0x20) {
! 313: printf("%s: FIFO error\n", sc->sc_dev.dv_xname);
! 314: return -1;
! 315: }
! 316: buf[n] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 317: AMCC_FIFO + (n % 4));
! 318: }
! 319: return 0;
! 320: }
! 321:
! 322: /*
! 323: * send a command and read back results to an ASIC based card
! 324: * (i.e. the PCI511 DCF77 radio clock)
! 325: */
! 326: int
! 327: mbg_read_asic(struct mbg_softc *sc, int cmd, char *buf, size_t len,
! 328: struct timespec *tstamp)
! 329: {
! 330: long timer, tmax;
! 331: size_t n;
! 332: u_int32_t data;
! 333: char *p = buf;
! 334: u_int16_t port;
! 335: u_int8_t status;
! 336: int s;
! 337:
! 338: /* write the command, optionally taking a timestamp */
! 339: if (tstamp) {
! 340: s = splhigh();
! 341: nanotime(tstamp);
! 342: bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd);
! 343: splx(s);
! 344: } else
! 345: bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd);
! 346:
! 347: /* wait for the BUSY flag to go low */
! 348: timer = 0;
! 349: tmax = cold ? 50 : hz / 10;
! 350: do {
! 351: if (cold)
! 352: delay(20);
! 353: else
! 354: tsleep(tstamp, 0, "mbg", 1);
! 355: status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASIC_STATUS);
! 356: } while ((status & MBG_BUSY) && timer++ < tmax);
! 357:
! 358: if (status & MBG_BUSY)
! 359: return -1;
! 360:
! 361: /* read data from the device FIFO */
! 362: port = ASIC_ADDON;
! 363: for (n = 0; n < len / 4; n++) {
! 364: data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port);
! 365: *(u_int32_t *)p = data;
! 366: p += sizeof(data);
! 367: port += sizeof(data);
! 368: }
! 369:
! 370: if (len % 4) {
! 371: data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port);
! 372: for (n = 0; n < len % 4; n++) {
! 373: *p++ = data & 0xff;
! 374: data >>= 8;
! 375: }
! 376: }
! 377: return 0;
! 378: }
CVSweb