[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     ! 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