1/*	$OpenBSD: mbg.c,v 1.37 2024/05/24 06:02:58 jsg Exp $ */
2
3/*
4 * Copyright (c) 2006, 2007 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/param.h>
20#include <sys/device.h>
21#include <sys/timeout.h>
22#include <sys/systm.h>
23#include <sys/sensors.h>
24#include <sys/time.h>
25
26#include <machine/bus.h>
27
28#include <dev/pci/pcivar.h>
29#include <dev/pci/pcireg.h>
30#include <dev/pci/pcidevs.h>
31
32struct mbg_softc {
33	struct device		sc_dev;
34	bus_space_tag_t		sc_iot;
35	bus_space_handle_t	sc_ioh;
36
37	/*
38	 * I/O region used by the AMCC S5920 found on the PCI509 card
39	 * used to access the data.
40	 */
41	bus_space_tag_t		sc_iot_s5920;
42	bus_space_handle_t	sc_ioh_s5920;
43
44	struct ksensor		sc_timedelta;
45	struct ksensor		sc_signal;
46	struct ksensordev	sc_sensordev;
47	struct timeout		sc_timeout;	/* invalidate sensor */
48	int			sc_trust;	/* trust time in seconds */
49
50	int			(*sc_read)(struct mbg_softc *, int cmd,
51				    char *buf, size_t len,
52				    struct timespec *tstamp);
53};
54
55struct mbg_time {
56	u_int8_t		hundreds;
57	u_int8_t		sec;
58	u_int8_t		min;
59	u_int8_t		hour;
60	u_int8_t		mday;
61	u_int8_t		wday;	/* 1 (monday) - 7 (sunday) */
62	u_int8_t		mon;
63	u_int8_t		year;	/* 0 - 99 */
64	u_int8_t		status;
65	u_int8_t		signal;
66	int8_t			utc_off;
67};
68
69struct mbg_time_hr {
70	u_int32_t		sec;		/* always UTC */
71	u_int32_t		frac;		/* fractions of second */
72	int32_t			utc_off;	/* informal only, in seconds */
73	u_int16_t		status;
74	u_int8_t		signal;
75};
76
77/* mbg_time.status bits */
78#define MBG_FREERUN		0x01	/* clock running on xtal */
79#define MBG_DST_ENA		0x02	/* DST enabled */
80#define MBG_SYNC		0x04	/* clock synced at least once */
81#define MBG_DST_CHG		0x08	/* DST change announcement */
82#define MBG_UTC			0x10	/* special UTC firmware is installed */
83#define MBG_LEAP		0x20	/* announcement of a leap second */
84#define MBG_IFTM		0x40	/* current time was set from host */
85#define MBG_INVALID		0x80	/* time invalid, batt. was disconn. */
86
87/* AMCC S5920 registers */
88#define AMCC_DATA		0x00	/* data register, on 2nd IO region */
89#define AMCC_OMB		0x0c	/* outgoing mailbox */
90#define AMCC_IMB		0x1c	/* incoming mailbox */
91
92/* AMCC S5933 registers */
93#define AMCC_OMB1		0x00	/* outgoing mailbox 1 */
94#define AMCC_IMB4		0x1c	/* incoming mailbox 4 */
95#define AMCC_FIFO		0x20	/* FIFO register */
96#define AMCC_INTCSR		0x38	/* interrupt control/status register */
97#define AMCC_MCSR		0x3c	/* master control/status register */
98
99/* ASIC registers */
100#define ASIC_CFG		0x00
101#define ASIC_FEATURES		0x08	/* r/o */
102#define ASIC_STATUS		0x10
103#define ASIC_CTLSTATUS		0x14
104#define ASIC_DATA		0x18
105#define ASIC_RES1		0x1c
106#define ASIC_ADDON		0x20
107
108/* commands */
109#define MBG_GET_TIME		0x00
110#define MBG_GET_SYNC_TIME	0x02
111#define MBG_GET_TIME_HR		0x03
112#define MBG_SET_TIME		0x10
113#define MBG_GET_TZCODE		0x32
114#define MBG_SET_TZCODE		0x33
115#define MBG_GET_FW_ID_1		0x40
116#define MBG_GET_FW_ID_2		0x41
117#define MBG_GET_SERNUM		0x42
118
119/* timezone codes (for MBG_{GET|SET}_TZCODE) */
120#define MBG_TZCODE_CET_CEST	0x00
121#define MBG_TZCODE_CET		0x01
122#define MBG_TZCODE_UTC		0x02
123#define MBG_TZCODE_EET_EEST	0x03
124
125/* misc. constants */
126#define MBG_FIFO_LEN		16
127#define MBG_ID_LEN		(2 * MBG_FIFO_LEN + 1)
128#define MBG_BUSY		0x01
129#define MBG_SIG_BIAS		55
130#define MBG_SIG_MAX		68
131#define NSECPERSEC		1000000000LL	/* nanoseconds per second */
132#define HRDIVISOR		0x100000000LL	/* for hi-res timestamp */
133
134int	mbg_probe(struct device *, void *, void *);
135void	mbg_attach(struct device *, struct device *, void *);
136void	mbg_task(void *);
137void	mbg_task_hr(void *);
138void	mbg_update_sensor(struct mbg_softc *sc, struct timespec *tstamp,
139	    int64_t timedelta, u_int8_t rsignal, u_int16_t status);
140int	mbg_read_amcc_s5920(struct mbg_softc *, int cmd, char *buf, size_t len,
141	    struct timespec *tstamp);
142int	mbg_read_amcc_s5933(struct mbg_softc *, int cmd, char *buf, size_t len,
143	    struct timespec *tstamp);
144int	mbg_read_asic(struct mbg_softc *, int cmd, char *buf, size_t len,
145	    struct timespec *tstamp);
146void	mbg_timeout(void *);
147
148const struct cfattach mbg_ca = {
149	sizeof(struct mbg_softc), mbg_probe, mbg_attach
150};
151
152struct cfdriver mbg_cd = {
153	NULL, "mbg", DV_DULL
154};
155
156const struct pci_matchid mbg_devices[] = {
157	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_GPS170PCI },
158	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI32 },
159	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI509 },
160	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI510 },
161	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI511 },
162	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PEX511 },
163	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PZF180PEX }
164};
165
166int
167mbg_probe(struct device *parent, void *match, void *aux)
168{
169	return pci_matchbyid((struct pci_attach_args *)aux, mbg_devices,
170	    nitems(mbg_devices));
171}
172
173void
174mbg_attach(struct device *parent, struct device *self, void *aux)
175{
176	struct mbg_softc *sc = (struct mbg_softc *)self;
177	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
178	struct mbg_time tframe;
179	pcireg_t memtype;
180	bus_size_t iosize, iosize2;
181	int bar = PCI_MAPREG_START, signal, t_trust;
182	const char *desc;
183#ifdef MBG_DEBUG
184	char fw_id[MBG_ID_LEN];
185#endif
186
187	timeout_set(&sc->sc_timeout, mbg_timeout, sc);
188
189	/* for the PEX511 use BAR2 instead of BAR0*/
190	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_MEINBERG_PEX511)
191		bar += 0x08;
192
193	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, bar);
194	if (pci_mapreg_map(pa, bar, memtype, 0, &sc->sc_iot,
195	    &sc->sc_ioh, NULL, &iosize, 0)) {
196		printf(": PCI %s region not found\n",
197		    memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory");
198		return;
199	}
200
201	if ((desc = pci_findproduct(pa->pa_id)) == NULL)
202		desc = "Radio clock";
203	strlcpy(sc->sc_timedelta.desc, desc, sizeof(sc->sc_timedelta.desc));
204
205	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
206	    sizeof(sc->sc_sensordev.xname));
207
208	sc->sc_timedelta.type = SENSOR_TIMEDELTA;
209	sc->sc_timedelta.status = SENSOR_S_UNKNOWN;
210	sensor_attach(&sc->sc_sensordev, &sc->sc_timedelta);
211
212	sc->sc_signal.type = SENSOR_PERCENT;
213	sc->sc_signal.status = SENSOR_S_UNKNOWN;
214	strlcpy(sc->sc_signal.desc, "Signal", sizeof(sc->sc_signal.desc));
215	sensor_attach(&sc->sc_sensordev, &sc->sc_signal);
216
217	t_trust = 12 * 60 * 60;		/* twelve hours */
218
219	switch (PCI_PRODUCT(pa->pa_id)) {
220	case PCI_PRODUCT_MEINBERG_PCI32:
221		sc->sc_read = mbg_read_amcc_s5933;
222		sensor_task_register(sc, mbg_task, 10);
223		break;
224	case PCI_PRODUCT_MEINBERG_PCI509:
225		/*
226		 * map the second I/O region needed in addition to the first
227		 * to get at the actual data.
228		 */
229		memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag,
230		    PCI_MAPREG_START + 0x04);
231		if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x04, memtype, 0,
232		    &sc->sc_iot_s5920, &sc->sc_ioh_s5920, NULL, &iosize2, 0)) {
233			printf(": PCI2 %s region not found\n",
234			    memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory");
235
236			/* unmap first mapped region as well if we fail */
237			bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
238			return;
239		}
240		sc->sc_read = mbg_read_amcc_s5920;
241		sensor_task_register(sc, mbg_task, 10);
242		break;
243	case PCI_PRODUCT_MEINBERG_PCI510:
244	case PCI_PRODUCT_MEINBERG_PCI511:
245	case PCI_PRODUCT_MEINBERG_PEX511:
246		sc->sc_read = mbg_read_asic;
247		sensor_task_register(sc, mbg_task, 10);
248		break;
249	case PCI_PRODUCT_MEINBERG_GPS170PCI:
250	case PCI_PRODUCT_MEINBERG_PZF180PEX:
251		t_trust = 4 * 24 * 60 * 60;	/* four days */
252		sc->sc_read = mbg_read_asic;
253		sensor_task_register(sc, mbg_task_hr, 1);
254		break;
255	default:
256		/* this can not normally happen, but then there is murphy */
257		panic(": unsupported product 0x%04x", PCI_PRODUCT(pa->pa_id));
258		break;
259	}
260
261	sc->sc_trust = t_trust;
262
263	if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe,
264	    sizeof(struct mbg_time), NULL)) {
265		printf(": unknown status");
266		sc->sc_signal.status = SENSOR_S_CRIT;
267	} else {
268		sc->sc_signal.status = SENSOR_S_OK;
269		signal = tframe.signal - MBG_SIG_BIAS;
270		if (signal < 0)
271			signal = 0;
272		else if (signal > MBG_SIG_MAX)
273			signal = MBG_SIG_MAX;
274		sc->sc_signal.value = signal;
275
276		if (tframe.status & MBG_SYNC)
277			printf(": synchronized");
278		else
279			printf(": not synchronized");
280		if (tframe.status & MBG_FREERUN) {
281			sc->sc_signal.status = SENSOR_S_WARN;
282			printf(", free running");
283		}
284		if (tframe.status & MBG_IFTM)
285			printf(", time set from host");
286	}
287#ifdef MBG_DEBUG
288	if (sc->sc_read(sc, MBG_GET_FW_ID_1, fw_id, MBG_FIFO_LEN, NULL) ||
289	    sc->sc_read(sc, MBG_GET_FW_ID_2, &fw_id[MBG_FIFO_LEN], MBG_FIFO_LEN,
290	    NULL))
291		printf(", firmware unknown");
292	else {
293		fw_id[MBG_ID_LEN - 1] = '\0';
294		printf(", firmware %s", fw_id);
295	}
296#endif
297	printf("\n");
298	sensordev_install(&sc->sc_sensordev);
299	timeout_add_sec(&sc->sc_timeout, sc->sc_trust);
300}
301
302/*
303 * mbg_task() reads a timestamp from cards that to not provide a high
304 * resolution timestamp.  The precision is limited to 1/100 sec.
305 */
306void
307mbg_task(void *arg)
308{
309	struct mbg_softc *sc = (struct mbg_softc *)arg;
310	struct mbg_time tframe;
311	struct clock_ymdhms ymdhms;
312	struct timespec tstamp;
313	int64_t timedelta;
314	time_t trecv;
315
316	if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe, sizeof(tframe),
317	    &tstamp)) {
318		sc->sc_signal.status = SENSOR_S_CRIT;
319		return;
320	}
321	if (tframe.status & MBG_INVALID) {
322		sc->sc_signal.status = SENSOR_S_CRIT;
323		return;
324	}
325	ymdhms.dt_year = tframe.year + 2000;
326	ymdhms.dt_mon = tframe.mon;
327	ymdhms.dt_day = tframe.mday;
328	ymdhms.dt_hour = tframe.hour;
329	ymdhms.dt_min = tframe.min;
330	ymdhms.dt_sec = tframe.sec;
331	trecv = clock_ymdhms_to_secs(&ymdhms) - tframe.utc_off * 3600;
332
333	timedelta = (int64_t)((tstamp.tv_sec - trecv) * 100
334	    - tframe.hundreds) * 10000000LL + tstamp.tv_nsec;
335
336	mbg_update_sensor(sc, &tstamp, timedelta, tframe.signal,
337	    (u_int16_t)tframe.status);
338}
339
340/*
341 * mbg_task_hr() reads a timestamp from cards that do provide a high
342 * resolution timestamp.
343 */
344void
345mbg_task_hr(void *arg)
346{
347	struct mbg_softc *sc = (struct mbg_softc *)arg;
348	struct mbg_time_hr tframe;
349	struct timespec tstamp;
350	int64_t tlocal, trecv;
351
352	if (sc->sc_read(sc, MBG_GET_TIME_HR, (char *)&tframe, sizeof(tframe),
353	    &tstamp)) {
354		sc->sc_signal.status = SENSOR_S_CRIT;
355		return;
356	}
357	if (tframe.status & MBG_INVALID) {
358		sc->sc_signal.status = SENSOR_S_CRIT;
359		return;
360	}
361
362	tlocal = tstamp.tv_sec * NSECPERSEC + tstamp.tv_nsec;
363	trecv = letoh32(tframe.sec) * NSECPERSEC +
364	    (letoh32(tframe.frac) * NSECPERSEC >> 32);
365
366	mbg_update_sensor(sc, &tstamp, tlocal - trecv, tframe.signal,
367	    letoh16(tframe.status));
368}
369
370/* update the sensor value, common to all cards */
371void
372mbg_update_sensor(struct mbg_softc *sc, struct timespec *tstamp,
373    int64_t timedelta, u_int8_t rsignal, u_int16_t status)
374{
375	int signal;
376
377	sc->sc_timedelta.value = timedelta;
378	sc->sc_timedelta.tv.tv_sec = tstamp->tv_sec;
379	sc->sc_timedelta.tv.tv_usec = tstamp->tv_nsec / 1000;
380
381	signal = rsignal - MBG_SIG_BIAS;
382	if (signal < 0)
383		signal = 0;
384	else if (signal > MBG_SIG_MAX)
385		signal = MBG_SIG_MAX;
386
387	sc->sc_signal.value = signal * 100000 / MBG_SIG_MAX;
388	sc->sc_signal.status = status & MBG_FREERUN ?
389	    SENSOR_S_WARN : SENSOR_S_OK;
390	sc->sc_signal.tv.tv_sec = sc->sc_timedelta.tv.tv_sec;
391	sc->sc_signal.tv.tv_usec = sc->sc_timedelta.tv.tv_usec;
392	if (!(status & MBG_FREERUN)) {
393		sc->sc_timedelta.status = SENSOR_S_OK;
394		timeout_add_sec(&sc->sc_timeout, sc->sc_trust);
395	}
396}
397
398/*
399 * send a command and read back results to an AMCC S5920 based card
400 * (e.g. the PCI509 DCF77 radio clock)
401 */
402int
403mbg_read_amcc_s5920(struct mbg_softc *sc, int cmd, char *buf, size_t len,
404    struct timespec *tstamp)
405{
406	long timer, tmax;
407	size_t quot, rem;
408	u_int32_t ul;
409	int n;
410	u_int8_t status;
411
412	quot = len / 4;
413	rem = len % 4;
414
415	/* write the command, optionally taking a timestamp */
416	if (tstamp)
417		nanotime(tstamp);
418	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_OMB, cmd);
419
420	/* wait for the BUSY flag to go low (approx 70 us on i386) */
421	timer = 0;
422	tmax = cold ? 50 : 10;
423	do {
424		if (cold)
425			delay(20);
426		else
427			tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1));
428		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
429		    AMCC_IMB4 + 3);
430	} while ((status & MBG_BUSY) && timer++ < tmax);
431
432	if (status & MBG_BUSY)
433		return -1;
434
435	/* read data from the device */
436	if (len) {
437		for (n = 0; n < quot; n++) {
438			*(u_int32_t *)buf = bus_space_read_4(sc->sc_iot_s5920,
439			    sc->sc_ioh_s5920, AMCC_DATA);
440			buf += sizeof(u_int32_t);
441		}
442		if (rem) {
443			ul =  bus_space_read_4(sc->sc_iot_s5920,
444			    sc->sc_ioh_s5920, AMCC_DATA);
445			for (n = 0; n < rem; n++)
446				*buf++ = *((char *)&ul + n);
447		}
448	} else
449		bus_space_read_4(sc->sc_iot_s5920, sc->sc_ioh_s5920, AMCC_DATA);
450	return 0;
451}
452
453/*
454 * send a command and read back results to an AMCC S5933 based card
455 * (e.g. the PCI32 DCF77 radio clock)
456 */
457int
458mbg_read_amcc_s5933(struct mbg_softc *sc, int cmd, char *buf, size_t len,
459    struct timespec *tstamp)
460{
461	long timer, tmax;
462	size_t n;
463	u_int8_t status;
464
465	/* reset inbound mailbox and clear FIFO status */
466	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_MCSR + 3, 0x0c);
467
468	/* set FIFO */
469	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_INTCSR + 3, 0x3c);
470
471	/* write the command, optionally taking a timestamp */
472	if (tstamp)
473		nanotime(tstamp);
474	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_OMB1, cmd);
475
476	/* wait for the BUSY flag to go low (approx 70 us on i386) */
477	timer = 0;
478	tmax = cold ? 50 : 10;
479	do {
480		if (cold)
481			delay(20);
482		else
483			tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1));
484		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
485		    AMCC_IMB4 + 3);
486	} while ((status & MBG_BUSY) && timer++ < tmax);
487
488	if (status & MBG_BUSY)
489		return -1;
490
491	/* read data from the device FIFO */
492	for (n = 0; n < len; n++) {
493		if (bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMCC_MCSR)
494		    & 0x20) {
495			return -1;
496		}
497		buf[n] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
498		    AMCC_FIFO + (n % 4));
499	}
500	return 0;
501}
502
503/*
504 * send a command and read back results to an ASIC based card
505 * (e.g. the PCI511 DCF77 radio clock)
506 */
507int
508mbg_read_asic(struct mbg_softc *sc, int cmd, char *buf, size_t len,
509    struct timespec *tstamp)
510{
511	long timer, tmax;
512	size_t n;
513	u_int32_t data;
514	u_int16_t port;
515	char *p = buf;
516	u_int8_t status;
517	int s;
518
519	/* write the command, optionally taking a timestamp */
520	if (tstamp) {
521		s = splhigh();
522		nanotime(tstamp);
523		bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd);
524		splx(s);
525	} else
526		bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd);
527
528	/* wait for the BUSY flag to go low */
529	timer = 0;
530	tmax = cold ? 50 : 10;
531	do {
532		if (cold)
533			delay(20);
534		else
535			tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1));
536		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASIC_STATUS);
537	} while ((status & MBG_BUSY) && timer++ < tmax);
538
539	if (status & MBG_BUSY)
540		return -1;
541
542	/* read data from the device FIFO */
543	port = ASIC_ADDON;
544	for (n = 0; n < len / 4; n++) {
545		data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port);
546		*(u_int32_t *)p = data;
547		p += sizeof(data);
548		port += sizeof(data);
549	}
550
551	if (len % 4) {
552		data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port);
553		for (n = 0; n < len % 4; n++) {
554			*p++ = data & 0xff;
555			data >>= 8;
556		}
557	}
558	return 0;
559}
560
561/*
562 * degrade the sensor state if we are freerunning for more than
563 * sc->sc_trust seconds.
564 */
565void
566mbg_timeout(void *xsc)
567{
568	struct mbg_softc *sc = xsc;
569
570	if (sc->sc_timedelta.status == SENSOR_S_OK) {
571		sc->sc_timedelta.status = SENSOR_S_WARN;
572		/*
573		 * further degrade in sc->sc_trust seconds if no new valid
574		 * time data can be read from the device.
575		 */
576		timeout_add_sec(&sc->sc_timeout, sc->sc_trust);
577	} else
578		sc->sc_timedelta.status = SENSOR_S_CRIT;
579}
580