xref: /netbsd-src/sys/arch/mac68k/nubus/cpi_nubus.c (revision c2d090fa74a790634b934c9d63ba18942f1fcee0)
1*c2d090faSthorpej /*	$NetBSD: cpi_nubus.c,v 1.11 2020/12/19 21:48:04 thorpej Exp $	*/
2f5ede5dcShauke 
3f5ede5dcShauke /*-
4f5ede5dcShauke  * Copyright (c) 2008 Hauke Fath
5f5ede5dcShauke  *
6f5ede5dcShauke  * Redistribution and use in source and binary forms, with or without
7f5ede5dcShauke  * modification, are permitted provided that the following conditions
8f5ede5dcShauke  * are met:
9f5ede5dcShauke  * 1. Redistributions of source code must retain the above copyright
10f5ede5dcShauke  *    notice, this list of conditions and the following disclaimer.
11f5ede5dcShauke  * 2. Redistributions in binary form must reproduce the above copyright
12f5ede5dcShauke  *    notice, this list of conditions and the following disclaimer in the
13f5ede5dcShauke  *    documentation and/or other materials provided with the distribution.
14f5ede5dcShauke  *
15f5ede5dcShauke  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16f5ede5dcShauke  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17f5ede5dcShauke  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18f5ede5dcShauke  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19f5ede5dcShauke  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20f5ede5dcShauke  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21f5ede5dcShauke  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22f5ede5dcShauke  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23f5ede5dcShauke  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24f5ede5dcShauke  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25f5ede5dcShauke  */
26f5ede5dcShauke 
27f5ede5dcShauke #include <sys/cdefs.h>
28*c2d090faSthorpej __KERNEL_RCSID(0, "$NetBSD: cpi_nubus.c,v 1.11 2020/12/19 21:48:04 thorpej Exp $");
29f5ede5dcShauke #include <sys/param.h>
30f5ede5dcShauke #include <sys/systm.h>
31f5ede5dcShauke #include <sys/proc.h>
32f5ede5dcShauke #include <sys/device.h>
33*c2d090faSthorpej #include <sys/kmem.h>
34f5ede5dcShauke #include <sys/event.h>
35f5ede5dcShauke #include <sys/callout.h>
36f5ede5dcShauke #include <sys/conf.h>
37f5ede5dcShauke #include <sys/file.h>
38f5ede5dcShauke #include <sys/uio.h>
39f5ede5dcShauke #include <sys/ioctl.h>
40f5ede5dcShauke #include <sys/tty.h>
41f5ede5dcShauke #include <sys/time.h>
422413e475Shauke #include <sys/timetc.h>
43f5ede5dcShauke #include <sys/kernel.h>
44f5ede5dcShauke #include <sys/syslog.h>
45f5ede5dcShauke #include <sys/errno.h>
46f5ede5dcShauke 
47f5ede5dcShauke #include <machine/intr.h>
48f5ede5dcShauke #include <machine/bus.h>
49f5ede5dcShauke #include <machine/viareg.h>
50f5ede5dcShauke 
51f5ede5dcShauke #include <dev/ic/z8536reg.h>
52f5ede5dcShauke #include <mac68k/nubus/nubus.h>
53f5ede5dcShauke #include <mac68k/nubus/cpi_nubusvar.h>
54f5ede5dcShauke 
55f5ede5dcShauke #include "ioconf.h"
56f5ede5dcShauke 
57f5ede5dcShauke #ifdef DEBUG
58f5ede5dcShauke #define CPI_DEBUG
59f5ede5dcShauke #endif
60f5ede5dcShauke 
61f5ede5dcShauke /*
62f5ede5dcShauke  * Stuff taken from Egan/Teixeira ch 8: 'if(TRACE_FOO)' debug output
63f5ede5dcShauke  * statements don't break indentation, and when DEBUG is not defined,
64f5ede5dcShauke  * the compiler code optimizer drops them as dead code.
65f5ede5dcShauke  */
66f5ede5dcShauke #ifdef CPI_DEBUG
67f5ede5dcShauke #define M_TRACE_CONFIG	0x0001
68f5ede5dcShauke #define M_TRACE_OPEN	0x0002
69f5ede5dcShauke #define M_TRACE_CLOSE	0x0004
70f5ede5dcShauke #define M_TRACE_READ	0x0008
71f5ede5dcShauke #define M_TRACE_WRITE	0x0010
72f5ede5dcShauke #define M_TRACE_IOCTL	0x0020
73f5ede5dcShauke #define M_TRACE_STATUS	0x0040
742413e475Shauke #define M_TRACE_TCNTR	0x0080
75f5ede5dcShauke #define M_TRACE_ALL	0xFFFF
76f5ede5dcShauke #define M_TRACE_NONE	0x0000
77f5ede5dcShauke 
78f5ede5dcShauke #define TRACE_CONFIG	(cpi_debug_mask & M_TRACE_CONFIG)
79f5ede5dcShauke #define TRACE_OPEN	(cpi_debug_mask & M_TRACE_OPEN)
80f5ede5dcShauke #define TRACE_CLOSE	(cpi_debug_mask & M_TRACE_CLOSE)
81f5ede5dcShauke #define TRACE_READ	(cpi_debug_mask & M_TRACE_READ)
82f5ede5dcShauke #define TRACE_WRITE	(cpi_debug_mask & M_TRACE_WRITE)
83f5ede5dcShauke #define TRACE_IOCTL	(cpi_debug_mask & M_TRACE_IOCTL)
84f5ede5dcShauke #define TRACE_STATUS	(cpi_debug_mask & M_TRACE_STATUS)
852413e475Shauke #define TRACE_TCNTR	(cpi_debug_mask & M_TRACE_TCNTR)
86f5ede5dcShauke #define TRACE_ALL	(cpi_debug_mask & M_TRACE_ALL)
87f5ede5dcShauke #define TRACE_NONE	(cpi_debug_mask & M_TRACE_NONE)
88f5ede5dcShauke 
892413e475Shauke uint32_t cpi_debug_mask = M_TRACE_NONE /* | M_TRACE_TCNTR | M_TRACE_WRITE */ ;
902413e475Shauke #else /* CPI_DEBUG */
91f5ede5dcShauke #define TRACE_CONFIG	0
92f5ede5dcShauke #define TRACE_OPEN	0
93f5ede5dcShauke #define TRACE_CLOSE	0
94f5ede5dcShauke #define TRACE_READ	0
95f5ede5dcShauke #define TRACE_WRITE	0
96f5ede5dcShauke #define TRACE_IOCTL	0
97f5ede5dcShauke #define TRACE_STATUS	0
982413e475Shauke #define TRACE_TCNTR	0
99f5ede5dcShauke #define TRACE_ALL	0
100f5ede5dcShauke #define TRACE_NONE	0
1012413e475Shauke #endif /* CPI_DEBUG */
102f5ede5dcShauke 
103f5ede5dcShauke /* autoconf interface */
104f5ede5dcShauke int cpi_nubus_match(device_t, cfdata_t, void *);
105f5ede5dcShauke void cpi_nubus_attach(device_t, device_t, void *);
106f5ede5dcShauke void cpi_nubus_intr(void *);
107f5ede5dcShauke 
108cbab9cadSchs CFATTACH_DECL_NEW(cpi, sizeof(struct cpi_softc),
109f5ede5dcShauke     cpi_nubus_match, cpi_nubus_attach, NULL, NULL);
110f5ede5dcShauke 
111f5ede5dcShauke dev_type_open(cpi_open);
112f5ede5dcShauke dev_type_close(cpi_close);
113f5ede5dcShauke dev_type_read(cpi_read);
114f5ede5dcShauke dev_type_write(cpi_write);
115f5ede5dcShauke dev_type_ioctl(cpi_ioctl);
116f5ede5dcShauke 
117f5ede5dcShauke const struct cdevsw cpi_cdevsw = {
118a68f9396Sdholland 	.d_open = cpi_open,
119a68f9396Sdholland 	.d_close = cpi_close,
120a68f9396Sdholland 	.d_read = noread,
121a68f9396Sdholland 	.d_write = cpi_write,
122a68f9396Sdholland 	.d_ioctl = cpi_ioctl,
123a68f9396Sdholland 	.d_stop = nostop,
124a68f9396Sdholland 	.d_tty = notty,
125a68f9396Sdholland 	.d_poll = nopoll,
126a68f9396Sdholland 	.d_mmap = nommap,
127a68f9396Sdholland 	.d_kqfilter = nokqfilter,
128f9228f42Sdholland 	.d_discard = nodiscard,
129a68f9396Sdholland 	.d_flag = D_OTHER
130f5ede5dcShauke };
131f5ede5dcShauke 
132f5ede5dcShauke /* prototypes */
133f5ede5dcShauke static void cpi_lpreset(struct cpi_softc *);
134f5ede5dcShauke static int cpi_notready(struct cpi_softc *);
135f5ede5dcShauke static void cpi_wakeup(void *);
136f5ede5dcShauke static int cpi_flush(struct cpi_softc *);
137f5ede5dcShauke static void cpi_intr(void *);
138f5ede5dcShauke 
1392413e475Shauke static void cpi_tc_initclock(struct cpi_softc *);
1402413e475Shauke static uint cpi_get_timecount(struct timecounter *);
1412413e475Shauke static uint z8536_read_counter1(bus_space_tag_t, bus_space_handle_t);
1422413e475Shauke static uint z8536_read_counter2(bus_space_tag_t, bus_space_handle_t);
1432413e475Shauke static void z8536_reg_set(bus_space_tag_t, bus_space_handle_t,
144f5ede5dcShauke     uint8_t, uint8_t);
1452413e475Shauke static uint8_t z8536_reg_get(bus_space_tag_t, bus_space_handle_t,
146f5ede5dcShauke     uint8_t);
147f5ede5dcShauke 
148f5ede5dcShauke 
149f5ede5dcShauke const uint8_t cio_reset[] = {
150f5ede5dcShauke 	/* register	value */
151f5ede5dcShauke 	Z8536_MICR, 	0x00,
152f5ede5dcShauke 	Z8536_MICR, 	MICR_RESET,
153f5ede5dcShauke 	Z8536_MICR, 	0x00
154f5ede5dcShauke };
155f5ede5dcShauke 
156f5ede5dcShauke const uint8_t cio_init[] = {
157f5ede5dcShauke 	/* register	value */
158f5ede5dcShauke 
159f5ede5dcShauke 	/* Interrupt vectors - clear all */
160f5ede5dcShauke 	Z8536_IVRA, 	0x00,
161f5ede5dcShauke 	Z8536_IVRB, 	0x00,
162f5ede5dcShauke 	Z8536_IVRCT, 	0x20 /* ??? Do we use this? */,
163f5ede5dcShauke 
164f5ede5dcShauke 	/*
165f5ede5dcShauke 	 * Port A specification - bit port, single buffered,
166f5ede5dcShauke 	 * latched output, pulsed handshake, all bits non-inverting
167f5ede5dcShauke 	 * non-special I/O
168f5ede5dcShauke 	 */
169f5ede5dcShauke 	Z8536_PMSRA, 	PMSR_PTS_OUT | PMSR_LPM,
170f5ede5dcShauke 	Z8536_PHSRA, 	PHSR_HTS_PUL,
171f5ede5dcShauke 	Z8536_DPPRA, 	0x00,
172f5ede5dcShauke 	Z8536_DDRA, 	0x00,
173f5ede5dcShauke 	Z8536_SIOCRA, 	0x00,
174f5ede5dcShauke 
175f5ede5dcShauke 	/*
176f5ede5dcShauke 	 * Port B specification - bit port, transparent output,
177f5ede5dcShauke 	 * pulsed handshake, all bits non-inverting
178f5ede5dcShauke 	 * bits 0, 4 output; bits 1-3, 5-8 input,
179f5ede5dcShauke 	 * non-special I/O
180f5ede5dcShauke 	 * Pattern matching: Bit 6 (BUSY) matching "1"
181f5ede5dcShauke 	 * Alternatively: Bit 3 (/ACK) matching "0"
182f5ede5dcShauke 	 */
183f5ede5dcShauke 	Z8536_PMSRB, 	PMSR_PMS_OR_PEV,
184f5ede5dcShauke 	Z8536_PHSRB, 	0x00,
185f5ede5dcShauke 	Z8536_DPPRB, 	0x00,
186f5ede5dcShauke 	Z8536_DDRB, 	0xee /*11101110b*/,
187f5ede5dcShauke 	Z8536_SIOCRB, 	0x00,
188f5ede5dcShauke 	Z8536_PPRB, 	0x00,
189f5ede5dcShauke 	Z8536_PTRB, 	0x00,
190f5ede5dcShauke 	Z8536_PMRB, 	0x40 /*01000000b = PB6 */,
191f5ede5dcShauke 
192f5ede5dcShauke 	Z8536_PDRB, 	0xFE,	/* Assign printer -RESET */
193f5ede5dcShauke 	Z8536_PCSRA, 	0x00,	/* Clear port A interrupt bits */
194f5ede5dcShauke 
195f5ede5dcShauke 	/*
196f5ede5dcShauke 	 * Port C specification - bit 3 out, bits 0-2 in,
197f5ede5dcShauke 	 * all 4 non-inverting, non-special I/O
198f5ede5dcShauke 	 */
199f5ede5dcShauke 	Z8536_DDRC, 	0x07 /*00000111b*/,
200f5ede5dcShauke 	Z8536_DPPRC, 	0x00,
201f5ede5dcShauke 	Z8536_SIOCRC, 	0x00,
202f5ede5dcShauke 
203f5ede5dcShauke 	/*
204f5ede5dcShauke 	 * We need Timer 3 for running port A in strobed mode.
205f5ede5dcShauke 	 *
206f5ede5dcShauke 	 * Counter/Timer 3 specification -- clear IP & IUS, trigger +
207f5ede5dcShauke 	 * gate command bit, one-shot operation
208f5ede5dcShauke 	 */
209f5ede5dcShauke 	Z8536_CTCSR3, 	CTCS_CLR_IP_IUS | CTCS_GCB | CTCS_TCB,
210f5ede5dcShauke 	Z8536_CTMSR3, 	CTMS_DCS_ONESHOT,
211f5ede5dcShauke 	Z8536_CTTCR3_MSB, 0x00,
212f5ede5dcShauke 	Z8536_CTTCR3_LSB, 0x03,
213f5ede5dcShauke 
2142413e475Shauke 	/* Enable ports A+B+C+CT3 */
2152413e475Shauke 	Z8536_MCCR,	MCCR_PAE | MCCR_PBE | MCCR_PC_CT3E,
2162413e475Shauke 
217f5ede5dcShauke 	/* Master Interrupt Enable, Disable Lower Chain,
2182413e475Shauke 	 * No Interrupt Vector, port A+B+CT vectors include status */
219f5ede5dcShauke 	Z8536_MICR,  	MICR_MIE | MICR_DLC | MICR_NV | MICR_PAVIS |
220f5ede5dcShauke 	MICR_PBVIS | MICR_CTVIS,
221f5ede5dcShauke 	Z8536_PDRB, 	0xFE,	/* Clear printer -RESET */
222f5ede5dcShauke };
223f5ede5dcShauke 
2242413e475Shauke /* CPI default options */
2252413e475Shauke /* int	cpi_options = 0 | CPI_CTC12_IS_TIMECOUNTER; */
2262413e475Shauke 
227f5ede5dcShauke 
228f5ede5dcShauke /*
229f5ede5dcShauke  * Look for Creative Systems Inc. "Hurdler Centronics Parallel Interface"
230f5ede5dcShauke  */
231f5ede5dcShauke int
cpi_nubus_match(device_t parent,cfdata_t cf,void * aux)232f5ede5dcShauke cpi_nubus_match(device_t parent, cfdata_t cf, void *aux)
233f5ede5dcShauke {
234f5ede5dcShauke 	struct nubus_attach_args *na;
235f5ede5dcShauke 
236f5ede5dcShauke 	na = aux;
237f5ede5dcShauke 	if ((na->category == NUBUS_CATEGORY_COMMUNICATIONS) &&
238f5ede5dcShauke 	    (na->type == NUBUS_TYPE_CENTRONICS) &&
239f5ede5dcShauke 	    (na->drsw == NUBUS_DRSW_CPI) &&
240f5ede5dcShauke 	    (na->drhw == NUBUS_DRHW_CPI))
241f5ede5dcShauke 		return 1;
242f5ede5dcShauke 	else
243f5ede5dcShauke 		return 0;
244f5ede5dcShauke }
245f5ede5dcShauke 
246f5ede5dcShauke void
cpi_nubus_attach(device_t parent,device_t self,void * aux)247f5ede5dcShauke cpi_nubus_attach(device_t parent, device_t self, void *aux)
248f5ede5dcShauke {
249f5ede5dcShauke 	struct cpi_softc *sc;
250f5ede5dcShauke 	struct nubus_attach_args *na;
251f5ede5dcShauke 	int err, ii;
252f5ede5dcShauke 
253f5ede5dcShauke 	sc = device_private(self);
2542413e475Shauke 	sc->sc_options = (device_cfdata(self)->cf_flags & CPI_OPTIONS_MASK);
2552413e475Shauke 
256f5ede5dcShauke 	na = aux;
257f5ede5dcShauke 	sc->sc_bst = na->na_tag;
258f5ede5dcShauke 	memcpy(&sc->sc_slot, na->fmt, sizeof(nubus_slot));
259f5ede5dcShauke 	sc->sc_basepa = (bus_addr_t)NUBUS_SLOT2PA(na->slot);
260f5ede5dcShauke 
261f5ede5dcShauke 	/*
2622413e475Shauke 	 * The CIO sits eight bit wide on the top byte lane of
263f5ede5dcShauke 	 * Nubus, so map 16 byte.
264f5ede5dcShauke 	 */
265f5ede5dcShauke 	if (TRACE_CONFIG) {
266f5ede5dcShauke 		printf("\n");
267f5ede5dcShauke 		printf("\tcpi_nubus_attach() mapping 8536 CIO at 0x%lx.\n",
268f5ede5dcShauke 		    sc->sc_basepa + CIO_BASE_OFFSET);
269f5ede5dcShauke 	}
270f5ede5dcShauke 
271f5ede5dcShauke 	err = bus_space_map(sc->sc_bst, sc->sc_basepa + CIO_BASE_OFFSET,
272f5ede5dcShauke 	    (Z8536_IOSIZE << 4), 0, &sc->sc_bsh);
273f5ede5dcShauke 	if (err) {
274f5ede5dcShauke 		aprint_normal(": failed to map memory space.\n");
275f5ede5dcShauke 		return;
276f5ede5dcShauke 	}
277f5ede5dcShauke 
278f5ede5dcShauke 	sc->sc_lpstate = LP_INITIAL;
279f5ede5dcShauke 	sc->sc_intcount = 0;
280f5ede5dcShauke 	sc->sc_bytestoport = 0;
281f5ede5dcShauke 
282f5ede5dcShauke 	if (TRACE_CONFIG)
283f5ede5dcShauke 		printf("\tcpi_nubus_attach() about to set up 8536 CIO.\n");
284f5ede5dcShauke 
285f5ede5dcShauke 	for (ii = 0; ii < sizeof(cio_reset); ii += 2)
286f5ede5dcShauke 		z8536_reg_set(sc->sc_bst, sc->sc_bsh, cio_reset[ii],
287f5ede5dcShauke 		    cio_reset[ii + 1]);
288f5ede5dcShauke 
2892413e475Shauke 	delay(1000);		/* Give the CIO time to set itself up */
290f5ede5dcShauke 	for (ii = 0; ii < sizeof(cio_init); ii += 2) {
291f5ede5dcShauke 		z8536_reg_set(sc->sc_bst, sc->sc_bsh, cio_init[ii],
292f5ede5dcShauke 		    cio_init[ii + 1]);
293f5ede5dcShauke 	}
294f5ede5dcShauke 
295f5ede5dcShauke 	if (TRACE_CONFIG)
296f5ede5dcShauke 		printf("\tcpi_nubus_attach() done with 8536 CIO setup.\n");
297f5ede5dcShauke 
2982413e475Shauke 	/* XXX Get information strings from the card ROM */
299f5ede5dcShauke 	aprint_normal(": CSI Hurdler II Centronics\n");
300f5ede5dcShauke 
3012413e475Shauke 	/* Attach CIO timers 1+2 as timecounter */
3022413e475Shauke 	if (sc->sc_options & CPI_CTC12_IS_TIMECOUNTER) {
3032413e475Shauke 		cpi_tc_initclock(sc);
3042413e475Shauke 	}
305f5ede5dcShauke 
306f5ede5dcShauke 	callout_init(&sc->sc_wakeupchan, 0);	/* XXX */
307f5ede5dcShauke 
308f5ede5dcShauke 	/* make sure interrupts are vectored to us */
309f5ede5dcShauke 	add_nubus_intr(na->slot, cpi_nubus_intr, sc);
310f5ede5dcShauke }
311f5ede5dcShauke 
312f5ede5dcShauke void
cpi_nubus_intr(void * arg)313f5ede5dcShauke cpi_nubus_intr(void *arg)
314f5ede5dcShauke {
315f5ede5dcShauke         struct cpi_softc *sc;
316f5ede5dcShauke 	int s;
317f5ede5dcShauke 
318f5ede5dcShauke 	sc = (struct cpi_softc *)arg;
319f5ede5dcShauke 
320f5ede5dcShauke 	s = spltty();
321f5ede5dcShauke 
322f5ede5dcShauke 	sc->sc_intcount++;
323f5ede5dcShauke 
324f5ede5dcShauke 	/* Check for interrupt source, and clear interrupt */
325f5ede5dcShauke 
326f5ede5dcShauke 	/*
327f5ede5dcShauke 	 * Clear port A interrupt
328f5ede5dcShauke 	 * Interrupt from register A, clear "pending"
329f5ede5dcShauke 	 * and set "under service"
330f5ede5dcShauke 	 */
331f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IE);
332f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IP);
333f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_SET_IUS);
334f5ede5dcShauke 
335f5ede5dcShauke 	cpi_intr(sc);
336f5ede5dcShauke 
337f5ede5dcShauke 	/* Interrupt from register A, mark serviced */
338f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IUS);
339f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_SET_IE);
340f5ede5dcShauke 
341f5ede5dcShauke 	splx(s);
342f5ede5dcShauke }
343f5ede5dcShauke 
344f5ede5dcShauke 
345f5ede5dcShauke /* cpi nuts and bolts */
346f5ede5dcShauke 
347f5ede5dcShauke int
cpi_open(dev_t device,int flag,int mode,struct lwp * l)348f5ede5dcShauke cpi_open(dev_t device, int flag, int mode, struct lwp *l)
349f5ede5dcShauke {
350f5ede5dcShauke 	int err, ii, s;
351f5ede5dcShauke         struct cpi_softc *sc;
352f5ede5dcShauke 
353f5ede5dcShauke 	if (TRACE_OPEN)
354f5ede5dcShauke 		printf("\tcpi_open() called...\n");
355f5ede5dcShauke 
356f5ede5dcShauke 	/* Consistency checks: Valid unit number, softc, device state */
3573d8fe45fScegger 	sc = device_lookup_private(&cpi_cd, CPI_UNIT(device));
358f5ede5dcShauke 	if (NULL == sc) {
359f5ede5dcShauke 		if (TRACE_OPEN)
360f5ede5dcShauke 			printf("Tried to cpi_open() with NULL softc\n");
361f5ede5dcShauke 		return ENXIO;
362f5ede5dcShauke 	}
363f5ede5dcShauke 	if (sc->sc_lpstate != LP_INITIAL) {
364f5ede5dcShauke 		if (TRACE_OPEN)
365cbab9cadSchs 			printf("Not in initial state (%x).\n",
366cbab9cadSchs 			    sc->sc_lpstate);
367f5ede5dcShauke 		return EBUSY;
368f5ede5dcShauke 	}
369f5ede5dcShauke 	sc->sc_lpstate = LP_OPENING;
370f5ede5dcShauke 
371f5ede5dcShauke 	if (TRACE_OPEN)
372f5ede5dcShauke 		printf("\tcpi_open() resetting the printer...\n");
373f5ede5dcShauke 	cpi_lpreset(sc);
374f5ede5dcShauke 
375f5ede5dcShauke 	if (TRACE_OPEN)
376f5ede5dcShauke 		printf("\tcpi_open() waiting for printer ready...\n");
377f5ede5dcShauke 
378f5ede5dcShauke 	/* Wait max 15 sec for printer to get ready */
379f5ede5dcShauke 	for (ii = 15; cpi_notready(sc); ii--) {
380f5ede5dcShauke 		if (0 == ii) {
381f5ede5dcShauke 			sc->sc_lpstate = LP_INITIAL;
382f5ede5dcShauke 			return EBUSY;
383f5ede5dcShauke 		}
384f5ede5dcShauke 		/* sleep for a second, unless we get a signal */
3856a0660a9Srmind 		err = tsleep(sc, PZERO | PCATCH, "cpi_open", hz);
386f5ede5dcShauke 		if (err != EWOULDBLOCK) {
387f5ede5dcShauke 			sc->sc_lpstate = LP_INITIAL;
388f5ede5dcShauke 			return err;
389f5ede5dcShauke 		}
390f5ede5dcShauke 	}
391f5ede5dcShauke 	if (TRACE_OPEN)
392f5ede5dcShauke 		printf("\tcpi_open() allocating printer buffer...\n");
393f5ede5dcShauke 
394f5ede5dcShauke 	/* Allocate the driver's line buffer */
395*c2d090faSthorpej 	sc->sc_printbuf = kmem_alloc(CPI_BUFSIZE, KM_SLEEP);
396f5ede5dcShauke 	sc->sc_bufbytes = 0;
397f5ede5dcShauke 	sc->sc_lpstate = LP_OPEN;
398f5ede5dcShauke 
399f5ede5dcShauke 	/* Statistics */
400f5ede5dcShauke 	sc->sc_intcount = 0;
401f5ede5dcShauke 	sc->sc_bytestoport = 0;
402f5ede5dcShauke 
403f5ede5dcShauke 	/* Kick off transfer */
404f5ede5dcShauke 	cpi_wakeup(sc);
405f5ede5dcShauke 
406f5ede5dcShauke 	/*
407f5ede5dcShauke 	 * Reset "interrupt {pending, under service}" bits, then
408f5ede5dcShauke 	 * enable Port A interrupts
409f5ede5dcShauke 	 */
410f5ede5dcShauke 	s = spltty();
411f5ede5dcShauke 
412f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IP_IUS);
413f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_SET_IE);
414f5ede5dcShauke 	splx(s);
415f5ede5dcShauke 
416f5ede5dcShauke 	if (TRACE_OPEN)
417f5ede5dcShauke 		printf("\tcpi_open() done...\n");
418f5ede5dcShauke 
419f5ede5dcShauke 	return 0;
420f5ede5dcShauke }
421f5ede5dcShauke 
422f5ede5dcShauke int
cpi_close(dev_t device,int flag,int mode,struct lwp * l)423f5ede5dcShauke cpi_close(dev_t device, int flag, int mode, struct lwp *l)
424f5ede5dcShauke {
425f5ede5dcShauke         struct cpi_softc *sc;
426f5ede5dcShauke 
4273d8fe45fScegger 	sc = device_lookup_private(&cpi_cd, CPI_UNIT(device));
428f5ede5dcShauke 
429f5ede5dcShauke 	if (TRACE_CLOSE)
430f5ede5dcShauke 		printf("\tcpi_close() called (%lu hard, %lu bytes to port)\n",
431f5ede5dcShauke 		    sc->sc_intcount, sc->sc_bytestoport);
432f5ede5dcShauke 
433f5ede5dcShauke 	/* Flush the remaining buffer content, ignoring any errors */
434f5ede5dcShauke 	if (0 < sc->sc_bufbytes)
435f5ede5dcShauke 		(void)cpi_flush(sc);
436f5ede5dcShauke 
437f5ede5dcShauke 	callout_stop(&sc->sc_wakeupchan);
438f5ede5dcShauke 
439f5ede5dcShauke 	/* Disable Port A interrupts */
440f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IE);
441f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IP_IUS);
442f5ede5dcShauke 
443f5ede5dcShauke 	sc->sc_lpstate = LP_INITIAL;
444*c2d090faSthorpej 	kmem_free(sc->sc_printbuf, CPI_BUFSIZE);
445f5ede5dcShauke 
446f5ede5dcShauke 	return 0;
447f5ede5dcShauke }
448f5ede5dcShauke 
449f5ede5dcShauke int
cpi_write(dev_t device,struct uio * uio,int flags)450f5ede5dcShauke cpi_write(dev_t device, struct uio *uio, int flags)
451f5ede5dcShauke {
452f5ede5dcShauke 	int err;
453f5ede5dcShauke 	size_t numbytes;
454f5ede5dcShauke         struct cpi_softc *sc;
455f5ede5dcShauke 
456f5ede5dcShauke 	err = 0;
457f5ede5dcShauke 
458f5ede5dcShauke 	if (TRACE_WRITE)
459f5ede5dcShauke 		printf("\tcpi_write() called for %u bytes\n", uio->uio_resid);
460f5ede5dcShauke 
4613d8fe45fScegger 	sc = device_lookup_private(&cpi_cd, CPI_UNIT(device));
462f5ede5dcShauke 
463f5ede5dcShauke 	/* Send data to printer, a line buffer full at a time */
464f5ede5dcShauke 	while (uio->uio_resid > 0) {
465d1579b2dSriastradh 		numbytes = uimin(CPI_BUFSIZE, uio->uio_resid);
466f5ede5dcShauke 		sc->sc_cp = sc->sc_printbuf;
467f5ede5dcShauke 		uiomove(sc->sc_cp, numbytes, uio);
468f5ede5dcShauke 		sc->sc_bufbytes = numbytes;
469f5ede5dcShauke 
470f5ede5dcShauke 		if (TRACE_WRITE)
471f5ede5dcShauke 			printf("\tQueuing %u bytes\n", numbytes);
472f5ede5dcShauke 		err = cpi_flush(sc);
473f5ede5dcShauke 		if (err) {
474f5ede5dcShauke 			/* Failure; adjust residual counter */
475f5ede5dcShauke 			if (TRACE_WRITE)
476f5ede5dcShauke 				printf("\tQueuing failed with %d\n", err);
477f5ede5dcShauke 			uio->uio_resid += sc->sc_bufbytes;
478f5ede5dcShauke 			sc->sc_bufbytes = 0;
479f5ede5dcShauke 			break;
480f5ede5dcShauke 		}
481f5ede5dcShauke 	}
482f5ede5dcShauke 	return err;
483f5ede5dcShauke }
484f5ede5dcShauke 
485f5ede5dcShauke int
cpi_ioctl(dev_t device,unsigned long cmd,void * data,int flag,struct lwp * l)486f5ede5dcShauke cpi_ioctl(dev_t device, unsigned long cmd, void *data,
487f5ede5dcShauke     int flag, struct lwp *l)
488f5ede5dcShauke {
489f5ede5dcShauke 	int err;
490f5ede5dcShauke 
491f5ede5dcShauke 	err = 0;
492f5ede5dcShauke 
493f5ede5dcShauke 	if (TRACE_IOCTL)
494f5ede5dcShauke 		printf("\tcpi_ioctl() called with %ld...\n", cmd);
495f5ede5dcShauke 
496f5ede5dcShauke 	switch (cmd) {
497f5ede5dcShauke 	default:
498f5ede5dcShauke 		if (TRACE_IOCTL)
499f5ede5dcShauke 			printf("\tcpi_ioctl() unknown ioctl %ld\n", cmd);
500f5ede5dcShauke 		err = ENODEV;
501f5ede5dcShauke 		break;
502f5ede5dcShauke 	}
503f5ede5dcShauke 	return err;
504f5ede5dcShauke }
505f5ede5dcShauke 
506f5ede5dcShauke /*
507f5ede5dcShauke  * Flush the print buffer that our top half uses to provide data to
508f5ede5dcShauke  * our bottom, interrupt-driven half.
509f5ede5dcShauke  */
510f5ede5dcShauke static int
cpi_flush(struct cpi_softc * sc)511f5ede5dcShauke cpi_flush(struct cpi_softc *sc)
512f5ede5dcShauke {
513f5ede5dcShauke 	int err, s;
514f5ede5dcShauke 
515f5ede5dcShauke 	err = 0;
516f5ede5dcShauke 	while (0 < sc->sc_bufbytes) {
517f5ede5dcShauke 		/* Feed the printer a char, if it's ready */
518f5ede5dcShauke 		if ( !cpi_notready(sc)) {
519f5ede5dcShauke 			if (TRACE_WRITE)
520f5ede5dcShauke 				printf("\tcpi_flush() writes %u bytes "
521f5ede5dcShauke 				    "(%lu hard, %lu bytes to port)\n",
522f5ede5dcShauke 				    sc->sc_bufbytes, sc->sc_intcount,
523f5ede5dcShauke 				    sc->sc_bytestoport);
524f5ede5dcShauke 			s = spltty();
525f5ede5dcShauke 			cpi_intr(sc);
526f5ede5dcShauke 			splx(s);
527f5ede5dcShauke 		}
528f5ede5dcShauke 		/* XXX Sure we want to wait forever for the printer? */
5296a0660a9Srmind 		err = tsleep((void *)sc, PZERO | PCATCH,
5306a0660a9Srmind 		    "cpi_flush", (60 * hz));
531f5ede5dcShauke 	}
532f5ede5dcShauke 	return err;
533f5ede5dcShauke }
534f5ede5dcShauke 
535f5ede5dcShauke 
536f5ede5dcShauke static void
cpi_wakeup(void * param)537f5ede5dcShauke cpi_wakeup(void *param)
538f5ede5dcShauke {
539f5ede5dcShauke 	struct cpi_softc *sc;
540f5ede5dcShauke 	int s;
541f5ede5dcShauke 
542f5ede5dcShauke 	sc = param;
543f5ede5dcShauke 
544f5ede5dcShauke 	s = spltty();
545f5ede5dcShauke 	cpi_intr(sc);
546f5ede5dcShauke 	splx(s);
547f5ede5dcShauke 
548f5ede5dcShauke 	callout_reset(&sc->sc_wakeupchan, hz, cpi_wakeup, sc);
549f5ede5dcShauke }
550f5ede5dcShauke 
551f5ede5dcShauke 
552f5ede5dcShauke static void
cpi_lpreset(struct cpi_softc * sc)553f5ede5dcShauke cpi_lpreset(struct cpi_softc *sc)
554f5ede5dcShauke {
555f5ede5dcShauke 	uint8_t portb;		/* Centronics -RESET is on port B, bit 0 */
556f5ede5dcShauke 
557f5ede5dcShauke 	portb = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_PDRB);
558f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PDRB, portb & ~CPI_RESET);
559f5ede5dcShauke 	delay(100);
560f5ede5dcShauke 	portb = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_PDRB);
561f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PDRB, portb | CPI_RESET);
562f5ede5dcShauke }
563f5ede5dcShauke 
564f5ede5dcShauke 
565f5ede5dcShauke /*
566f5ede5dcShauke  * Centronics BUSY 		is on port B, bit 6
567f5ede5dcShauke  *	      SELECT		is on Port B, bit 5
568f5ede5dcShauke  *	      /FAULT		is on Port B, bit 1
569f5ede5dcShauke  *            PAPER EMPTY	is on Port C, bit 1
570f5ede5dcShauke  */
571f5ede5dcShauke static int
cpi_notready(struct cpi_softc * sc)572f5ede5dcShauke cpi_notready(struct cpi_softc *sc)
573f5ede5dcShauke {
574f5ede5dcShauke 	uint8_t portb, portc;
575f5ede5dcShauke 	int is_busy, is_select, is_fault, is_paper_empty;
576f5ede5dcShauke 
577f5ede5dcShauke 	if (TRACE_STATUS)
578f5ede5dcShauke 		printf("\tcpi_notready() checking printer status...\n");
579f5ede5dcShauke 
580f5ede5dcShauke 	portb = bus_space_read_1(sc->sc_bst, sc->sc_bsh, CIO_PORTB);
581f5ede5dcShauke 	if (TRACE_STATUS)
582f5ede5dcShauke 		printf("\tPort B has 0x0%X\n", portb);
583f5ede5dcShauke 
584f5ede5dcShauke 	is_busy = CPI_BUSY & portb;
585f5ede5dcShauke 	if (TRACE_STATUS)
586f5ede5dcShauke 		printf("\t\tBUSY = %d\n", is_busy);
587f5ede5dcShauke 
588f5ede5dcShauke 	is_select = CPI_SELECT & portb;
589f5ede5dcShauke 	if (TRACE_STATUS)
590f5ede5dcShauke 		printf("\t\tSELECT = %d\n", is_select);
591f5ede5dcShauke 
592f5ede5dcShauke 	is_fault = CPI_FAULT & portb;
593f5ede5dcShauke 	if (TRACE_STATUS)
594f5ede5dcShauke 		printf("\t\t/FAULT = %d\n", is_fault);
595f5ede5dcShauke 
596f5ede5dcShauke 	portc = bus_space_read_1(sc->sc_bst, sc->sc_bsh, CIO_PORTC);
597f5ede5dcShauke 	if (TRACE_STATUS)
598f5ede5dcShauke 		printf("\tPort C has 0x0%X\n", portc);
599f5ede5dcShauke 
600f5ede5dcShauke 	is_paper_empty = CPI_PAPER_EMPTY & portc;
601f5ede5dcShauke 	if (TRACE_STATUS)
602f5ede5dcShauke 		printf("\t\tPAPER EMPTY = %d\n", is_paper_empty);
603f5ede5dcShauke 
604f5ede5dcShauke 	return (is_busy || !is_select || !is_fault || is_paper_empty);
605f5ede5dcShauke }
606f5ede5dcShauke 
607f5ede5dcShauke static void
cpi_intr(void * arg)608f5ede5dcShauke cpi_intr(void *arg)
609f5ede5dcShauke {
610f5ede5dcShauke 	struct cpi_softc *sc;
611f5ede5dcShauke 
612f5ede5dcShauke 	sc = arg;
613f5ede5dcShauke 
614f5ede5dcShauke 	/* Printer ready for output? */
615f5ede5dcShauke 	if (cpi_notready(sc))
616f5ede5dcShauke 		return;
617f5ede5dcShauke 
618f5ede5dcShauke 	if (0 && TRACE_WRITE)
619f5ede5dcShauke 		printf("\tcpi_soft_intr() has %u bytes.\n", sc->sc_bufbytes);
620f5ede5dcShauke 
621f5ede5dcShauke 	/* Anything to print? */
622f5ede5dcShauke 	if (sc->sc_bufbytes) {
623f5ede5dcShauke 		/* Data byte */
624f5ede5dcShauke 		bus_space_write_1(sc->sc_bst, sc->sc_bsh,
625f5ede5dcShauke 		    CIO_PORTA, *sc->sc_cp++);
626f5ede5dcShauke 		sc->sc_bufbytes--;
627f5ede5dcShauke 		sc->sc_bytestoport++;
628f5ede5dcShauke 	}
629f5ede5dcShauke 	if (0 == sc->sc_bufbytes)
630f5ede5dcShauke 		/* line buffer empty, wake up our top half */
631f5ede5dcShauke 		wakeup((void *)sc);
632f5ede5dcShauke }
633f5ede5dcShauke 
634f5ede5dcShauke static void
cpi_tc_initclock(struct cpi_softc * sc)6352413e475Shauke cpi_tc_initclock(struct cpi_softc *sc)
636f5ede5dcShauke {
6372413e475Shauke 	uint8_t reg;
638f5ede5dcShauke 
639f5ede5dcShauke 	/*
6402413e475Shauke 	 * Set up c/t 1 and 2 as a single, free-running 32 bit counter
641f5ede5dcShauke 	 */
642f5ede5dcShauke 
6432413e475Shauke 	/* Disable counters 1 and 2 */
644f5ede5dcShauke 	reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_MCCR);
645f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_MCCR,
6469d61fcf1Schristos 	    reg & ~(MCCR_CT1E | MCCR_CT2E));
647f5ede5dcShauke 
648f5ede5dcShauke 	/* Make sure interrupt enable bits are cleared */
6492413e475Shauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1, CTCS_CLR_IE);
6502413e475Shauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR2, CTCS_CLR_IE);
651f5ede5dcShauke 
652f5ede5dcShauke 	/* Initialise counter start values, and set to continuous cycle */
6532413e475Shauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTMSR1,
6542413e475Shauke 	    CTMS_CSC | CTMS_DCS_PULSE);
655f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR1_MSB, 0x00);
656f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR1_LSB, 0x00);
657f5ede5dcShauke 
6582413e475Shauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTMSR2,
6592413e475Shauke 	    CTMS_CSC | CTMS_DCS_PULSE);
660f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR2_MSB, 0x00);
661f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR2_LSB, 0x00);
662f5ede5dcShauke 
6632413e475Shauke 	/* Link counters 1 and 2 */
6642413e475Shauke 	reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_MCCR);
6652413e475Shauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_MCCR, reg | MCCR_CT1CT2);
6662413e475Shauke 
6672413e475Shauke 	/* Enable and counter pair */
668f5ede5dcShauke 	reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_MCCR);
669f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_MCCR,
6702413e475Shauke 	    reg | (MCCR_CT1E | MCCR_CT2E));
671f5ede5dcShauke 
6722413e475Shauke 	/* Start c/t 1; c/t 2 gets started by c/t 1 pulse */
673f5ede5dcShauke 	reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1);
674f5ede5dcShauke 	z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1,
6752413e475Shauke 	    CTCSR_MASK(reg | CTCS_TCB | CTCS_GCB));
676f5ede5dcShauke 
6772413e475Shauke 	if (TRACE_TCNTR) {
6782413e475Shauke 		printf("Before tc_init():\n");
6792413e475Shauke 		reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1);
6802413e475Shauke 		printf("Counter 1 CTCSR setup bits are 0x%03x\n", reg);
6812413e475Shauke 		printf("Counter 1 (LSW) is now 0x%05x\n",
6822413e475Shauke 		    z8536_read_counter1(sc->sc_bst, sc->sc_bsh));
6832413e475Shauke 		reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR2);
6842413e475Shauke 		printf("Counter 2 CTCSR setup bits are 0x%03x\n", reg);
6852413e475Shauke 		printf("Counter 2 (MSW) is now 0x%05x\n",
6862413e475Shauke 		    z8536_read_counter2(sc->sc_bst, sc->sc_bsh));
6872413e475Shauke 
6882413e475Shauke 		delay(1000);
6892413e475Shauke 	}
6902413e475Shauke 
6912413e475Shauke 	sc->sc_timecounter.tc_get_timecount = cpi_get_timecount;
6922413e475Shauke 	sc->sc_timecounter.tc_poll_pps      = 0;
6932413e475Shauke 	sc->sc_timecounter.tc_counter_mask  = ~0u;
6942413e475Shauke 	sc->sc_timecounter.tc_frequency     = CPI_CLK_FREQ;
6952413e475Shauke 	sc->sc_timecounter.tc_name          = "Nubus CPI";
6962413e475Shauke 	sc->sc_timecounter.tc_quality       = 1000;
6972413e475Shauke 	/*
6982413e475Shauke 	 * Squirrel away the device's sc so we can talk
6992413e475Shauke 	 * to the CIO later
7002413e475Shauke 	 */
7012413e475Shauke 	sc->sc_timecounter.tc_priv          = sc;
7022413e475Shauke 	sc->sc_timecounter.tc_next          = NULL;
7032413e475Shauke 
7042413e475Shauke         tc_init(&(sc->sc_timecounter));
7052413e475Shauke 
7062413e475Shauke 	if (TRACE_TCNTR) {
7072413e475Shauke 		delay(1000);
7082413e475Shauke 
7092413e475Shauke 		printf("After tc_init():\n");
7102413e475Shauke 		reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1);
7112413e475Shauke 		printf("Counter 1 CTCSR setup bits are 0x%03x\n", reg);
7122413e475Shauke 		printf("Counter 1 (LSW) is now 0x%05x\n",
7132413e475Shauke 		    z8536_read_counter1(sc->sc_bst, sc->sc_bsh));
7142413e475Shauke 		reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR2);
7152413e475Shauke 		printf("Counter 2 CTCSR setup bits are 0x%03x\n", reg);
7162413e475Shauke 		printf("Counter 2 (MSW) is now 0x%05x\n",
7172413e475Shauke 		    z8536_read_counter2(sc->sc_bst, sc->sc_bsh));
7182413e475Shauke 	}
719f5ede5dcShauke }
720f5ede5dcShauke 
721f5ede5dcShauke static u_int
cpi_get_timecount(struct timecounter * tc)722f5ede5dcShauke cpi_get_timecount(struct timecounter *tc)
723f5ede5dcShauke {
724f5ede5dcShauke         int s;
7252413e475Shauke 	uint msw, msw2, lsw;
7262413e475Shauke 	uint8_t reg;
7272413e475Shauke 	bus_space_tag_t bst;
7282413e475Shauke 	bus_space_handle_t bsh;
729f5ede5dcShauke 
7302413e475Shauke 	bst = ((struct cpi_softc *)tc->tc_priv)->sc_bst;
7312413e475Shauke 	bsh = ((struct cpi_softc *)tc->tc_priv)->sc_bsh;
732f5ede5dcShauke 	/*
7332413e475Shauke 	 * We run CIO counters 1 and 2 in an internally coupled mode,
7342413e475Shauke 	 * where the output of counter 1 (LSW) clocks counter 2 (MSW).
7352413e475Shauke 	 * The counters are buffered, and the buffers have to be
7362413e475Shauke 	 * locked before we can read out a consistent counter
7372413e475Shauke 	 * value. Reading the LSB releases the buffer lock.
738f5ede5dcShauke 	 *
7392413e475Shauke 	 * Unfortunately, there is no such mechanism between MSW and
7402413e475Shauke 	 * LSW of the coupled counter. To ensure a consistent
7412413e475Shauke 	 * read-out, we read the MSW, then the LSW, then re-read the
7422413e475Shauke 	 * MSW and compare with the old value. If we find that the MSW
7432413e475Shauke 	 * has just been incremented, we re-read the LSW. This avoids
7442413e475Shauke 	 * a race that could leave us with a new (just wrapped) LSW
7452413e475Shauke 	 * and an old MSW value.
7462413e475Shauke 	 *
7472413e475Shauke 	 * For simplicity, we roll the procedure into a loop - the
7482413e475Shauke 	 * rollover case is rare.
749f5ede5dcShauke 	 */
7502413e475Shauke 	do {
7512413e475Shauke 
7522413e475Shauke #define delay(a)
7532413e475Shauke 
7542413e475Shauke 		/* Guard HW timer access */
755f5ede5dcShauke 		s = splhigh();
756f5ede5dcShauke 
7572413e475Shauke 		/* Lock counter 2 latch in preparation for read-out */
7582413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR2);
7592413e475Shauke 		delay(1);
7602413e475Shauke 		reg = bus_space_read_1(bst, bsh, CIO_CTRL);
7612413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR2);
7622413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, CTCSR_MASK(reg | CTCS_RCC));
763f5ede5dcShauke 
7642413e475Shauke 		/* Read out counter 2 MSB,then LSB (releasing the latch) */
7652413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR2_MSB);
7662413e475Shauke 		delay(1);
7672413e475Shauke 		msw = bus_space_read_1(bst, bsh, CIO_CTRL) << 8;
7682413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR2_LSB);
7692413e475Shauke 		delay(1);
7702413e475Shauke 		msw |= bus_space_read_1(bst, bsh, CIO_CTRL);
7712413e475Shauke 
7722413e475Shauke 		/* Lock counter 1 latch in preparation for read-out */
7732413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR1);
7742413e475Shauke 		delay(1);
7752413e475Shauke 		reg = bus_space_read_1(bst, bsh, CIO_CTRL);
7762413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR1);
7772413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, CTCSR_MASK(reg |CTCS_RCC));
7782413e475Shauke 
7792413e475Shauke 		/* Read out counter 1 MSB,then LSB (releasing the latch) */
7802413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR1_MSB);
7812413e475Shauke 		delay(1);
7822413e475Shauke 		lsw = bus_space_read_1(bst, bsh, CIO_CTRL) << 8;
7832413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR1_LSB);
7842413e475Shauke 		delay(1);
7852413e475Shauke 		lsw |= bus_space_read_1(bst, bsh, CIO_CTRL);
7862413e475Shauke 
7872413e475Shauke 		/* Lock counter 2 latch in preparation for read-out */
7882413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR2);
7892413e475Shauke 		delay(1);
7902413e475Shauke 		reg = bus_space_read_1(bst, bsh, CIO_CTRL);
7912413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR2);
7922413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, CTCSR_MASK(reg | CTCS_RCC));
7932413e475Shauke 
7942413e475Shauke 		/* Read out counter 2 MSB,then LSB (releasing the latch) */
7952413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR2_MSB);
7962413e475Shauke 		delay(1);
7972413e475Shauke 		msw2 = bus_space_read_1(bst, bsh, CIO_CTRL) << 8;
7982413e475Shauke 		bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR2_LSB);
7992413e475Shauke 		delay(1);
8002413e475Shauke 		msw2 |= bus_space_read_1(bst, bsh, CIO_CTRL);
8012413e475Shauke 
8022413e475Shauke 		splx(s);
8032413e475Shauke 
8042413e475Shauke 	} while (msw2 != msw);
8052413e475Shauke 
8062413e475Shauke 	/* timecounter expects an upward counter */
8072413e475Shauke 	return ~0u - ((msw << 16) | lsw);
808f5ede5dcShauke }
809f5ede5dcShauke 
810f5ede5dcShauke /*
8112413e475Shauke  * Z8536 CIO convenience atomic register getter/setter
812f5ede5dcShauke  */
813f5ede5dcShauke 
8142413e475Shauke static uint
z8536_read_counter1(bus_space_tag_t bst,bus_space_handle_t bsh)8152413e475Shauke z8536_read_counter1(bus_space_tag_t bst, bus_space_handle_t bsh)
8162413e475Shauke {
8172413e475Shauke 	uint8_t reg;
8182413e475Shauke 	uint32_t lsw;
8192413e475Shauke 	int s;
8202413e475Shauke 
8212413e475Shauke 	s = splhigh();
8222413e475Shauke 
8232413e475Shauke 	/* Lock counter 1 latch in preparation for read-out */
8242413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR1);
8252413e475Shauke 	delay(1);
8262413e475Shauke 	reg = bus_space_read_1(bst, bsh, CIO_CTRL);
8272413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR1);
8282413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, CTCSR_MASK(reg | CTCS_RCC));
8292413e475Shauke 
8302413e475Shauke 	/* Read out counter 1 MSB,then LSB (releasing the latch) */
8312413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR1_MSB);
8322413e475Shauke 	delay(1);
8332413e475Shauke 	lsw = bus_space_read_1(bst, bsh, CIO_CTRL) << 8;
8342413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR1_LSB);
8352413e475Shauke 	delay(1);
8362413e475Shauke 	lsw |= bus_space_read_1(bst, bsh, CIO_CTRL);
8372413e475Shauke 
8382413e475Shauke 	splx(s);
8392413e475Shauke 
8402413e475Shauke 	return lsw;
8412413e475Shauke }
8422413e475Shauke 
8432413e475Shauke static uint
z8536_read_counter2(bus_space_tag_t bst,bus_space_handle_t bsh)8442413e475Shauke z8536_read_counter2(bus_space_tag_t bst, bus_space_handle_t bsh)
8452413e475Shauke {
8462413e475Shauke 	uint8_t reg;
8472413e475Shauke 	uint32_t msw;
8482413e475Shauke 	int s;
8492413e475Shauke 
8502413e475Shauke 	s = splhigh();
8512413e475Shauke 
8522413e475Shauke 	/* Lock counter 2 latch in preparation for read-out */
8532413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR2);
8542413e475Shauke 	delay(1);
8552413e475Shauke 	reg = bus_space_read_1(bst, bsh, CIO_CTRL);
8562413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCSR2);
8572413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, CTCSR_MASK(reg | CTCS_RCC));
8582413e475Shauke 
8592413e475Shauke 	/* Read out counter 2 MSB,then LSB (releasing the latch) */
8602413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR2_MSB);
8612413e475Shauke 	delay(1);
8622413e475Shauke 	msw = bus_space_read_1(bst, bsh, CIO_CTRL) << 8;
8632413e475Shauke 	bus_space_write_1(bst, bsh, CIO_CTRL, Z8536_CTCCR2_LSB);
8642413e475Shauke 	delay(1);
8652413e475Shauke 	msw |= bus_space_read_1(bst, bsh, CIO_CTRL);
8662413e475Shauke 
8672413e475Shauke 	splx(s);
8682413e475Shauke 
8692413e475Shauke 	return msw;
8702413e475Shauke }
8712413e475Shauke 
8722413e475Shauke static void
z8536_reg_set(bus_space_tag_t bspace,bus_space_handle_t bhandle,uint8_t reg,uint8_t val)873f5ede5dcShauke z8536_reg_set(bus_space_tag_t bspace, bus_space_handle_t bhandle,
874f5ede5dcShauke     uint8_t reg, uint8_t val)
875f5ede5dcShauke {
876f5ede5dcShauke 	int s;
877f5ede5dcShauke 
878f5ede5dcShauke 	s = splhigh();
879f5ede5dcShauke 	bus_space_write_1(bspace, bhandle, CIO_CTRL, reg);
880f5ede5dcShauke 	delay(1);
881f5ede5dcShauke 	bus_space_write_1(bspace, bhandle, CIO_CTRL, val);
882f5ede5dcShauke 	splx(s);
883f5ede5dcShauke }
884f5ede5dcShauke 
8852413e475Shauke static uint8_t
z8536_reg_get(bus_space_tag_t bspace,bus_space_handle_t bhandle,uint8_t reg)886f5ede5dcShauke z8536_reg_get(bus_space_tag_t bspace, bus_space_handle_t bhandle, uint8_t reg)
887f5ede5dcShauke {
888f5ede5dcShauke 	int s;
889f5ede5dcShauke 	uint8_t val;
890f5ede5dcShauke 
891f5ede5dcShauke 	s = splhigh();
892f5ede5dcShauke 	bus_space_write_1(bspace, bhandle, CIO_CTRL, reg);
893f5ede5dcShauke 	delay(1);
894f5ede5dcShauke 	val = bus_space_read_1(bspace, bhandle, CIO_CTRL);
895f5ede5dcShauke 	splx(s);
896f5ede5dcShauke 
897f5ede5dcShauke 	return val;
898f5ede5dcShauke }
899