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