xref: /netbsd-src/sys/arch/luna68k/dev/xpbus.c (revision 8b8575d933fe91428231bb68e1efabe962f2b668)
1*8b8575d9Stsutsui /*	$NetBSD: xpbus.c,v 1.1 2022/06/10 21:42:23 tsutsui Exp $	*/
2*8b8575d9Stsutsui 
3*8b8575d9Stsutsui /*-
4*8b8575d9Stsutsui  * Copyright (c) 2016 Izumi Tsutsui.  All rights reserved.
5*8b8575d9Stsutsui  *
6*8b8575d9Stsutsui  * Redistribution and use in source and binary forms, with or without
7*8b8575d9Stsutsui  * modification, are permitted provided that the following conditions
8*8b8575d9Stsutsui  * are met:
9*8b8575d9Stsutsui  * 1. Redistributions of source code must retain the above copyright
10*8b8575d9Stsutsui  *    notice, this list of conditions and the following disclaimer.
11*8b8575d9Stsutsui  * 2. Redistributions in binary form must reproduce the above copyright
12*8b8575d9Stsutsui  *    notice, this list of conditions and the following disclaimer in the
13*8b8575d9Stsutsui  *    documentation and/or other materials provided with the distribution.
14*8b8575d9Stsutsui  *
15*8b8575d9Stsutsui  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16*8b8575d9Stsutsui  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17*8b8575d9Stsutsui  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18*8b8575d9Stsutsui  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19*8b8575d9Stsutsui  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20*8b8575d9Stsutsui  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21*8b8575d9Stsutsui  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22*8b8575d9Stsutsui  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23*8b8575d9Stsutsui  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24*8b8575d9Stsutsui  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*8b8575d9Stsutsui  */
26*8b8575d9Stsutsui 
27*8b8575d9Stsutsui /*
28*8b8575d9Stsutsui  * LUNA's Hitachi HD647180 "XP" I/O processor
29*8b8575d9Stsutsui  */
30*8b8575d9Stsutsui 
31*8b8575d9Stsutsui /*
32*8b8575d9Stsutsui  * Specification of interrupts from XP to the host is confirmed
33*8b8575d9Stsutsui  * by Kenji Aoyama, in xptty(4) driver for OpenBSD/luna88k:
34*8b8575d9Stsutsui  *  https://gist.github.com/ao-kenji/790b0822e46a50ea63131cfa8d9110e7
35*8b8575d9Stsutsui  * and CP/M BIOS for HD647180 on LUNA:
36*8b8575d9Stsutsui  *  https://gist.github.com/ao-kenji/4f1e2b010f3b2b41ab07f3a8a3cc7484
37*8b8575d9Stsutsui  */
38*8b8575d9Stsutsui 
39*8b8575d9Stsutsui #include <sys/cdefs.h>
40*8b8575d9Stsutsui __KERNEL_RCSID(0, "$NetBSD: xpbus.c,v 1.1 2022/06/10 21:42:23 tsutsui Exp $");
41*8b8575d9Stsutsui 
42*8b8575d9Stsutsui #include <sys/param.h>
43*8b8575d9Stsutsui #include <sys/systm.h>
44*8b8575d9Stsutsui #include <sys/conf.h>
45*8b8575d9Stsutsui #include <sys/atomic.h>
46*8b8575d9Stsutsui #include <sys/device.h>
47*8b8575d9Stsutsui 
48*8b8575d9Stsutsui #include <machine/autoconf.h>
49*8b8575d9Stsutsui #include <machine/board.h>
50*8b8575d9Stsutsui 
51*8b8575d9Stsutsui #include <luna68k/dev/xpbusvar.h>
52*8b8575d9Stsutsui #include <luna68k/dev/xplxfirm.h>
53*8b8575d9Stsutsui 
54*8b8575d9Stsutsui /*
55*8b8575d9Stsutsui  * PIO 0 port C is connected to XP's reset line
56*8b8575d9Stsutsui  *
57*8b8575d9Stsutsui  * XXX: PIO port functions should be shared with machdep.c for DIP SWs
58*8b8575d9Stsutsui  */
59*8b8575d9Stsutsui #define PIO_ADDR	OBIO_PIO0_BASE
60*8b8575d9Stsutsui #define PORT_A		0
61*8b8575d9Stsutsui #define PORT_B		1
62*8b8575d9Stsutsui #define PORT_C		2
63*8b8575d9Stsutsui #define CTRL		3
64*8b8575d9Stsutsui 
65*8b8575d9Stsutsui /* PIO0 Port C bit definition */
66*8b8575d9Stsutsui #define XP_INT1_REQ	0	/* INTR B */
67*8b8575d9Stsutsui 	/* unused */		/* IBF B */
68*8b8575d9Stsutsui #define XP_INT1_ENA	2	/* INTE B */
69*8b8575d9Stsutsui #define XP_INT5_REQ	3	/* INTR A */
70*8b8575d9Stsutsui #define XP_INT5_ENA	4	/* INTE A */
71*8b8575d9Stsutsui 	/* unused */		/* IBF A */
72*8b8575d9Stsutsui #define PARITY		6	/* PC6 output to enable parity error */
73*8b8575d9Stsutsui #define XP_RESET	7	/* PC7 output to reset HD647180 XP */
74*8b8575d9Stsutsui 
75*8b8575d9Stsutsui /* Port control for PC6 and PC7 */
76*8b8575d9Stsutsui #define ON		1
77*8b8575d9Stsutsui #define OFF		0
78*8b8575d9Stsutsui 
79*8b8575d9Stsutsui 
80*8b8575d9Stsutsui struct xpbus_softc {
81*8b8575d9Stsutsui 	device_t	sc_dev;
82*8b8575d9Stsutsui };
83*8b8575d9Stsutsui 
84*8b8575d9Stsutsui static const struct xpbus_attach_args xpdevs[] = {
85*8b8575d9Stsutsui 	{ "xp" },
86*8b8575d9Stsutsui 	{ "psgpam" },
87*8b8575d9Stsutsui };
88*8b8575d9Stsutsui 
89*8b8575d9Stsutsui static int xpbus_match(device_t, cfdata_t, void *);
90*8b8575d9Stsutsui static void xpbus_attach(device_t, device_t, void *);
91*8b8575d9Stsutsui 
92*8b8575d9Stsutsui CFATTACH_DECL_NEW(xpbus, sizeof(struct xpbus_softc),
93*8b8575d9Stsutsui     xpbus_match, xpbus_attach, NULL, NULL);
94*8b8575d9Stsutsui 
95*8b8575d9Stsutsui static bool xpbus_matched;
96*8b8575d9Stsutsui 
97*8b8575d9Stsutsui /*
98*8b8575d9Stsutsui  * xpbus acquired device sharing bitmap
99*8b8575d9Stsutsui  */
100*8b8575d9Stsutsui static volatile unsigned int xp_acquired;
101*8b8575d9Stsutsui 
102*8b8575d9Stsutsui /* XP SHM dirty flag */
103*8b8575d9Stsutsui static bool xp_shm_dirty = true;
104*8b8575d9Stsutsui 
105*8b8575d9Stsutsui static int
xpbus_match(device_t parent,cfdata_t cf,void * aux)106*8b8575d9Stsutsui xpbus_match(device_t parent, cfdata_t cf, void *aux)
107*8b8575d9Stsutsui {
108*8b8575d9Stsutsui 	struct mainbus_attach_args *ma = aux;
109*8b8575d9Stsutsui 
110*8b8575d9Stsutsui 	/* only one XP processor */
111*8b8575d9Stsutsui 	if (xpbus_matched)
112*8b8575d9Stsutsui 		return 0;
113*8b8575d9Stsutsui 
114*8b8575d9Stsutsui 	if (ma->ma_addr != XP_SHM_BASE)
115*8b8575d9Stsutsui 		return 0;
116*8b8575d9Stsutsui 
117*8b8575d9Stsutsui 	xpbus_matched = true;
118*8b8575d9Stsutsui 	return 1;
119*8b8575d9Stsutsui }
120*8b8575d9Stsutsui 
121*8b8575d9Stsutsui static void
xpbus_attach(device_t parent,device_t self,void * aux)122*8b8575d9Stsutsui xpbus_attach(device_t parent, device_t self, void *aux)
123*8b8575d9Stsutsui {
124*8b8575d9Stsutsui 	struct xpbus_softc *sc = device_private(self);
125*8b8575d9Stsutsui 	struct xpbus_attach_args xa;
126*8b8575d9Stsutsui 	int i;
127*8b8575d9Stsutsui 
128*8b8575d9Stsutsui 	sc->sc_dev = self;
129*8b8575d9Stsutsui 	aprint_normal("\n");
130*8b8575d9Stsutsui 
131*8b8575d9Stsutsui 	for (i = 0; i < __arraycount(xpdevs); i++) {
132*8b8575d9Stsutsui 		xa = xpdevs[i];
133*8b8575d9Stsutsui 		config_found(self, &xa, NULL, CFARGS_NONE);
134*8b8575d9Stsutsui 	}
135*8b8575d9Stsutsui }
136*8b8575d9Stsutsui 
137*8b8575d9Stsutsui /*
138*8b8575d9Stsutsui  * acquire xpbus from child devices
139*8b8575d9Stsutsui  * if success, return non-zero acquired map
140*8b8575d9Stsutsui  * if fail, return 0
141*8b8575d9Stsutsui  */
142*8b8575d9Stsutsui u_int
xp_acquire(int xplx_devid,u_int excl)143*8b8575d9Stsutsui xp_acquire(int xplx_devid, u_int excl)
144*8b8575d9Stsutsui {
145*8b8575d9Stsutsui 
146*8b8575d9Stsutsui 	for (;;) {
147*8b8575d9Stsutsui 		unsigned int before, after;
148*8b8575d9Stsutsui 		before = xp_acquired;
149*8b8575d9Stsutsui 		if (before & XP_ACQ_EXCL)
150*8b8575d9Stsutsui 			return 0;
151*8b8575d9Stsutsui 		if (before & (1 << xplx_devid))
152*8b8575d9Stsutsui 			return 0;
153*8b8575d9Stsutsui 		after = before | (1 << xplx_devid) | excl;
154*8b8575d9Stsutsui 		if (atomic_cas_uint(&xp_acquired, before, after) == before) {
155*8b8575d9Stsutsui 			return after & ~(excl);
156*8b8575d9Stsutsui 		}
157*8b8575d9Stsutsui 	}
158*8b8575d9Stsutsui }
159*8b8575d9Stsutsui 
160*8b8575d9Stsutsui /* release xpbus by child devices */
161*8b8575d9Stsutsui void
xp_release(int xplx_devid)162*8b8575d9Stsutsui xp_release(int xplx_devid)
163*8b8575d9Stsutsui {
164*8b8575d9Stsutsui 
165*8b8575d9Stsutsui 	for (;;) {
166*8b8575d9Stsutsui 		unsigned int before, after;
167*8b8575d9Stsutsui 		before = xp_acquired;
168*8b8575d9Stsutsui 		after = before & ~(1 << xplx_devid) & ~XP_ACQ_EXCL;
169*8b8575d9Stsutsui 		if (atomic_cas_uint(&xp_acquired, before, after) == before) {
170*8b8575d9Stsutsui 			return;
171*8b8575d9Stsutsui 		}
172*8b8575d9Stsutsui 	}
173*8b8575d9Stsutsui }
174*8b8575d9Stsutsui 
175*8b8575d9Stsutsui /* set the xp_shm_dirty flag */
176*8b8575d9Stsutsui void
xp_set_shm_dirty(void)177*8b8575d9Stsutsui xp_set_shm_dirty(void)
178*8b8575d9Stsutsui {
179*8b8575d9Stsutsui 
180*8b8575d9Stsutsui 	xp_shm_dirty = true;
181*8b8575d9Stsutsui }
182*8b8575d9Stsutsui 
183*8b8575d9Stsutsui /* reload firmware if xp_shm_dirty */
184*8b8575d9Stsutsui void
xp_ensure_firmware(void)185*8b8575d9Stsutsui xp_ensure_firmware(void)
186*8b8575d9Stsutsui {
187*8b8575d9Stsutsui 
188*8b8575d9Stsutsui 	if (xp_shm_dirty) {
189*8b8575d9Stsutsui 		/* firmware transfer */
190*8b8575d9Stsutsui 		xp_cpu_reset_hold();
191*8b8575d9Stsutsui 		delay(100);
192*8b8575d9Stsutsui 		memcpy((void *)XP_SHM_BASE, xplx, xplx_size);
193*8b8575d9Stsutsui 		/* XXX maybe not necessary */
194*8b8575d9Stsutsui 		delay(100);
195*8b8575d9Stsutsui 		xp_cpu_reset_release();
196*8b8575d9Stsutsui 		xp_shm_dirty = false;
197*8b8575d9Stsutsui 	}
198*8b8575d9Stsutsui }
199*8b8575d9Stsutsui 
200*8b8575d9Stsutsui /* PIO PORTC write */
201*8b8575d9Stsutsui uint8_t
put_pio0c(uint8_t bit,uint8_t set)202*8b8575d9Stsutsui put_pio0c(uint8_t bit, uint8_t set)
203*8b8575d9Stsutsui {
204*8b8575d9Stsutsui 	volatile uint8_t * const pio0 = (uint8_t *)PIO_ADDR;
205*8b8575d9Stsutsui 
206*8b8575d9Stsutsui 	pio0[CTRL] = (bit << 1) | (set & 0x01);
207*8b8575d9Stsutsui 
208*8b8575d9Stsutsui 	return pio0[PORT_C];
209*8b8575d9Stsutsui }
210*8b8575d9Stsutsui 
211*8b8575d9Stsutsui /* hold XP RESET signal */
212*8b8575d9Stsutsui void
xp_cpu_reset_hold(void)213*8b8575d9Stsutsui xp_cpu_reset_hold(void)
214*8b8575d9Stsutsui {
215*8b8575d9Stsutsui 
216*8b8575d9Stsutsui 	put_pio0c(XP_RESET, ON);
217*8b8575d9Stsutsui }
218*8b8575d9Stsutsui 
219*8b8575d9Stsutsui /* release XP RESET signal */
220*8b8575d9Stsutsui void
xp_cpu_reset_release(void)221*8b8575d9Stsutsui xp_cpu_reset_release(void)
222*8b8575d9Stsutsui {
223*8b8575d9Stsutsui 
224*8b8575d9Stsutsui 	put_pio0c(XP_RESET, OFF);
225*8b8575d9Stsutsui }
226*8b8575d9Stsutsui 
227*8b8575d9Stsutsui /* one-shot XP RESET signal */
228*8b8575d9Stsutsui void
xp_cpu_reset(void)229*8b8575d9Stsutsui xp_cpu_reset(void)
230*8b8575d9Stsutsui {
231*8b8575d9Stsutsui 
232*8b8575d9Stsutsui 	xp_cpu_reset_hold();
233*8b8575d9Stsutsui 	delay(100);
234*8b8575d9Stsutsui 	xp_cpu_reset_release();
235*8b8575d9Stsutsui }
236*8b8575d9Stsutsui 
237*8b8575d9Stsutsui /* enable XP to Host interrupt 1 */
238*8b8575d9Stsutsui void
xp_intr1_enable(void)239*8b8575d9Stsutsui xp_intr1_enable(void)
240*8b8575d9Stsutsui {
241*8b8575d9Stsutsui 
242*8b8575d9Stsutsui 	put_pio0c(XP_INT1_ENA, ON);
243*8b8575d9Stsutsui }
244*8b8575d9Stsutsui 
245*8b8575d9Stsutsui /* disable XP to Host interrupt 1 */
246*8b8575d9Stsutsui void
xp_intr1_disable(void)247*8b8575d9Stsutsui xp_intr1_disable(void)
248*8b8575d9Stsutsui {
249*8b8575d9Stsutsui 
250*8b8575d9Stsutsui 	put_pio0c(XP_INT1_ENA, OFF);
251*8b8575d9Stsutsui }
252*8b8575d9Stsutsui 
253*8b8575d9Stsutsui /* interrupt 1 ack */
254*8b8575d9Stsutsui void
xp_intr1_acknowledge(void)255*8b8575d9Stsutsui xp_intr1_acknowledge(void)
256*8b8575d9Stsutsui {
257*8b8575d9Stsutsui 
258*8b8575d9Stsutsui 	/* reset the interrupt request: read PIO0 port A */
259*8b8575d9Stsutsui 	/* XXX: probably */
260*8b8575d9Stsutsui 	*(volatile uint8_t *)OBIO_PIO0A;
261*8b8575d9Stsutsui 	/* XXX: just a guess */
262*8b8575d9Stsutsui 	*(volatile uint8_t *)OBIO_PIO0B;
263*8b8575d9Stsutsui }
264*8b8575d9Stsutsui 
265*8b8575d9Stsutsui /* enable XP to Host interrupt 5 */
266*8b8575d9Stsutsui void
xp_intr5_enable(void)267*8b8575d9Stsutsui xp_intr5_enable(void)
268*8b8575d9Stsutsui {
269*8b8575d9Stsutsui 
270*8b8575d9Stsutsui 	put_pio0c(XP_INT5_ENA, ON);
271*8b8575d9Stsutsui }
272*8b8575d9Stsutsui 
273*8b8575d9Stsutsui /* disable XP to Host interrupt 5 */
274*8b8575d9Stsutsui void
xp_intr5_disable(void)275*8b8575d9Stsutsui xp_intr5_disable(void)
276*8b8575d9Stsutsui {
277*8b8575d9Stsutsui 
278*8b8575d9Stsutsui 	put_pio0c(XP_INT5_ENA, OFF);
279*8b8575d9Stsutsui }
280*8b8575d9Stsutsui 
281*8b8575d9Stsutsui /* interrupt 5 ack */
282*8b8575d9Stsutsui void
xp_intr5_acknowledge(void)283*8b8575d9Stsutsui xp_intr5_acknowledge(void)
284*8b8575d9Stsutsui {
285*8b8575d9Stsutsui 
286*8b8575d9Stsutsui 	/* reset the interrupt request: read PIO0 port A */
287*8b8575d9Stsutsui 	(void)*(volatile uint8_t *)OBIO_PIO0A;
288*8b8575d9Stsutsui }
289*8b8575d9Stsutsui 
290*8b8575d9Stsutsui /* get XP shared memory pointer */
291*8b8575d9Stsutsui void *
xp_shmptr(int offset)292*8b8575d9Stsutsui xp_shmptr(int offset)
293*8b8575d9Stsutsui {
294*8b8575d9Stsutsui 
295*8b8575d9Stsutsui 	return (uint8_t *)XP_SHM_BASE + offset;
296*8b8575d9Stsutsui }
297*8b8575d9Stsutsui 
298*8b8575d9Stsutsui /* read 1 byte */
299*8b8575d9Stsutsui int
xp_readmem8(int offset)300*8b8575d9Stsutsui xp_readmem8(int offset)
301*8b8575d9Stsutsui {
302*8b8575d9Stsutsui 
303*8b8575d9Stsutsui 	return *((volatile uint8_t *)xp_shmptr(offset));
304*8b8575d9Stsutsui }
305*8b8575d9Stsutsui 
306*8b8575d9Stsutsui /* read 1 16bitLE */
307*8b8575d9Stsutsui int
xp_readmem16le(int offset)308*8b8575d9Stsutsui xp_readmem16le(int offset)
309*8b8575d9Stsutsui {
310*8b8575d9Stsutsui 
311*8b8575d9Stsutsui 	return le16toh(*(volatile uint16_t *)xp_shmptr(offset));
312*8b8575d9Stsutsui }
313*8b8575d9Stsutsui 
314*8b8575d9Stsutsui /* write 1 byte */
315*8b8575d9Stsutsui void
xp_writemem8(int offset,int v)316*8b8575d9Stsutsui xp_writemem8(int offset, int v)
317*8b8575d9Stsutsui {
318*8b8575d9Stsutsui 
319*8b8575d9Stsutsui 	*(volatile uint8_t *)(xp_shmptr(offset)) = v;
320*8b8575d9Stsutsui }
321*8b8575d9Stsutsui 
322*8b8575d9Stsutsui /* write 1 16bitLE */
323*8b8575d9Stsutsui void
xp_writemem16le(int offset,int v)324*8b8575d9Stsutsui xp_writemem16le(int offset, int v)
325*8b8575d9Stsutsui {
326*8b8575d9Stsutsui 
327*8b8575d9Stsutsui 	*((volatile uint16_t *)xp_shmptr(offset)) = htole16((uint16_t)v);
328*8b8575d9Stsutsui }
329