1 /*
2  *  Copyright (C) 2004-2009  Anders Gavare.  All rights reserved.
3  *
4  *  Redistribution and use in source and binary forms, with or without
5  *  modification, are permitted provided that the following conditions are met:
6  *
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. The name of the author may not be used to endorse or promote products
13  *     derived from this software without specific prior written permission.
14  *
15  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  *  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  *  SUCH DAMAGE.
26  *
27  *
28  *  COMMENT: Zilog Z8530 "zs" serial controller
29  *
30  *  Features:
31  *	o)  Two channels, 0 = "channel B", 1 = "channel A".
32  *	    Normally, only channel B is in use.
33  *
34  *  This is a work in progress... TODOs include:
35  *	o)  Implement more of the register set.
36  *	o)  Verify that it works with other guest OSes than NetBSD and OpenBSD.
37  *	o)  Implement DMA!
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "console.h"
45 #include "cpu.h"
46 #include "device.h"
47 #include "interrupt.h"
48 #include "machine.h"
49 #include "memory.h"
50 #include "misc.h"
51 
52 #include "thirdparty/z8530reg.h"
53 
54 
55 /*  #define debug fatal  */
56 
57 #define	ZS_TICK_SHIFT		14
58 #define	ZS_N_REGS		16
59 #define	ZS_N_CHANNELS		2
60 #define	DEV_Z8530_LENGTH	4
61 
62 struct z8530_data {
63 	struct interrupt irq;
64 	int		irq_asserted;
65 	int		addr_mult;
66 
67 	int		console_handle[ZS_N_CHANNELS];
68 	int		reg_select[ZS_N_CHANNELS];
69 	uint8_t		rr[ZS_N_CHANNELS][ZS_N_REGS];
70 	uint8_t		wr[ZS_N_CHANNELS][ZS_N_REGS];
71 };
72 
73 
74 /*
75  *  check_incoming():
76  *
77  *  Sets the RX interrupt flag for ports B and A, if something there is input
78  *  available on port 0 or 1, respectively.
79  */
check_incoming(struct cpu * cpu,struct z8530_data * d)80 static void check_incoming(struct cpu *cpu, struct z8530_data *d)
81 {
82 	if (console_charavail(d->console_handle[0])) {
83 		d->rr[1][3] |= ZSRR3_IP_B_RX;
84 		d->rr[0][0] |= ZSRR0_RX_READY;
85 	}
86 	if (console_charavail(d->console_handle[1])) {
87 		d->rr[1][3] |= ZSRR3_IP_A_RX;
88 		d->rr[1][0] |= ZSRR0_RX_READY;
89 	}
90 }
91 
92 
DEVICE_TICK(z8530)93 DEVICE_TICK(z8530)
94 {
95 	/*  Generate transmit and receive interrupts at regular intervals.  */
96 	struct z8530_data *d = (struct z8530_data *) extra;
97 	int asserted = 0;
98 
99 	if (d->rr[1][3] & ZSRR3_IP_B_TX && d->wr[0][1] & ZSWR1_TIE)
100 		asserted = 1;
101 	if (d->rr[1][3] & ZSRR3_IP_A_TX && d->wr[1][1] & ZSWR1_TIE)
102 		asserted = 1;
103 
104 	d->rr[1][3] &= ~(ZSRR3_IP_B_RX | ZSRR3_IP_A_RX);
105 	if (!asserted)
106 		check_incoming(cpu, d);
107 
108 	if (d->rr[1][3] & ZSRR3_IP_B_RX && (d->wr[0][1]&0x18) != ZSWR1_RIE_NONE)
109 		asserted = 1;
110 	if (d->rr[1][3] & ZSRR3_IP_A_RX && (d->wr[1][1]&0x18) != ZSWR1_RIE_NONE)
111 		asserted = 1;
112 
113 	if (!(d->wr[1][9] & ZSWR9_MASTER_IE))
114 		asserted = 0;
115 
116 	if (asserted)
117 		INTERRUPT_ASSERT(d->irq);
118 
119 	if (d->irq_asserted && !asserted)
120 		INTERRUPT_DEASSERT(d->irq);
121 
122 	d->irq_asserted = asserted;
123 }
124 
125 
DEVICE_ACCESS(z8530)126 DEVICE_ACCESS(z8530)
127 {
128 	struct z8530_data *d = (struct z8530_data *) extra;
129 	uint64_t idata = 0, odata = 0;
130 	int port_nr;
131 
132 	if (writeflag == MEM_WRITE)
133 		idata = memory_readmax64(cpu, data, len);
134 
135 	/*  Both ports are always ready to transmit:  */
136 	d->rr[0][0] |= ZSRR0_TX_READY | ZSRR0_DCD | ZSRR0_CTS;
137 	d->rr[1][0] |= ZSRR0_TX_READY | ZSRR0_DCD | ZSRR0_CTS;
138 
139 	relative_addr /= d->addr_mult;
140 
141 	port_nr = (relative_addr / 2) % ZS_N_CHANNELS;
142 	relative_addr &= 1;
143 
144 	if (relative_addr == 0) {
145 		/*  Register access:  */
146 		if (writeflag == MEM_READ) {
147 			odata = d->rr[port_nr][d->reg_select[port_nr]];
148 			debug("[ z8530: read from port %i reg %2i: "
149 			    "0x%02x ]\n", port_nr, d->reg_select[
150 			    port_nr], (int)odata);
151 			d->reg_select[port_nr] = 0;
152 		} else {
153 			if (d->reg_select[port_nr] == 0) {
154 				if (idata < 16)
155 					d->reg_select[port_nr] = idata & 15;
156 				else
157 					d->reg_select[port_nr] = idata & 7;
158 				switch (idata & 0xf8) {
159 				case ZSWR0_CLR_INTR:	/*  Interrupt ack:  */
160 					d->rr[1][3] = 0;
161 					break;
162 				}
163 			} else {
164 				d->wr[port_nr][d->reg_select[port_nr]] = idata;
165 				switch (d->reg_select[port_nr]) {
166 				default:debug("[ z8530: write to  port %i reg "
167 					    "%2i: 0x%02x ]\n", port_nr, d->
168 					    reg_select[port_nr], (int)idata);
169 				}
170 				d->reg_select[port_nr] = 0;
171 			}
172 		}
173 	} else {
174 		/*  Data access:  */
175 		if (writeflag == MEM_READ) {
176 			int x = console_readchar(d->console_handle[port_nr]);
177 			d->rr[port_nr][0] &= ~ZSRR0_RX_READY;
178 			odata = x < 0? 0 : x;
179 		} else {
180 			idata &= 255;
181 			if (idata != 0)
182 				console_putchar(d->console_handle[port_nr],
183 				    idata);
184 			if (1 /* d->wr[port_nr][1] & ZSWR1_TIE */) {
185 				if (port_nr == 0)
186 					d->rr[1][3] |= ZSRR3_IP_B_TX;
187 				else
188 					d->rr[1][3] |= ZSRR3_IP_A_TX;
189 			}
190 		}
191 	}
192 
193 	if (writeflag == MEM_READ)
194 		memory_writemax64(cpu, data, len, odata);
195 
196 	dev_z8530_tick(cpu, extra);
197 
198 	return 1;
199 }
200 
201 
DEVINIT(z8530)202 DEVINIT(z8530)
203 {
204 	struct z8530_data *d;
205 	char tmp[100];
206 
207 	CHECK_ALLOCATION(d = (struct z8530_data *) malloc(sizeof(struct z8530_data)));
208 	memset(d, 0, sizeof(struct z8530_data));
209 
210 	d->addr_mult  = devinit->addr_mult;
211 
212 	INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
213 
214 	snprintf(tmp, sizeof(tmp), "%s [ch-b]", devinit->name);
215 	d->console_handle[0] = console_start_slave(devinit->machine, tmp,
216 	    devinit->in_use);
217 	snprintf(tmp, sizeof(tmp), "%s [ch-a]", devinit->name);
218 	d->console_handle[1] = console_start_slave(devinit->machine, tmp, 0);
219 
220 	if (devinit->name2 != NULL && devinit->name2[0])
221 		snprintf(tmp, sizeof(tmp), "%s [%s]", devinit->name,
222 		    devinit->name2);
223 	else
224 		snprintf(tmp, sizeof(tmp), "%s", devinit->name);
225 
226 	memory_device_register(devinit->machine->memory, tmp, devinit->addr,
227 	    DEV_Z8530_LENGTH * d->addr_mult, dev_z8530_access, d, DM_DEFAULT,
228 	    NULL);
229 
230 	machine_add_tickfunction(devinit->machine, dev_z8530_tick, d,
231 	    ZS_TICK_SHIFT);
232 
233 	devinit->return_ptr = (void *)(size_t) d->console_handle[0];
234 
235 	return 1;
236 }
237 
238