1/*	$NetBSD: par.c,v 1.45 2024/01/07 07:58:33 isaki Exp $	*/
2
3/*
4 * Copyright (c) 1982, 1990 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	@(#)ppi.c	7.3 (Berkeley) 12/16/90
32 */
33
34/*
35 * parallel port interface
36 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD: par.c,v 1.45 2024/01/07 07:58:33 isaki Exp $");
40
41#include <sys/param.h>
42#include <sys/errno.h>
43#include <sys/uio.h>
44#include <sys/device.h>
45#include <sys/malloc.h>
46#include <sys/file.h>
47#include <sys/systm.h>
48#include <sys/callout.h>
49#include <sys/proc.h>
50#include <sys/conf.h>
51#include <sys/kernel.h>
52
53#include <machine/bus.h>
54#include <machine/cpu.h>
55#include <machine/parioctl.h>
56
57#include <arch/x68k/dev/intiovar.h>
58
59#include "ioconf.h"
60
61struct	par_softc {
62	device_t		sc_dev;
63
64	bus_space_tag_t		sc_bst;
65	bus_space_handle_t	sc_bsh;
66	int			sc_flags;
67	struct parparam		sc_param;
68#define sc_burst	sc_param.burst
69#define sc_timo		sc_param.timo
70#define sc_delay	sc_param.delay
71	struct callout		sc_timo_ch;
72	struct callout		sc_start_ch;
73} ;
74
75/* par registers */
76#define PAR_DATA	1
77#define PAR_STROBE	3
78
79/* sc_flags values */
80#define	PARF_ALIVE	0x01
81#define	PARF_OPEN	0x02
82#define PARF_UIO	0x04
83#define PARF_TIMO	0x08
84#define PARF_DELAY	0x10
85#define PARF_OREAD	0x40	/* no support */
86#define PARF_OWRITE	0x80
87
88
89void partimo(void *);
90void parstart(void *);
91void parintr(void *);
92int parrw(dev_t, struct uio *);
93int parhztoms(int);
94int parmstohz(int);
95int parsendch(struct par_softc *, u_char);
96int parsend(struct par_softc *, u_char *, int);
97
98static struct callout intr_callout;
99
100#define UNIT(x)		minor(x)
101
102#ifdef DEBUG
103#define PDB_FOLLOW	0x01
104#define PDB_IO		0x02
105#define PDB_INTERRUPT   0x04
106#define PDB_NOCHECK	0x80
107#ifdef PARDEBUG
108int	pardebug = PDB_FOLLOW | PDB_IO | PDB_INTERRUPT;
109#else
110int	pardebug = 0;
111#endif
112#endif
113
114int parmatch(device_t, cfdata_t, void *);
115void parattach(device_t, device_t, void *);
116
117CFATTACH_DECL_NEW(par, sizeof(struct par_softc),
118    parmatch, parattach, NULL, NULL);
119
120static int par_attached;
121
122dev_type_open(paropen);
123dev_type_close(parclose);
124dev_type_write(parwrite);
125dev_type_ioctl(parioctl);
126
127const struct cdevsw par_cdevsw = {
128	.d_open = paropen,
129	.d_close = parclose,
130	.d_read = noread,
131	.d_write = parwrite,
132	.d_ioctl = parioctl,
133	.d_stop = nostop,
134	.d_tty = notty,
135	.d_poll = nopoll,
136	.d_mmap = nommap,
137	.d_kqfilter = nokqfilter,
138	.d_discard = nodiscard,
139	.d_flag = 0
140};
141
142int
143parmatch(device_t parent, cfdata_t cf, void *aux)
144{
145	struct intio_attach_args *ia = aux;
146
147	/* X680x0 has only one parallel port */
148	if (strcmp(ia->ia_name, "par") || par_attached)
149		return 0;
150
151	if (ia->ia_addr == INTIOCF_ADDR_DEFAULT)
152		ia->ia_addr = 0xe8c000;
153	ia->ia_size = 0x2000;
154	if (intio_map_allocate_region(parent, ia, INTIO_MAP_TESTONLY))
155		return 0;
156	if (ia->ia_intr == INTIOCF_INTR_DEFAULT)
157		ia->ia_intr = 99;
158#if DIAGNOSTIC
159	if (ia->ia_intr != 99)
160		return 0;
161#endif
162
163	return 1;
164}
165
166void
167parattach(device_t parent, device_t self, void *aux)
168{
169	struct par_softc *sc = device_private(self);
170	struct intio_attach_args *ia = aux;
171	int r __diagused;
172
173	par_attached = 1;
174
175	sc->sc_dev = self;
176	sc->sc_flags = PARF_ALIVE;
177	aprint_normal(": parallel port (write only, interrupt)\n");
178	ia->ia_size = 0x2000;
179	r = intio_map_allocate_region(parent, ia, INTIO_MAP_ALLOCATE);
180#ifdef DIAGNOSTIC
181	if (r)
182		panic("IO map for PAR corruption??");
183#endif
184	sc->sc_bst = ia->ia_bst;
185	r = bus_space_map(sc->sc_bst,
186			   ia->ia_addr, ia->ia_size,
187			   BUS_SPACE_MAP_SHIFTED,
188			   &sc->sc_bsh);
189#ifdef DIAGNOSTIC
190	if (r)
191		panic("Cannot map IO space for PAR.");
192#endif
193
194	intio_set_sicilian_intr(intio_get_sicilian_intr() &
195				~SICILIAN_INTR_PAR);
196
197	intio_intr_establish(ia->ia_intr, "par",
198			     (intio_intr_handler_t)parintr, (void *)1);
199
200	callout_init(&sc->sc_timo_ch, 0);
201	callout_init(&sc->sc_start_ch, 0);
202	callout_init(&intr_callout, 0);
203}
204
205int
206paropen(dev_t dev, int flags, int mode, struct lwp *l)
207{
208	int unit = UNIT(dev);
209	struct par_softc *sc;
210
211	sc = device_lookup_private(&par_cd, unit);
212	if (sc == NULL || !(sc->sc_flags & PARF_ALIVE))
213		return(ENXIO);
214	if (sc->sc_flags & PARF_OPEN)
215		return(EBUSY);
216	/* X680x0 can't read */
217	if ((flags & FREAD) == FREAD)
218		return (EINVAL);
219
220	sc->sc_flags |= PARF_OPEN;
221
222	sc->sc_flags |= PARF_OWRITE;
223
224	sc->sc_burst = PAR_BURST;
225	sc->sc_timo = parmstohz(PAR_TIMO);
226	sc->sc_delay = parmstohz(PAR_DELAY);
227
228	return(0);
229}
230
231int
232parclose(dev_t dev, int flags, int mode, struct lwp *l)
233{
234	int unit = UNIT(dev);
235	int s;
236	struct par_softc *sc = device_lookup_private(&par_cd, unit);
237
238	sc->sc_flags &= ~(PARF_OPEN|PARF_OWRITE);
239
240	/* don't allow interrupts any longer */
241	s = spl1();
242	intio_set_sicilian_intr(intio_get_sicilian_intr() &
243				~SICILIAN_INTR_PAR);
244	splx(s);
245
246	return (0);
247}
248
249void
250parstart(void *arg)
251{
252	struct par_softc *sc = arg;
253#ifdef DEBUG
254	if (pardebug & PDB_FOLLOW)
255		printf("parstart(%x)\n", device_unit(sc->sc_dev));
256#endif
257	sc->sc_flags &= ~PARF_DELAY;
258	wakeup(sc);
259}
260
261void
262partimo(void *arg)
263{
264	struct par_softc *sc = arg;
265#ifdef DEBUG
266	if (pardebug & PDB_FOLLOW)
267		printf("partimo(%x)\n", device_unit(sc->sc_dev));
268#endif
269	sc->sc_flags &= ~(PARF_UIO|PARF_TIMO);
270	wakeup(sc);
271}
272
273int
274parwrite(dev_t dev, struct uio *uio, int flag)
275{
276
277#ifdef DEBUG
278	if (pardebug & PDB_FOLLOW)
279		printf("parwrite(%x, %p)\n", UNIT(dev), uio);
280#endif
281	return (parrw(dev, uio));
282}
283
284int
285parrw(dev_t dev, struct uio *uio)
286{
287	int unit = UNIT(dev);
288	struct par_softc *sc = device_lookup_private(&par_cd, unit);
289	int len=0xdeadbeef;	/* XXX: shutup gcc */
290	int s, cnt=0;
291	char *cp;
292	int error = 0;
293	int buflen;
294	char *buf;
295
296	if (!!(sc->sc_flags & PARF_OREAD) ^ (uio->uio_rw == UIO_READ))
297		return EINVAL;
298
299	if (uio->uio_resid == 0)
300		return(0);
301
302	buflen = uimin(sc->sc_burst, uio->uio_resid);
303	buf = (char *)malloc(buflen, M_DEVBUF, M_WAITOK);
304	sc->sc_flags |= PARF_UIO;
305	if (sc->sc_timo > 0) {
306		sc->sc_flags |= PARF_TIMO;
307		callout_reset(&sc->sc_timo_ch, sc->sc_timo, partimo, sc);
308	}
309	while (uio->uio_resid > 0) {
310		len = uimin(buflen, uio->uio_resid);
311		cp = buf;
312		if (uio->uio_rw == UIO_WRITE) {
313			error = uiomove(cp, len, uio);
314			if (error)
315				break;
316		}
317	      again:
318		s = splsoftclock();
319		/*
320		 * Check if we timed out during sleep or uiomove
321		 */
322		if ((sc->sc_flags & PARF_UIO) == 0) {
323#ifdef DEBUG
324			if (pardebug & PDB_IO)
325				printf("parrw: uiomove/sleep timo, flags %x\n",
326				       sc->sc_flags);
327#endif
328			if (sc->sc_flags & PARF_TIMO) {
329				callout_stop(&sc->sc_timo_ch);
330				sc->sc_flags &= ~PARF_TIMO;
331			}
332			splx(s);
333			break;
334		}
335		splx(s);
336		/*
337		 * Perform the operation
338		 */
339		cnt = parsend(sc, cp, len);
340		if (cnt < 0) {
341			error = -cnt;
342			break;
343		}
344
345		s = splsoftclock();
346		/*
347		 * Operation timeout (or non-blocking), quit now.
348		 */
349		if ((sc->sc_flags & PARF_UIO) == 0) {
350#ifdef DEBUG
351			if (pardebug & PDB_IO)
352				printf("parrw: timeout/done\n");
353#endif
354			splx(s);
355			break;
356		}
357		/*
358		 * Implement inter-read delay
359		 */
360		if (sc->sc_delay > 0) {
361			sc->sc_flags |= PARF_DELAY;
362			callout_reset(&sc->sc_start_ch, sc->sc_delay,
363			    parstart, sc);
364			error = tsleep(sc, PCATCH|(PZERO-1), "par-cdelay", 0);
365			if (error) {
366				splx(s);
367				break;
368			}
369		}
370		splx(s);
371		/*
372		 * Must not call uiomove again til we've used all data
373		 * that we already grabbed.
374		 */
375		if (uio->uio_rw == UIO_WRITE && cnt != len) {
376			cp += cnt;
377			len -= cnt;
378			cnt = 0;
379			goto again;
380		}
381	}
382	s = splsoftclock();
383	if (sc->sc_flags & PARF_TIMO) {
384		callout_stop(&sc->sc_timo_ch);
385		sc->sc_flags &= ~PARF_TIMO;
386	}
387	if (sc->sc_flags & PARF_DELAY)	{
388		callout_stop(&sc->sc_start_ch);
389		sc->sc_flags &= ~PARF_DELAY;
390	}
391	splx(s);
392	/*
393	 * Adjust for those chars that we uiomove'ed but never wrote
394	 */
395	/*
396	 * XXXjdolecek: this len usage is wrong, this will be incorrect
397	 * if the transfer size is longer than sc_burst
398	 */
399	if (uio->uio_rw == UIO_WRITE && cnt != len) {
400		uio->uio_resid += (len - cnt);
401#ifdef DEBUG
402			if (pardebug & PDB_IO)
403				printf("parrw: short write, adjust by %d\n",
404				       len-cnt);
405#endif
406	}
407	free(buf, M_DEVBUF);
408#ifdef DEBUG
409	if (pardebug & (PDB_FOLLOW|PDB_IO))
410		printf("parrw: return %d, resid %d\n", error, uio->uio_resid);
411#endif
412	return (error);
413}
414
415int
416parioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
417{
418	struct par_softc *sc = device_lookup_private(&par_cd, UNIT(dev));
419	struct parparam *pp, *upp;
420	int error = 0;
421
422	switch (cmd) {
423	case PARIOCGPARAM:
424		pp = &sc->sc_param;
425		upp = (struct parparam *)data;
426		upp->burst = pp->burst;
427		upp->timo = parhztoms(pp->timo);
428		upp->delay = parhztoms(pp->delay);
429		break;
430
431	case PARIOCSPARAM:
432		pp = &sc->sc_param;
433		upp = (struct parparam *)data;
434		if (upp->burst < PAR_BURST_MIN || upp->burst > PAR_BURST_MAX ||
435		    upp->delay < PAR_DELAY_MIN || upp->delay > PAR_DELAY_MAX)
436			return(EINVAL);
437		pp->burst = upp->burst;
438		pp->timo = parmstohz(upp->timo);
439		pp->delay = parmstohz(upp->delay);
440		break;
441
442	default:
443		return(EINVAL);
444	}
445	return (error);
446}
447
448int
449parhztoms(int h)
450{
451	int m = h;
452
453	if (m > 0)
454		m = m * 1000 / hz;
455	return(m);
456}
457
458int
459parmstohz(int m)
460{
461	int h = m;
462
463	if (h > 0) {
464		h = h * hz / 1000;
465		if (h == 0)
466			h = 1000 / hz;
467	}
468	return(h);
469}
470
471/* stuff below here if for interrupt driven output of data thru
472   the parallel port. */
473
474int partimeout_pending;
475int parsend_pending;
476
477void
478parintr(void *arg)
479{
480	int s, mask;
481
482	mask = (int)arg;
483	s = splclock();
484
485	intio_set_sicilian_intr(intio_get_sicilian_intr() &
486				~SICILIAN_INTR_PAR);
487
488#ifdef DEBUG
489	if (pardebug & PDB_INTERRUPT)
490		printf("parintr %d(%s)\n", mask, mask ? "FLG" : "tout");
491#endif
492	/* if invoked from timeout handler, mask will be 0,
493	 * if from interrupt, it will contain the cia-icr mask,
494	 * which is != 0
495	 */
496	if (mask) {
497		if (partimeout_pending)
498			callout_stop(&intr_callout);
499		if (parsend_pending)
500			parsend_pending = 0;
501	}
502
503	/* either way, there won't be a timeout pending any longer */
504	partimeout_pending = 0;
505
506	wakeup(parintr);
507	splx(s);
508}
509
510int
511parsendch(struct par_softc *sc, u_char ch)
512{
513	int error = 0;
514	int s;
515
516	/* if either offline, busy or out of paper, wait for that
517	   condition to clear */
518	s = spl1();
519	while (!error
520	       && (parsend_pending
521		   || !(intio_get_sicilian_intr() & SICILIAN_STAT_PAR)))
522	{
523		/* wait a second, and try again */
524		callout_reset(&intr_callout, hz, parintr, 0);
525		partimeout_pending = 1;
526		/* this is essentially a flipflop to have us wait for the
527		   first character being transmitted when trying to transmit
528		   the second, etc. */
529		parsend_pending = 0;
530		/* it's quite important that a parallel putc can be
531		   interrupted, given the possibility to lock a printer
532		   in an offline condition.. */
533		if ((error = tsleep(parintr, PCATCH|(PZERO-1), "parsendch", 0))) {
534#ifdef DEBUG
535			if (pardebug & PDB_INTERRUPT)
536				printf("parsendch interrupted, error = %d\n", error);
537#endif
538			if (partimeout_pending)
539				callout_stop(&intr_callout);
540
541			partimeout_pending = 0;
542		}
543	}
544
545	if (!error) {
546#ifdef DEBUG
547		if (pardebug & PDB_INTERRUPT)
548			printf("#%d", ch);
549#endif
550		bus_space_write_1(sc->sc_bst, sc->sc_bsh, PAR_DATA, ch);
551		DELAY(1);	/* (DELAY(1) == 1us) > 0.5us */
552		bus_space_write_1(sc->sc_bst, sc->sc_bsh, PAR_STROBE, 0);
553		intio_set_sicilian_intr(intio_get_sicilian_intr() |
554					 SICILIAN_INTR_PAR);
555		DELAY(1);
556		bus_space_write_1(sc->sc_bst, sc->sc_bsh, PAR_STROBE, 1);
557		parsend_pending = 1;
558	}
559
560	splx(s);
561
562	return error;
563}
564
565
566int
567parsend(struct par_softc *sc, u_char *buf, int len)
568{
569	int err, orig_len = len;
570
571	for (; len; len--, buf++)
572		if ((err = parsendch(sc, *buf)))
573			return err < 0 ? -EINTR : -err;
574
575	/* either all or nothing.. */
576	return orig_len;
577}
578