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