| 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 | |
| 116 | enum { |
| 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 | |
| 129 | typedef struct _hw_pal_console_buffer { |
| 130 | char buffer; |
| 131 | int status; |
| 132 | } hw_pal_console_buffer; |
| 133 | |
| 134 | typedef 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 */ |
| 142 | static void |
| 143 | scan_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 */ |
| 161 | static void |
| 162 | write_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 | |
| 171 | static unsigned |
| 172 | hw_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 | |
| 218 | static unsigned |
| 219 | hw_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 | |
| 263 | static void |
| 264 | hw_pal_instance_delete_callback(device_instance *instance) |
| 265 | { |
| 266 | /* nothing to delete, the hw_pal is attached to the device */ |
| 267 | return; |
| 268 | } |
| 269 | |
| 270 | static int |
| 271 | hw_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 | |
| 279 | static int |
| 280 | hw_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 | |
| 294 | static 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 | |
| 300 | static device_instance * |
| 301 | hw_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 | |
| 311 | static const device_interrupt_port_descriptor hw_pal_interrupt_ports[] = { |
| 312 | { "int", 0, MAX_NR_PROCESSORS }, |
| 313 | { NULL } |
| 314 | }; |
| 315 | |
| 316 | |
| 317 | static void |
| 318 | hw_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 | |
| 331 | static 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 | |
| 346 | static void * |
| 347 | hw_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 | |
| 361 | const device_descriptor hw_pal_device_descriptor[] = { |
| 362 | { "pal", hw_pal_create, &hw_pal_callbacks }, |
| 363 | { NULL }, |
| 364 | }; |
| 365 | |
| 366 | #endif /* _HW_PAL_C_ */ |