1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1997-2000 Nicolas Souchu
5 * Copyright (c) 2001 Alcove - Nicolas Souchu
6 * Copyright (c) 2006 Marcel Moolenaar
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/lock.h>
38#include <sys/module.h>
39#include <sys/mutex.h>
40#include <sys/bus.h>
41#include <machine/bus.h>
42#include <sys/malloc.h>
43#include <sys/rman.h>
44
45#include <isa/isavar.h>
46
47#include <dev/ppbus/ppbconf.h>
48#include <dev/ppbus/ppb_msq.h>
49#include <dev/ppc/ppcvar.h>
50#include <dev/ppc/ppcreg.h>
51
52#include "ppbus_if.h"
53
54static int ppc_isa_probe(device_t dev);
55
56int ppc_isa_attach(device_t dev);
57int ppc_isa_write(device_t, char *, int, int);
58
59static device_method_t ppc_isa_methods[] = {
60	/* device interface */
61	DEVMETHOD(device_probe,		ppc_isa_probe),
62	DEVMETHOD(device_attach,	ppc_isa_attach),
63	DEVMETHOD(device_detach,	ppc_detach),
64
65	/* bus interface */
66	DEVMETHOD(bus_read_ivar,	ppc_read_ivar),
67	DEVMETHOD(bus_write_ivar,	ppc_write_ivar),
68	DEVMETHOD(bus_alloc_resource,	ppc_alloc_resource),
69	DEVMETHOD(bus_release_resource,	ppc_release_resource),
70
71	/* ppbus interface */
72	DEVMETHOD(ppbus_io,		ppc_io),
73	DEVMETHOD(ppbus_exec_microseq,	ppc_exec_microseq),
74	DEVMETHOD(ppbus_reset_epp,	ppc_reset_epp),
75	DEVMETHOD(ppbus_setmode,	ppc_setmode),
76	DEVMETHOD(ppbus_ecp_sync,	ppc_ecp_sync),
77	DEVMETHOD(ppbus_read,		ppc_read),
78	DEVMETHOD(ppbus_write,		ppc_isa_write),
79	{ 0, 0 }
80};
81
82static driver_t ppc_isa_driver = {
83	ppc_driver_name,
84	ppc_isa_methods,
85	sizeof(struct ppc_data),
86};
87
88static struct isa_pnp_id lpc_ids[] = {
89	{ 0x0004d041, "Standard parallel printer port" },	/* PNP0400 */
90	{ 0x0104d041, "ECP parallel printer port" },		/* PNP0401 */
91	{ 0 }
92};
93
94static void
95ppc_isa_dmadone(struct ppc_data *ppc)
96{
97	isa_dmadone(ppc->ppc_dmaflags, ppc->ppc_dmaddr, ppc->ppc_dmacnt,
98	    ppc->ppc_dmachan);
99}
100
101int
102ppc_isa_attach(device_t dev)
103{
104	struct ppc_data *ppc = device_get_softc(dev);
105
106	if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) {
107		/* acquire the DMA channel forever */   /* XXX */
108		isa_dma_acquire(ppc->ppc_dmachan);
109		isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */
110		ppc->ppc_dmadone = ppc_isa_dmadone;
111	}
112
113	return (ppc_attach(dev));
114}
115
116static int
117ppc_isa_probe(device_t dev)
118{
119	device_t parent;
120	int error;
121
122	parent = device_get_parent(dev);
123
124	error = ISA_PNP_PROBE(parent, dev, lpc_ids);
125	if (error == ENXIO)
126		return (ENXIO);
127	if (error != 0)		/* XXX shall be set after detection */
128		device_set_desc(dev, "Parallel port");
129
130	return (ppc_probe(dev, 0));
131}
132
133/*
134 * Call this function if you want to send data in any advanced mode
135 * of your parallel port: FIFO, DMA
136 *
137 * If what you want is not possible (no ECP, no DMA...),
138 * EINVAL is returned
139 */
140int
141ppc_isa_write(device_t dev, char *buf, int len, int how)
142{
143	struct ppc_data *ppc = device_get_softc(dev);
144	char ecr, ecr_sav, ctr, ctr_sav;
145	int error = 0;
146	int spin;
147
148	PPC_ASSERT_LOCKED(ppc);
149	if (!(ppc->ppc_avm & PPB_ECP))
150		return (EINVAL);
151	if (ppc->ppc_dmachan == 0)
152		return (EINVAL);
153
154#ifdef PPC_DEBUG
155	printf("w");
156#endif
157
158	ecr_sav = r_ecr(ppc);
159	ctr_sav = r_ctr(ppc);
160
161	/*
162	 * Send buffer with DMA, FIFO and interrupts
163	 */
164
165	/* byte mode, no intr, no DMA, dir=0, flush fifo */
166	ecr = PPC_ECR_STD | PPC_DISABLE_INTR;
167	w_ecr(ppc, ecr);
168
169	/* disable nAck interrupts */
170	ctr = r_ctr(ppc);
171	ctr &= ~IRQENABLE;
172	w_ctr(ppc, ctr);
173
174	ppc->ppc_dmaflags = 0;
175	ppc->ppc_dmaddr = (caddr_t)buf;
176	ppc->ppc_dmacnt = (u_int)len;
177
178	switch (ppc->ppc_mode) {
179	case PPB_COMPATIBLE:
180		/* compatible mode with FIFO, no intr, DMA, dir=0 */
181		ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
182		break;
183	case PPB_ECP:
184		ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
185		break;
186	default:
187		error = EINVAL;
188		goto error;
189	}
190
191	w_ecr(ppc, ecr);
192	ecr = r_ecr(ppc);
193
194	ppc->ppc_dmastat = PPC_DMA_INIT;
195
196	/* enable interrupts */
197	ecr &= ~PPC_SERVICE_INTR;
198	ppc->ppc_irqstat = PPC_IRQ_DMA;
199	w_ecr(ppc, ecr);
200
201	isa_dmastart(ppc->ppc_dmaflags, ppc->ppc_dmaddr, ppc->ppc_dmacnt,
202		     ppc->ppc_dmachan);
203	ppc->ppc_dmastat = PPC_DMA_STARTED;
204
205#ifdef PPC_DEBUG
206	printf("s%d", ppc->ppc_dmacnt);
207#endif
208
209	/* Wait for the DMA completed interrupt. We hope we won't
210	 * miss it, otherwise a signal will be necessary to unlock the
211	 * process.
212	 */
213	do {
214		/* release CPU */
215		error = mtx_sleep(ppc, &ppc->ppc_lock, PPBPRI | PCATCH,
216		    "ppcdma", 0);
217	} while (error == EWOULDBLOCK);
218
219	if (error) {
220#ifdef PPC_DEBUG
221		printf("i");
222#endif
223		/* stop DMA */
224		isa_dmadone(ppc->ppc_dmaflags, ppc->ppc_dmaddr,
225			    ppc->ppc_dmacnt, ppc->ppc_dmachan);
226
227		/* no dma, no interrupt, flush the fifo */
228		w_ecr(ppc, PPC_ECR_RESET);
229
230		ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
231		goto error;
232	}
233
234	/* wait for an empty fifo */
235	while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
236		for (spin=100; spin; spin--)
237			if (r_ecr(ppc) & PPC_FIFO_EMPTY)
238				goto fifo_empty;
239#ifdef PPC_DEBUG
240		printf("Z");
241#endif
242		error = mtx_sleep(ppc, &ppc->ppc_lock, PPBPRI | PCATCH,
243		    "ppcfifo", hz / 100);
244		if (error != EWOULDBLOCK) {
245#ifdef PPC_DEBUG
246			printf("I");
247#endif
248			/* no dma, no interrupt, flush the fifo */
249			w_ecr(ppc, PPC_ECR_RESET);
250
251			ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
252			error = EINTR;
253			goto error;
254		}
255	}
256
257fifo_empty:
258	/* no dma, no interrupt, flush the fifo */
259	w_ecr(ppc, PPC_ECR_RESET);
260
261error:
262	/* PDRQ must be kept unasserted until nPDACK is
263	 * deasserted for a minimum of 350ns (SMC datasheet)
264	 *
265	 * Consequence may be a FIFO that never empty
266	 */
267	DELAY(1);
268
269	w_ecr(ppc, ecr_sav);
270	w_ctr(ppc, ctr_sav);
271	return (error);
272}
273
274DRIVER_MODULE(ppc, isa, ppc_isa_driver, ppc_devclass, 0, 0);
275ISA_PNP_INFO(lpc_ids);
276