pcii.c revision 141123
1/*-
2 * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer as
10 *    the first lines of this file unmodified.
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 AUTHORS ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * Supported hardware:
27 *    PCIIA compatible cards.
28 *
29 *    Tested and known working:
30 *	"B&C Microsystems PC488A-0"
31 *
32 * A whole lot of wonderful things could be written for GPIB, but for now
33 * I have just written it such that it is possible to capture data in the
34 * mode known as "unaddressed listen only mode".  This is what many plotters
35 * and printers do on GPIB.  This is enough to capture some output from
36 * various test instruments.
37 *
38 * If you are interested in working on this, send me email.
39 */
40
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD: head/sys/dev/ieee488/pcii.c 141123 2005-02-01 20:34:47Z phk $");
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/conf.h>
47#include <sys/malloc.h>
48#include <sys/kernel.h>
49#include <sys/module.h>
50#include <sys/bus.h>
51#include <sys/mutex.h>
52#include <sys/uio.h>
53#include <machine/bus.h>
54#include <machine/resource.h>
55#include <sys/rman.h>
56
57/* ---> upd7210.h at some point. */
58
59struct upd7210 {
60	bus_space_handle_t	reg_handle[8];
61	bus_space_tag_t		reg_tag[8];
62	u_int			reg_offset[8];
63
64	/* private stuff */
65	struct mtx		mutex;
66	uint8_t			rreg[8];
67	uint8_t			wreg[8];
68
69	int			busy;
70	u_char			*buf;
71	size_t			bufsize;
72	u_int			buf_wp;
73	u_int			buf_rp;
74	struct cdev		*cdev;
75};
76
77static void upd7210intr(void *);
78static void upd7210attach(struct upd7210 *);
79
80
81/* ----> pcii.c */
82
83struct pcii_softc {
84	int foo;
85	struct resource	*port[8];
86	struct resource	*irq;
87	void *intr_handler;
88	struct upd7210	upd7210;
89};
90
91#define HERE() printf("pcii HERE %s:%d\n", __FILE__, __LINE__)
92
93static devclass_t pcii_devclass;
94
95static int	pcii_probe(device_t dev);
96static int	pcii_attach(device_t dev);
97
98static device_method_t pcii_methods[] = {
99	DEVMETHOD(device_probe,		pcii_probe),
100	DEVMETHOD(device_attach,	pcii_attach),
101	DEVMETHOD(device_suspend,	bus_generic_suspend),
102	DEVMETHOD(device_resume,	bus_generic_resume),
103
104	{ 0, 0 }
105};
106
107static driver_t pcii_driver = {
108	"pcii",
109	pcii_methods,
110	sizeof(struct pcii_softc *),
111};
112
113static int
114pcii_probe(device_t dev)
115{
116	struct resource	*port;
117	int rid;
118	u_long start, count;
119	int i, j, error = 0;
120
121	device_set_desc(dev, "PCII IEEE-4888 controller");
122
123	rid = 0;
124	if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count) != 0)
125		return ENXIO;
126	if ((start & 0x3ff) != 0x2e1)
127		return (ENXIO);
128	count = 1;
129	if (bus_set_resource(dev, SYS_RES_IOPORT, rid, start, count) != 0)
130		return ENXIO;
131	for (i = 0; i < 8; i++) {
132		j = bus_set_resource(dev, SYS_RES_IOPORT, i,
133		    start + 0x400 * i, 1);
134		if (j) {
135			error = ENXIO;
136			break;
137		}
138		rid = i;
139		port = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
140		    &rid, RF_ACTIVE);
141		if (port == NULL)
142			return (ENXIO);
143		else
144			bus_release_resource(dev, SYS_RES_IOPORT, i, port);
145	}
146
147	rid = 0;
148	port = bus_alloc_resource_any(dev,
149	    SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE);
150	if (port == NULL)
151		return (ENXIO);
152	bus_release_resource(dev, SYS_RES_IRQ, rid, port);
153
154	return (error);
155}
156
157static int
158pcii_attach(device_t dev)
159{
160	struct pcii_softc *sc;
161	int		unit;
162	int		rid;
163	int i, error = 0;
164
165	unit = device_get_unit(dev);
166	sc = device_get_softc(dev);
167	memset(sc, 0, sizeof *sc);
168
169	device_set_desc(dev, "PCII IEEE-4888 controller");
170
171	for (rid = 0; rid < 8; rid++) {
172		sc->port[rid] = bus_alloc_resource_any(dev,
173		    SYS_RES_IOPORT, &rid, RF_ACTIVE);
174		if (sc->port[rid] == NULL) {
175			error = ENXIO;
176			break;
177		}
178		sc->upd7210.reg_tag[rid] = rman_get_bustag(sc->port[rid]);
179		sc->upd7210.reg_handle[rid] = rman_get_bushandle(sc->port[rid]);
180	}
181	if (!error) {
182		rid = 0;
183		sc->irq = bus_alloc_resource_any(dev,
184		    SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE);
185		if (sc->irq == NULL) {
186			error = ENXIO;
187		} else {
188			error = bus_setup_intr(dev, sc->irq,
189			    INTR_TYPE_MISC | INTR_MPSAFE,
190			    upd7210intr, &sc->upd7210, &sc->intr_handler);
191		}
192	}
193	if (error) {
194device_printf(dev, "error = %d\n", error);
195		for (i = 0; i < 8; i++) {
196			if (sc->port[i] == NULL)
197				break;
198			bus_release_resource(dev, SYS_RES_IOPORT,
199			    0, sc->port[i]);
200		}
201		if (sc->intr_handler != NULL)
202			bus_teardown_intr(dev, sc->irq, sc->intr_handler);
203		if (sc->irq != NULL)
204			bus_release_resource(dev, SYS_RES_IRQ, i, sc->irq);
205	}
206	upd7210attach(&sc->upd7210);
207	return (error);
208}
209
210DRIVER_MODULE(pcii, isa, pcii_driver, pcii_devclass, 0, 0);
211DRIVER_MODULE(pcii, acpi, pcii_driver, pcii_devclass, 0, 0);
212
213/* ---> upd7210.c at some point */
214
215enum upd7210_wreg {
216	CDOR	= 0,	/* Command/data out	*/
217	IMR1	= 1,	/* Interrupt mask 1	*/
218	IMR2	= 2,	/* Interrupt mask 2	*/
219	SPMR	= 3,	/* Serial poll mode	*/
220	ADMR	= 4,	/* Address mode		*/
221	AUXMR	= 5,	/* Auxilliary mode	*/
222	ADR	= 6,	/* Address		*/
223	EOSR	= 7,	/* End-of-string	*/
224};
225
226enum upd7210_rreg {
227	DIR	= 0,	/* Data in		*/
228	ISR1	= 1,	/* Interrupt status 1	*/
229	ISR2	= 2,	/* Interrupt status 2	*/
230	SPSR	= 3,	/* Serial poll status	*/
231	ADSR	= 4,	/* Address status	*/
232	CPTR	= 5,	/* Command pass though	*/
233	ADR0	= 6,	/* Address 1		*/
234	ADR1	= 7,	/* Address 2		*/
235};
236
237#define AUXMR_PON        0x00
238#define AUXMR_CRST       0x02
239#define AUXMR_RFD        0x03
240#define AUXMR_SEOI       0x06
241#define AUXMR_GTS        0x10
242#define AUXMR_TCA        0x11
243#define AUXMR_TCS        0x12
244#define AUXMR_TCSE       0x1a
245#define AUXMR_DSC        0x14
246#define AUXMR_CIFC       0x16
247#define AUXMR_SIFC       0x1e
248#define AUXMR_CREN       0x17
249#define AUXMR_SREN       0x1f
250#define AUXMR_ICTR       0x20
251#define AUXMR_PPR        0x60
252#define AUXMR_RA         0x80
253#define AUXMR_RB         0xa0
254#define AUXMR_RE         0xc0
255
256
257/* upd7210 generic stuff */
258
259static u_int
260read_reg(struct upd7210 *u, enum upd7210_rreg reg)
261{
262	u_int r;
263
264	r = bus_space_read_1(
265	    u->reg_tag[reg],
266	    u->reg_handle[reg],
267	    u->reg_offset[reg]);
268	u->rreg[reg] = r;
269	return (r);
270}
271
272static void
273write_reg(struct upd7210 *u, enum upd7210_wreg reg, u_int val)
274{
275	bus_space_write_1(
276	    u->reg_tag[reg],
277	    u->reg_handle[reg],
278	    u->reg_offset[reg], val);
279	u->wreg[reg] = val;
280}
281
282static void
283upd7210intr(void *arg)
284{
285	int i;
286	u_int isr1, isr2;
287	struct upd7210 *u;
288
289	u = arg;
290	mtx_lock(&u->mutex);
291	isr1 = read_reg(u, ISR1);
292	isr2 = read_reg(u, ISR2);
293	if (isr1 & 1) {
294		i = read_reg(u, DIR);
295		u->buf[u->buf_wp++] = i;
296		u->buf_wp &= (u->bufsize - 1);
297		i = (u->buf_rp + u->bufsize - u->buf_wp) & (u->bufsize - 1);
298		if (i < 8)
299			write_reg(u, IMR1, 0);
300		wakeup(u->buf);
301	} else {
302		printf("upd7210intr [%02x %02x %02x",
303		    read_reg(u, DIR), isr1, isr2);
304		printf(" %02x %02x %02x %02x %02x]\n",
305		    read_reg(u, SPSR),
306		    read_reg(u, ADSR),
307		    read_reg(u, CPTR),
308		    read_reg(u, ADR0),
309		    read_reg(u, ADR1));
310	}
311	mtx_unlock(&u->mutex);
312}
313
314static int
315gpib_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
316{
317	struct upd7210 *u;
318
319	u = dev->si_drv1;
320
321	mtx_lock(&u->mutex);
322	if (u->busy)
323		return (EBUSY);
324	u->busy = 1;
325	mtx_unlock(&u->mutex);
326
327	u->buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
328	u->bufsize = PAGE_SIZE;
329	u->buf_wp = 0;
330	u->buf_rp = 0;
331
332	write_reg(u, AUXMR, AUXMR_CRST);
333	DELAY(10000);
334	write_reg(u, AUXMR, AUXMR_ICTR | 8);
335	DELAY(1000);
336	write_reg(u, ADR, 0x60);
337	write_reg(u, ADR, 0xe0);
338	write_reg(u, ADMR, 0x70);
339	write_reg(u, AUXMR, AUXMR_PON);
340	write_reg(u, IMR1, 0x01);
341	return (0);
342}
343
344static int
345gpib_close(struct cdev *dev, int oflags, int devtype, struct thread *td)
346{
347	struct upd7210 *u;
348
349	u = dev->si_drv1;
350
351	mtx_lock(&u->mutex);
352	u->busy = 0;
353	write_reg(u, AUXMR, AUXMR_CRST);
354	DELAY(10000);
355	write_reg(u, IMR1, 0x00);
356	write_reg(u, IMR2, 0x00);
357	free(u->buf, M_DEVBUF);
358	u->buf = NULL;
359	mtx_unlock(&u->mutex);
360	return (0);
361}
362
363static int
364gpib_read(struct cdev *dev, struct uio *uio, int ioflag)
365{
366	struct upd7210 *u;
367	int error;
368	size_t z;
369
370	u = dev->si_drv1;
371	error = 0;
372
373	mtx_lock(&u->mutex);
374	while (u->buf_wp == u->buf_rp) {
375		error = msleep(u->buf, &u->mutex, PZERO | PCATCH,
376		    "gpibrd", hz);
377		if (error && error != EWOULDBLOCK) {
378			mtx_unlock(&u->mutex);
379			return (error);
380		}
381	}
382	while (uio->uio_resid > 0 && u->buf_wp != u->buf_rp) {
383		if (u->buf_wp < u->buf_rp)
384			z = u->bufsize - u->buf_rp;
385		else
386			z = u->buf_wp - u->buf_rp;
387		if (z > uio->uio_resid)
388			z = uio->uio_resid;
389		mtx_unlock(&u->mutex);
390		error = uiomove(u->buf + u->buf_rp, z, uio);
391		mtx_lock(&u->mutex);
392		if (error)
393			break;
394		u->buf_rp += z;
395		u->buf_rp &= (u->bufsize - 1);
396	}
397	if (u->wreg[IMR1] == 0)
398		write_reg(u, IMR1, 0x01);
399	mtx_unlock(&u->mutex);
400	return (error);
401}
402
403
404
405struct cdevsw gpib_cdevsw = {
406	.d_version =	D_VERSION,
407	.d_name =	"gpib",
408	.d_open	=	gpib_open,
409	.d_close =	gpib_close,
410	.d_read =	gpib_read,
411};
412
413static void
414upd7210attach(struct upd7210 *u)
415{
416	int unit = 0;
417
418	mtx_init(&u->mutex, "gpib", NULL, MTX_DEF);
419	u->cdev = make_dev(&gpib_cdevsw, unit,
420	    UID_ROOT, GID_WHEEL, 0444,
421	    "gpib%ul", unit);
422	u->cdev->si_drv1 = u;
423}
424