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