root/dev/pci/mbg.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mbg_probe
  2. mbg_attach
  3. mbg_task
  4. mbg_read_amcc_s5933
  5. mbg_read_asic

    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 }

/* [<][>][^][v][top][bottom][index][help] */