[BACK]Return to mbg.c CVS log [TXT][DIR] Up to [local] / sys / dev / pci

Annotation of sys/dev/pci/mbg.c, Revision 1.1.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