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