1/*-
2 * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org>
3 * Copyright (c) 2010 Joerg Wunsch <joerg@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * High-level driver for µPD7210 based GPIB cards.
28 *
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#  define	GPIB_DEBUG
35#  undef	GPIB_DEBUG
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/conf.h>
40#include <sys/malloc.h>
41#include <sys/kernel.h>
42#include <sys/limits.h>
43#include <sys/module.h>
44#include <sys/rman.h>
45#include <sys/bus.h>
46#include <sys/lock.h>
47#include <sys/mutex.h>
48#include <sys/uio.h>
49#include <sys/time.h>
50#include <machine/bus.h>
51#include <machine/resource.h>
52#include <isa/isavar.h>
53
54#define UPD7210_HW_DRIVER
55#define UPD7210_SW_DRIVER
56#include <dev/ieee488/upd7210.h>
57#include <dev/ieee488/tnt4882.h>
58
59static MALLOC_DEFINE(M_GPIB, "GPIB", "GPIB");
60
61/* upd7210 generic stuff */
62
63void
64upd7210_print_isr(u_int isr1, u_int isr2)
65{
66	printf("isr1=0x%b isr2=0x%b",
67	    isr1, "\20\10CPT\7APT\6DET\5ENDRX\4DEC\3ERR\2DO\1DI",
68	    isr2, "\20\10INT\7SRQI\6LOK\5REM\4CO\3LOKC\2REMC\1ADSC");
69}
70
71u_int
72upd7210_rd(struct upd7210 *u, enum upd7210_rreg reg)
73{
74	u_int r;
75
76	r = bus_read_1(u->reg_res[reg], u->reg_offset[reg]);
77	u->rreg[reg] = r;
78	return (r);
79}
80
81void
82upd7210_wr(struct upd7210 *u, enum upd7210_wreg reg, u_int val)
83{
84
85	bus_write_1(u->reg_res[reg], u->reg_offset[reg], val);
86	u->wreg[reg] = val;
87	if (reg == AUXMR)
88		u->wreg[8 + (val >> 5)] = val & 0x1f;
89}
90
91void
92upd7210intr(void *arg)
93{
94	u_int isr_1, isr_2, isr_3;
95	struct upd7210 *u;
96
97	u = arg;
98	mtx_lock(&u->mutex);
99	isr_1 = upd7210_rd(u, ISR1);
100	isr_2 = upd7210_rd(u, ISR2);
101	if (u->use_fifo) {
102		isr_3 = bus_read_1(u->reg_res[0], isr3);
103	} else {
104		isr_3 = 0;
105	}
106	if (isr_1 != 0 || isr_2 != 0 || isr_3 != 0) {
107		if (u->busy == 0 || u->irq == NULL || !u->irq(u, isr_3)) {
108#if 0
109			printf("upd7210intr [%02x %02x %02x",
110			       upd7210_rd(u, DIR), isr1, isr2);
111			printf(" %02x %02x %02x %02x %02x] ",
112			       upd7210_rd(u, SPSR),
113			       upd7210_rd(u, ADSR),
114			       upd7210_rd(u, CPTR),
115			       upd7210_rd(u, ADR0),
116		    upd7210_rd(u, ADR1));
117			upd7210_print_isr(isr1, isr2);
118			printf("\n");
119#endif
120		}
121		/*
122		 * "special interrupt handling"
123		 *
124		 * In order to implement shared IRQs, the original
125		 * PCIIa uses IO locations 0x2f0 + (IRQ#) as an output
126		 * location.  If an ISR for a particular card has
127		 * detected this card triggered the IRQ, it must reset
128		 * the card's IRQ by writing (anything) to that IO
129		 * location.
130		 *
131		 * Some clones apparently don't implement this
132		 * feature, but National Instrument cards do.
133		 */
134		if (u->irq_clear_res != NULL)
135			bus_write_1(u->irq_clear_res, 0, 42);
136	}
137	mtx_unlock(&u->mutex);
138}
139
140int
141upd7210_take_ctrl_async(struct upd7210 *u)
142{
143	int i;
144
145	upd7210_wr(u, AUXMR, AUXMR_TCA);
146
147	if (!(upd7210_rd(u, ADSR) & ADSR_ATN))
148		return (0);
149	for (i = 0; i < 20; i++) {
150		DELAY(1);
151		if (!(upd7210_rd(u, ADSR) & ADSR_ATN))
152			return (0);
153	}
154	return (1);
155}
156
157int
158upd7210_goto_standby(struct upd7210 *u)
159{
160	int i;
161
162	upd7210_wr(u, AUXMR, AUXMR_GTS);
163
164	if (upd7210_rd(u, ADSR) & ADSR_ATN)
165		return (0);
166	for (i = 0; i < 20; i++) {
167		DELAY(1);
168		if (upd7210_rd(u, ADSR) & ADSR_ATN)
169			return (0);
170	}
171	return (1);
172}
173
174/* Unaddressed Listen Only mode */
175
176static int
177gpib_l_irq(struct upd7210 *u, int isr_3)
178{
179	int i;
180	int have_data = 0;
181
182	if (u->use_fifo) {
183		/* TNT5004 or TNT4882 in FIFO mode */
184		if (isr_3 & 0x04) {
185			/* FIFO not empty */
186			i = bus_read_1(u->reg_res[0], fifob);
187			have_data = 1;
188			bus_write_1(u->reg_res[0], cnt0, -1);
189			bus_write_1(u->reg_res[0], cnt1, (-1) >> 8);
190			bus_write_1(u->reg_res[0], cnt2, (-1) >> 16);
191			bus_write_1(u->reg_res[0], cnt3, (-1) >> 24);
192			bus_write_1(u->reg_res[0], cmdr, 0x04); /* GO */
193		}
194	} else if (u->rreg[ISR1] & 1) {
195		i = upd7210_rd(u, DIR);
196		have_data = 1;
197	}
198
199	if (have_data) {
200		u->buf[u->buf_wp++] = i;
201		u->buf_wp &= (u->bufsize - 1);
202		i = (u->buf_rp + u->bufsize - u->buf_wp) & (u->bufsize - 1);
203		if (i < 8) {
204			if (u->use_fifo)
205				bus_write_1(u->reg_res[0], imr3, 0x00);
206			else
207				upd7210_wr(u, IMR1, 0);
208		}
209		wakeup(u->buf);
210		return (1);
211	}
212	return (0);
213}
214
215static int
216gpib_l_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
217{
218	struct upd7210 *u;
219
220	u = dev->si_drv1;
221
222	mtx_lock(&u->mutex);
223	if (u->busy) {
224		mtx_unlock(&u->mutex);
225		return (EBUSY);
226	}
227	u->busy = 1;
228	u->irq = gpib_l_irq;
229	mtx_unlock(&u->mutex);
230
231	u->buf = malloc(PAGE_SIZE, M_GPIB, M_WAITOK);
232	u->bufsize = PAGE_SIZE;
233	u->buf_wp = 0;
234	u->buf_rp = 0;
235
236	upd7210_wr(u, AUXMR, AUXMR_CRST); /* chip reset */
237	DELAY(10000);
238	upd7210_wr(u, AUXMR, C_ICR | 8); /* 8 MHz clock */
239	DELAY(1000);
240	upd7210_wr(u, ADR, 0x60); /* ADR0: disable listener and talker 0 */
241	upd7210_wr(u, ADR, 0xe0); /* ADR1: disable listener and talker 1 */
242	upd7210_wr(u, ADMR, 0x70); /* listen-only (lon) */
243	upd7210_wr(u, AUXMR, AUXMR_PON); /* immediate execute power-on (pon) */
244	if (u->use_fifo) {
245		/* TNT5004 or TNT4882 in FIFO mode */
246		bus_write_1(u->reg_res[0], cmdr, 0x10); /* reset FIFO */
247		bus_write_1(u->reg_res[0], cfg, 0x20); /* xfer IN, 8-bit FIFO */
248		bus_write_1(u->reg_res[0], cnt0, -1);
249		bus_write_1(u->reg_res[0], cnt1, (-1) >> 8);
250		bus_write_1(u->reg_res[0], cnt2, (-1) >> 16);
251		bus_write_1(u->reg_res[0], cnt3, (-1) >> 24);
252		bus_write_1(u->reg_res[0], cmdr, 0x04); /* GO */
253		bus_write_1(u->reg_res[0], imr3, 0x04); /* NEF IE */
254	} else {
255		/* µPD7210/NAT7210, or TNT4882 in non-FIFO mode */
256		upd7210_wr(u, IMR1, 0x01); /* data in interrupt enable */
257	}
258	return (0);
259}
260
261static int
262gpib_l_close(struct cdev *dev, int oflags, int devtype, struct thread *td)
263{
264	struct upd7210 *u;
265
266	u = dev->si_drv1;
267
268	mtx_lock(&u->mutex);
269	u->busy = 0;
270	if (u->use_fifo) {
271		/* TNT5004 or TNT4882 in FIFO mode */
272		bus_write_1(u->reg_res[0], cmdr, 0x22); /* soft RESET */
273		bus_write_1(u->reg_res[0], imr3, 0x00);
274	}
275	upd7210_wr(u, AUXMR, AUXMR_CRST);
276	DELAY(10000);
277	upd7210_wr(u, IMR1, 0x00);
278	upd7210_wr(u, IMR2, 0x00);
279	free(u->buf, M_GPIB);
280	u->buf = NULL;
281	mtx_unlock(&u->mutex);
282	return (0);
283}
284
285static int
286gpib_l_read(struct cdev *dev, struct uio *uio, int ioflag)
287{
288	struct upd7210 *u;
289	int error;
290	size_t z;
291
292	u = dev->si_drv1;
293	error = 0;
294
295	mtx_lock(&u->mutex);
296	while (u->buf_wp == u->buf_rp) {
297		error = msleep(u->buf, &u->mutex, PZERO | PCATCH,
298		    "gpibrd", hz);
299		if (error && error != EWOULDBLOCK) {
300			mtx_unlock(&u->mutex);
301			return (error);
302		}
303	}
304	while (uio->uio_resid > 0 && u->buf_wp != u->buf_rp) {
305		if (u->buf_wp < u->buf_rp)
306			z = u->bufsize - u->buf_rp;
307		else
308			z = u->buf_wp - u->buf_rp;
309		if (z > uio->uio_resid)
310			z = uio->uio_resid;
311		mtx_unlock(&u->mutex);
312		error = uiomove(u->buf + u->buf_rp, z, uio);
313		mtx_lock(&u->mutex);
314		if (error)
315			break;
316		u->buf_rp += z;
317		u->buf_rp &= (u->bufsize - 1);
318	}
319	if (u->use_fifo) {
320		bus_write_1(u->reg_res[0], imr3, 0x04); /* NFF IE */
321	} else {
322		if (u->wreg[IMR1] == 0)
323			upd7210_wr(u, IMR1, 0x01);
324	}
325	mtx_unlock(&u->mutex);
326	return (error);
327}
328
329static struct cdevsw gpib_l_cdevsw = {
330	.d_version =	D_VERSION,
331	.d_name =	"gpib_l",
332	.d_open	=	gpib_l_open,
333	.d_close =	gpib_l_close,
334	.d_read =	gpib_l_read,
335};
336
337/* Housekeeping */
338
339static struct unrhdr *units;
340
341void
342upd7210attach(struct upd7210 *u)
343{
344	struct cdev *dev;
345
346	if (units == NULL)
347		units = new_unrhdr(0, INT_MAX, NULL);
348	u->unit = alloc_unr(units);
349	mtx_init(&u->mutex, "gpib", NULL, MTX_DEF);
350	u->cdev = make_dev(&gpib_l_cdevsw, u->unit,
351	    UID_ROOT, GID_WHEEL, 0444,
352	    "gpib%ul", u->unit);
353	u->cdev->si_drv1 = u;
354
355	dev = make_dev(&gpib_ib_cdevsw, u->unit,
356	    UID_ROOT, GID_WHEEL, 0444,
357	    "gpib%uib", u->unit);
358	dev->si_drv1 = u;
359	dev_depends(u->cdev, dev);
360}
361
362void
363upd7210detach(struct upd7210 *u)
364{
365
366	destroy_dev(u->cdev);
367	mtx_destroy(&u->mutex);
368	free_unr(units, u->unit);
369}
370