1/*  This file is part of the program psim.
2
3    Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, see <http://www.gnu.org/licenses/>.
17
18    */
19
20
21#ifndef _HW_PAL_C_
22#define _HW_PAL_C_
23
24#ifndef STATIC_INLINE_HW_PAL
25#define STATIC_INLINE_HW_PAL STATIC_INLINE
26#endif
27
28#include "device_table.h"
29
30#include "cpu.h"
31
32#ifdef HAVE_STRING_H
33#include <string.h>
34#else
35#ifdef HAVE_STRINGS_H
36#include <strings.h>
37#endif
38#endif
39
40#ifdef HAVE_UNISTD_H
41#include <unistd.h>
42#endif
43#ifdef HAVE_STDLIB_H
44#include <stdlib.h>
45#endif
46
47
48/* DEVICE
49
50
51   pal - glue logic device containing assorted junk
52
53
54   DESCRIPTION
55
56
57   Typical hardware dependant hack.  This device allows the firmware
58   to gain access to all the things the firmware needs (but the OS
59   doesn't).
60
61   The pal contains the following registers.  Except for the interrupt
62   level register, each of the below is 8 bytes in size and must be
63   accessed using correct alignment.  For 16 and 32 bit accesses the
64   bytes not directed to the register are ignored:
65
66   |0	reset register (write)
67   |4	processor id register (read)
68   |8	interrupt port (write)
69   |9	interrupt level (write)
70   |12	processor count register (read)
71   |16	tty input fifo register (read)
72   |20	tty input status register (read)
73   |24	tty output fifo register (write)
74   |28	tty output status register (read)
75
76   Reset register (write) halts the simulator exiting with the
77   value written.
78
79   Processor id register (read) returns the processor number (0
80   .. N-1) of the processor performing the read.
81
82   The interrupt registers should be accessed as a pair (using a 16 or
83   32 bit store).  The low byte specifies the interrupt port while the
84   high byte specifies the level to drive that port at.  By
85   convention, the pal's interrupt ports (int0, int1, ...) are wired
86   up to the corresponding processor's level sensative external
87   interrupt pin.  Eg: A two byte write to address 8 of 0x0102
88   (big-endian) will result in processor 2's external interrupt pin to
89   be asserted.
90
91   Processor count register (read) returns the total number of
92   processors active in the current simulation.
93
94   TTY input fifo register (read), if the TTY input status register
95   indicates a character is available by being nonzero, returns the
96   next available character from the pal's tty input port.
97
98   Similarly, the TTY output fifo register (write), if the TTY output
99   status register indicates the output fifo is not full by being
100   nonzero, outputs the character written to the tty's output port.
101
102
103   PROPERTIES
104
105
106   reg = <address> <size> (required)
107
108   Specify the address (within the parent bus) that this device is to
109   live.
110
111
112   */
113
114
115enum {
116  hw_pal_reset_register = 0x0,
117  hw_pal_cpu_nr_register = 0x4,
118  hw_pal_int_register = 0x8,
119  hw_pal_nr_cpu_register = 0xa,
120  hw_pal_read_fifo = 0x10,
121  hw_pal_read_status = 0x14,
122  hw_pal_write_fifo = 0x18,
123  hw_pal_write_status = 0x1a,
124  hw_pal_address_mask = 0x1f,
125};
126
127
128typedef struct _hw_pal_console_buffer {
129  char buffer;
130  int status;
131} hw_pal_console_buffer;
132
133typedef struct _hw_pal_device {
134  hw_pal_console_buffer input;
135  hw_pal_console_buffer output;
136  device *disk;
137} hw_pal_device;
138
139
140/* check the console for an available character */
141static void
142scan_hw_pal(hw_pal_device *hw_pal)
143{
144  char c;
145  int count;
146  count = sim_io_read_stdin(&c, sizeof(c));
147  switch (count) {
148  case sim_io_not_ready:
149  case sim_io_eof:
150    hw_pal->input.buffer = 0;
151    hw_pal->input.status = 0;
152    break;
153  default:
154    hw_pal->input.buffer = c;
155    hw_pal->input.status = 1;
156  }
157}
158
159/* write the character to the hw_pal */
160static void
161write_hw_pal(hw_pal_device *hw_pal,
162	     char val)
163{
164  sim_io_write_stdout(&val, 1);
165  hw_pal->output.buffer = val;
166  hw_pal->output.status = 1;
167}
168
169
170static unsigned
171hw_pal_io_read_buffer_callback(device *me,
172			       void *dest,
173			       int space,
174			       unsigned_word addr,
175			       unsigned nr_bytes,
176			       cpu *processor,
177			       unsigned_word cia)
178{
179  hw_pal_device *hw_pal = (hw_pal_device*)device_data(me);
180  unsigned_1 val;
181  switch (addr & hw_pal_address_mask) {
182  case hw_pal_cpu_nr_register:
183    val = cpu_nr(processor);
184    DTRACE(pal, ("read - cpu-nr %d\n", val));
185    break;
186  case hw_pal_nr_cpu_register:
187    val = tree_find_integer_property(me, "/openprom/options/smp");
188    DTRACE(pal, ("read - nr-cpu %d\n", val));
189    break;
190  case hw_pal_read_fifo:
191    val = hw_pal->input.buffer;
192    DTRACE(pal, ("read - input-fifo %d\n", val));
193    break;
194  case hw_pal_read_status:
195    scan_hw_pal(hw_pal);
196    val = hw_pal->input.status;
197    DTRACE(pal, ("read - input-status %d\n", val));
198    break;
199  case hw_pal_write_fifo:
200    val = hw_pal->output.buffer;
201    DTRACE(pal, ("read - output-fifo %d\n", val));
202    break;
203  case hw_pal_write_status:
204    val = hw_pal->output.status;
205    DTRACE(pal, ("read - output-status %d\n", val));
206    break;
207  default:
208    val = 0;
209    DTRACE(pal, ("read - ???\n"));
210  }
211  memset(dest, 0, nr_bytes);
212  *(unsigned_1*)dest = val;
213  return nr_bytes;
214}
215
216
217static unsigned
218hw_pal_io_write_buffer_callback(device *me,
219				const void *source,
220				int space,
221				unsigned_word addr,
222				unsigned nr_bytes,
223				cpu *processor,
224				unsigned_word cia)
225{
226  hw_pal_device *hw_pal = (hw_pal_device*)device_data(me);
227  unsigned_1 *byte = (unsigned_1*)source;
228
229  switch (addr & hw_pal_address_mask) {
230  case hw_pal_reset_register:
231    cpu_halt(processor, cia, was_exited, byte[0]);
232    break;
233  case hw_pal_int_register:
234    device_interrupt_event(me,
235			   byte[0], /*port*/
236			   (nr_bytes > 1 ? byte[1] : 0), /* val */
237			   processor, cia);
238    break;
239  case hw_pal_read_fifo:
240    hw_pal->input.buffer = byte[0];
241    DTRACE(pal, ("write - input-fifo %d\n", byte[0]));
242    break;
243  case hw_pal_read_status:
244    hw_pal->input.status = byte[0];
245    DTRACE(pal, ("write - input-status %d\n", byte[0]));
246    break;
247  case hw_pal_write_fifo:
248    write_hw_pal(hw_pal, byte[0]);
249    DTRACE(pal, ("write - output-fifo %d\n", byte[0]));
250    break;
251  case hw_pal_write_status:
252    hw_pal->output.status = byte[0];
253    DTRACE(pal, ("write - output-status %d\n", byte[0]));
254    break;
255  }
256  return nr_bytes;
257}
258
259
260/* instances of the hw_pal device */
261
262static void
263hw_pal_instance_delete_callback(device_instance *instance)
264{
265  /* nothing to delete, the hw_pal is attached to the device */
266  return;
267}
268
269static int
270hw_pal_instance_read_callback(device_instance *instance,
271			      void *buf,
272			      unsigned_word len)
273{
274  DITRACE(pal, ("read - %s (%ld)", (const char*)buf, (long int)len));
275  return sim_io_read_stdin(buf, len);
276}
277
278static int
279hw_pal_instance_write_callback(device_instance *instance,
280			       const void *buf,
281			       unsigned_word len)
282{
283  int i;
284  const char *chp = buf;
285  hw_pal_device *hw_pal = device_instance_data(instance);
286  DITRACE(pal, ("write - %s (%ld)", (const char*)buf, (long int)len));
287  for (i = 0; i < len; i++)
288    write_hw_pal(hw_pal, chp[i]);
289  sim_io_flush_stdoutput();
290  return i;
291}
292
293static const device_instance_callbacks hw_pal_instance_callbacks = {
294  hw_pal_instance_delete_callback,
295  hw_pal_instance_read_callback,
296  hw_pal_instance_write_callback,
297};
298
299static device_instance *
300hw_pal_create_instance(device *me,
301		       const char *path,
302		       const char *args)
303{
304  return device_create_instance_from(me, NULL,
305				     device_data(me),
306				     path, args,
307				     &hw_pal_instance_callbacks);
308}
309
310static const device_interrupt_port_descriptor hw_pal_interrupt_ports[] = {
311  { "int", 0, MAX_NR_PROCESSORS },
312  { NULL }
313};
314
315
316static void
317hw_pal_attach_address(device *me,
318		      attach_type attach,
319		      int space,
320		      unsigned_word addr,
321		      unsigned nr_bytes,
322		      access_type access,
323		      device *client)
324{
325  hw_pal_device *pal = (hw_pal_device*)device_data(me);
326  pal->disk = client;
327}
328
329
330static device_callbacks const hw_pal_callbacks = {
331  { generic_device_init_address, },
332  { hw_pal_attach_address, }, /* address */
333  { hw_pal_io_read_buffer_callback,
334      hw_pal_io_write_buffer_callback, },
335  { NULL, }, /* DMA */
336  { NULL, NULL, hw_pal_interrupt_ports }, /* interrupt */
337  { generic_device_unit_decode,
338    generic_device_unit_encode,
339    generic_device_address_to_attach_address,
340    generic_device_size_to_attach_size },
341  hw_pal_create_instance,
342};
343
344
345static void *
346hw_pal_create(const char *name,
347	      const device_unit *unit_address,
348	      const char *args)
349{
350  /* create the descriptor */
351  hw_pal_device *hw_pal = ZALLOC(hw_pal_device);
352  hw_pal->output.status = 1;
353  hw_pal->output.buffer = '\0';
354  hw_pal->input.status = 0;
355  hw_pal->input.buffer = '\0';
356  return hw_pal;
357}
358
359
360const device_descriptor hw_pal_device_descriptor[] = {
361  { "pal", hw_pal_create, &hw_pal_callbacks },
362  { NULL },
363};
364
365#endif /* _HW_PAL_C_ */
366