1 /*
2  *  Copyright (C) 2005-2020  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: Intel 8253/8254 Programmable Interval Timer
29  *
30  *  TODO/NOTE:
31  *	The timers don't really count down. Timer 0 causes clock interrupts
32  *	at a specific frequency, but reading the counter register would not
33  *	result in anything meaningful.
34  *
35  *  (Split counter[] into reset value and current value.)
36  */
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "cpu.h"
43 #include "device.h"
44 #include "emul.h"
45 #include "interrupt.h"
46 #include "machine.h"
47 #include "memory.h"
48 #include "misc.h"
49 #include "timer.h"
50 
51 #include "thirdparty/i8253reg.h"
52 
53 
54 /*  #define debug fatal  */
55 
56 #define	DEV_8253_LENGTH		4
57 #define	TICK_SHIFT		14
58 
59 
60 struct pit8253_data {
61 	int		in_use;
62 
63 	int		counter_select;
64 	uint8_t		mode_byte;
65 
66 	int		mode[3];
67 	int		counter[3];
68 
69 	int		hz[3];
70 
71 	struct timer	*timer0;
72 	struct interrupt irq;
73 	int		pending_interrupts_timer0;
74 };
75 
76 
timer0_tick(struct timer * t,void * extra)77 static void timer0_tick(struct timer *t, void *extra)
78 {
79 	struct pit8253_data *d = (struct pit8253_data *) extra;
80 	d->pending_interrupts_timer0 ++;
81 
82 	/*  printf("%i ", d->pending_interrupts_timer0); fflush(stdout);  */
83 }
84 
85 
86 DEVICE_TICK(8253)
87 {
88 	struct pit8253_data *d = (struct pit8253_data *) extra;
89 
90 	if (!d->in_use)
91 		return;
92 
93 	// Generate interrupts regardless of (d->mode[0] & 0x0e)?
94 	// (It seems like Linux/MALTA kernels like this.)
95 	if (d->pending_interrupts_timer0 > 0)
96 		INTERRUPT_ASSERT(d->irq);
97 }
98 
99 
100 DEVICE_ACCESS(8253)
101 {
102 	struct pit8253_data *d = (struct pit8253_data *) extra;
103 	uint64_t idata = 0, odata = 0;
104 
105 	if (writeflag == MEM_WRITE)
106 		idata = memory_readmax64(cpu, data, len);
107 
108 	d->in_use = 1;
109 
110 	switch (relative_addr) {
111 
112 	case I8253_TIMER_CNTR0:
113 	case I8253_TIMER_CNTR1:
114 	case I8253_TIMER_CNTR2:
115 		if (writeflag == MEM_WRITE) {
116 			switch (d->mode_byte & 0x30) {
117 			case I8253_TIMER_LSB:
118 			case I8253_TIMER_16BIT:
119 				d->counter[relative_addr] &= 0xff00;
120 				d->counter[relative_addr] |= (idata & 0xff);
121 				break;
122 			case I8253_TIMER_MSB:
123 				d->counter[relative_addr] &= 0x00ff;
124 				d->counter[relative_addr] |= ((idata&0xff)<<8);
125 				if (d->counter[relative_addr] != 0)
126 					d->hz[relative_addr] = (int) (
127 					    I8253_TIMER_FREQ / (float)
128 					    d->counter[relative_addr] + 0.5);
129 				else
130 					d->hz[relative_addr] = 0;
131 				debug("[ 8253: counter %i set to %i (%i Hz) "
132 				    "]\n", relative_addr, d->counter[
133 				    relative_addr], d->hz[relative_addr]);
134 				switch (relative_addr) {
135 				case 0:	if (d->timer0 == NULL)
136 						d->timer0 = timer_add(
137 						    d->hz[0], timer0_tick, d);
138 					else
139 						timer_update_frequency(
140 						    d->timer0, d->hz[0]);
141 					break;
142 				case 1:	fatal("TODO: DMA refresh?\n");
143 					exit(1);
144 				case 2:	fatal("TODO: 8253 tone generation?\n");
145 					break;
146 				}
147 				break;
148 			default:fatal("[ 8253: huh? writing to counter"
149 				    " %i but neither from msb nor lsb? ]\n",
150 				    relative_addr);
151 				exit(1);
152 			}
153 		} else {
154 			switch (d->mode_byte & 0x30) {
155 			case I8253_TIMER_LSB:
156 			case I8253_TIMER_16BIT:
157 				odata = d->counter[relative_addr] & 0xff;
158 				break;
159 			case I8253_TIMER_MSB:
160 				odata = (d->counter[relative_addr] >> 8) & 0xff;
161 				break;
162 			default:fatal("[ 8253: huh? reading from counter"
163 				    " %i but neither from msb nor lsb? ]\n",
164 				    relative_addr);
165 				exit(1);
166 			}
167 		}
168 
169 		/*  Switch from LSB to MSB, if accessing as 16-bit word:  */
170 		if ((d->mode_byte & 0x30) == I8253_TIMER_16BIT)
171 			d->mode_byte &= ~I8253_TIMER_LSB;
172 
173 		break;
174 
175 	case I8253_TIMER_MODE:
176 		if (writeflag == MEM_WRITE) {
177 			d->mode_byte = idata;
178 
179 			d->counter_select = idata >> 6;
180 			if (d->counter_select > 2) {
181 				debug("[ 8253: attempt to select counter 3,"
182 				    " which doesn't exist. ]\n");
183 				d->counter_select = 0;
184 			}
185 
186 			d->mode[d->counter_select] = idata & 0x0e;
187 
188 			debug("[ 8253: select=%i mode=0x%x ",
189 			    d->counter_select, d->mode[d->counter_select]);
190 			if (idata & 0x30) {
191 				switch (idata & 0x30) {
192 				case I8253_TIMER_LSB:
193 					debug("LSB ");
194 					break;
195 				case I8253_TIMER_16BIT:
196 					debug("LSB+");
197 					// fall through
198 				case I8253_TIMER_MSB:
199 					debug("MSB ");
200 				}
201 			}
202 			debug("]\n");
203 
204 			if (idata & I8253_TIMER_BCD) {
205 				fatal("[ 8253: BCD not yet implemented ]\n");
206 				exit(1);
207 			}
208 		} else {
209 			debug("[ 8253: read; can this actually happen? ]\n");
210 			odata = d->mode_byte;
211 		}
212 		break;
213 
214 	default:if (writeflag == MEM_WRITE) {
215 			fatal("[ 8253: unimplemented write to address 0x%x"
216 			    " data=0x%02x ]\n", (int)relative_addr, (int)idata);
217 		} else {
218 			fatal("[ 8253: unimplemented read from address 0x%x "
219 			    "]\n", (int)relative_addr);
220 		}
221 		exit(1);
222 	}
223 
224 	if (writeflag == MEM_READ)
225 		memory_writemax64(cpu, data, len, odata);
226 
227 	return 1;
228 }
229 
230 
231 DEVINIT(8253)
232 {
233 	struct pit8253_data *d;
234 
235 	CHECK_ALLOCATION(d = (struct pit8253_data *) malloc(sizeof(struct pit8253_data)));
236 	memset(d, 0, sizeof(struct pit8253_data));
237 
238 	d->in_use = devinit->in_use;
239 
240 	INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
241 
242 	/*  Don't cause interrupt, by default.  */
243 	d->mode[0] = I8253_TIMER_RATEGEN;
244 	d->mode[1] = I8253_TIMER_RATEGEN;
245 	d->mode[2] = I8253_TIMER_RATEGEN;
246 
247 	devinit->machine->isa_pic_data.pending_timer_interrupts =
248 	    &d->pending_interrupts_timer0;
249 
250 	memory_device_register(devinit->machine->memory, devinit->name,
251 	    devinit->addr, DEV_8253_LENGTH, dev_8253_access, (void *)d,
252 	    DM_DEFAULT, NULL);
253 
254 	machine_add_tickfunction(devinit->machine, dev_8253_tick,
255 	    d, TICK_SHIFT);
256 
257 	return 1;
258 }
259 
260