1 /* dv-m68hc11.c -- CPU 68HC11 as a device.
2 Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3 Written by Stephane Carrez (stcarrez@worldnet.fr)
4 (From a driver model Contributed by Cygnus Solutions.)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 m68hc11cpu - m68hc11 cpu virtual device
33 Implements the external m68hc11 functionality. This includes the
34 delivery of of interrupts generated from other devices and the
35 handling of device specific registers.
42 Register base (should be 0x1000 0x03f).
46 Frequency of the quartz used by the processor.
48 mode [single | expanded | bootstrap | test]
50 Cpu operating mode (the MODA and MODB external pins).
57 Reset the cpu and generates a cpu-reset event (used to reset
62 Deliver a non-maskable interrupt to the processor.
67 Event generated after the CPU performs a reset.
72 When delivering an interrupt, this code assumes that there is only
73 one processor (number 0).
80 /* Pending interrupts for delivery by event handler. */
84 struct hw_event
*event
;
85 unsigned_word attach_address
;
102 static const struct hw_port_descriptor m68hc11cpu_ports
[] = {
104 /* Interrupt inputs. */
105 { "reset", RESET_PORT
, 0, input_port
, },
106 { "nmi", NMI_PORT
, 0, input_port
, },
107 { "irq", IRQ_PORT
, 0, input_port
, },
109 /* Events generated for connection to other devices. */
110 { "cpu-reset", CPU_RESET_PORT
, 0, output_port
, },
115 static hw_io_read_buffer_method m68hc11cpu_io_read_buffer
;
116 static hw_io_write_buffer_method m68hc11cpu_io_write_buffer
;
117 static hw_ioctl_method m68hc11_ioctl
;
119 /* Finish off the partially created hw device. Attach our local
120 callbacks. Wire up our port names etc. */
122 static hw_port_event_method m68hc11cpu_port_event
;
126 dv_m6811_attach_address_callback (struct hw
*me
,
130 address_word nr_bytes
,
133 HW_TRACE ((me
, "attach - level=%d, space=%d, addr=0x%lx, sz=%ld, client=%s",
134 level
, space
, (unsigned long) addr
, (unsigned long) nr_bytes
,
139 sim_core_attach (hw_system (me
),
142 access_read_write_exec
,
151 /*printf("Attach from sub device: %d\n", (long) addr);*/
152 sim_core_attach (hw_system (me
),
165 dv_m6811_detach_address_callback (struct hw
*me
,
169 address_word nr_bytes
,
172 sim_core_detach (hw_system (me
), NULL
, /*cpu*/
178 attach_m68hc11_regs (struct hw
*me
,
179 struct m68hc11cpu
*controller
)
183 reg_property_spec reg
;
184 const char *cpu_mode
;
186 if (hw_find_property (me
, "reg") == NULL
)
187 hw_abort (me
, "Missing \"reg\" property");
189 if (!hw_find_reg_array_property (me
, "reg", 0, ®
))
190 hw_abort (me
, "\"reg\" property must contain one addr/size entry");
192 hw_unit_address_to_attach_address (hw_parent (me
),
194 &controller
->attach_space
,
195 &controller
->attach_address
,
197 hw_unit_size_to_attach_size (hw_parent (me
),
199 &controller
->attach_size
, me
);
201 hw_attach_address (hw_parent (me
), 0,
202 controller
->attach_space
,
203 controller
->attach_address
,
204 controller
->attach_size
,
208 /* Get cpu frequency. */
210 cpu
= STATE_CPU (sd
, 0);
211 if (hw_find_property (me
, "clock") != NULL
)
213 cpu
->cpu_frequency
= hw_find_integer_property (me
, "clock");
217 cpu
->cpu_frequency
= 8*1000*1000;
220 cpu_mode
= "expanded";
221 if (hw_find_property (me
, "mode") != NULL
)
222 cpu_mode
= hw_find_string_property (me
, "mode");
224 if (strcmp (cpu_mode
, "test") == 0)
225 cpu
->cpu_mode
= M6811_MDA
| M6811_SMOD
;
226 else if (strcmp (cpu_mode
, "bootstrap") == 0)
227 cpu
->cpu_mode
= M6811_SMOD
;
228 else if (strcmp (cpu_mode
, "single") == 0)
231 cpu
->cpu_mode
= M6811_MDA
;
235 m68hc11cpu_finish (struct hw
*me
)
237 struct m68hc11cpu
*controller
;
239 controller
= HW_ZALLOC (me
, struct m68hc11cpu
);
240 me
->overlap_mode_hw
= 1;
241 set_hw_data (me
, controller
);
242 set_hw_io_read_buffer (me
, m68hc11cpu_io_read_buffer
);
243 set_hw_io_write_buffer (me
, m68hc11cpu_io_write_buffer
);
244 set_hw_ports (me
, m68hc11cpu_ports
);
245 set_hw_port_event (me
, m68hc11cpu_port_event
);
246 set_hw_attach_address (me
, dv_m6811_attach_address_callback
);
247 set_hw_detach_address (me
, dv_m6811_detach_address_callback
);
249 set_hw_ioctl (me
, m68hc11_ioctl
);
251 me
->to_ioctl
= m68hc11_ioctl
;
254 /* Initialize the pending interrupt flags. */
255 controller
->pending_level
= 0;
256 controller
->pending_reset
= 0;
257 controller
->pending_nmi
= 0;
258 controller
->event
= NULL
;
260 attach_m68hc11_regs (me
, controller
);
265 /* An event arrives on an interrupt port. */
268 deliver_m68hc11cpu_interrupt (struct hw
*me
, void *data
)
274 m68hc11cpu_port_event (struct hw
*me
,
280 struct m68hc11cpu
*controller
= hw_data (me
);
285 cpu
= STATE_CPU (sd
, 0);
289 HW_TRACE ((me
, "port-in reset"));
291 /* The reset is made in 3 steps:
292 - First, cleanup the current sim_cpu struct.
294 - Restart the cpu for the reset (get the CPU mode from the
295 CONFIG register that gets initialized by EEPROM device). */
297 hw_port_event (me
, CPU_RESET_PORT
, 1);
302 controller
->pending_nmi
= 1;
303 HW_TRACE ((me
, "port-in nmi"));
307 /* level == 0 means that the interrupt was cleared. */
309 controller
->pending_level
= -1; /* signal end of interrupt */
311 controller
->pending_level
= level
;
312 HW_TRACE ((me
, "port-in level=%d", level
));
316 hw_abort (me
, "bad switch");
320 /* Schedule an event to be delivered immediately after current
322 if(controller
->event
!= NULL
)
323 hw_event_queue_deschedule(me
, controller
->event
);
325 hw_event_queue_schedule (me
, 0, deliver_m68hc11cpu_interrupt
, NULL
);
329 io_reg_desc config_desc
[] = {
330 { M6811_NOSEC
, "NOSEC ", "Security Mode Disable" },
331 { M6811_NOCOP
, "NOCOP ", "COP System Disable" },
332 { M6811_ROMON
, "ROMON ", "Enable On-chip Rom" },
333 { M6811_EEON
, "EEON ", "Enable On-chip EEprom" },
337 io_reg_desc hprio_desc
[] = {
338 { M6811_RBOOT
, "RBOOT ", "Read Bootstrap ROM" },
339 { M6811_SMOD
, "SMOD ", "Special Mode" },
340 { M6811_MDA
, "MDA ", "Mode Select A" },
341 { M6811_IRV
, "IRV ", "Internal Read Visibility" },
345 io_reg_desc option_desc
[] = {
346 { M6811_ADPU
, "ADPU ", "A/D Powerup" },
347 { M6811_CSEL
, "CSEL ", "A/D/EE Charge pump clock source select" },
348 { M6811_IRQE
, "IRQE ", "IRQ Edge/Level sensitive" },
349 { M6811_DLY
, "DLY ", "Stop exit turn on delay" },
350 { M6811_CME
, "CME ", "Clock Monitor Enable" },
351 { M6811_CR1
, "CR1 ", "COP timer rate select (CR1)" },
352 { M6811_CR0
, "CR0 ", "COP timer rate select (CR0)" },
357 m68hc11_info (struct hw
*me
)
362 struct m68hc11sio
*controller
;
366 cpu
= STATE_CPU (sd
, 0);
367 controller
= hw_data (me
);
369 base
= cpu_get_io_base (cpu
);
370 sim_io_printf (sd
, "M68HC11:\n");
372 val
= cpu
->ios
[M6811_HPRIO
];
373 print_io_byte (sd
, "HPRIO ", hprio_desc
, val
, base
+ M6811_HPRIO
);
374 sim_io_printf (sd
, "\n");
376 val
= cpu
->ios
[M6811_CONFIG
];
377 print_io_byte (sd
, "CONFIG", config_desc
, val
, base
+ M6811_CONFIG
);
378 sim_io_printf (sd
, "\n");
380 val
= cpu
->ios
[M6811_OPTION
];
381 print_io_byte (sd
, "OPTION", option_desc
, val
, base
+ M6811_OPTION
);
382 sim_io_printf (sd
, "\n");
384 val
= cpu
->ios
[M6811_INIT
];
385 print_io_byte (sd
, "INIT ", 0, val
, base
+ M6811_INIT
);
386 sim_io_printf (sd
, "Ram = 0x%04x IO = 0x%04x\n",
387 (((uint16
) (val
& 0xF0)) << 8),
388 (((uint16
) (val
& 0x0F)) << 12));
392 interrupts_info (sd
, &cpu
->cpu_interrupts
);
396 m68hc11_ioctl (struct hw
*me
,
397 hw_ioctl_request request
,
404 /* generic read/write */
407 m68hc11cpu_io_read_buffer (struct hw
*me
,
414 struct m68hc11cpu
*controller
= hw_data (me
);
419 HW_TRACE ((me
, "read 0x%08lx %d", (long) base
, (int) nr_bytes
));
422 cpu
= STATE_CPU (sd
, 0);
424 /* Handle reads for the sub-devices. */
425 base
-= controller
->attach_address
;
426 result
= sim_core_read_buffer (sd
, cpu
,
427 io_map
, dest
, base
, nr_bytes
);
436 memcpy (dest
, &cpu
->ios
[base
], 1);
447 m68hc11cpu_io_write (struct hw
*me
, sim_cpu
*cpu
,
448 unsigned_word addr
, uint8 val
)
480 /* Change the RAM and I/O mapping. */
483 uint8 old_bank
= cpu
->ios
[M6811_INIT
];
485 cpu
->ios
[M6811_INIT
] = val
;
487 /* Update IO mapping. Detach from the old address
488 and attach to the new one. */
489 if ((old_bank
& 0xF0) != (val
& 0xF0))
491 struct m68hc11cpu
*controller
= hw_data (me
);
493 hw_detach_address (hw_parent (me
), 0,
494 controller
->attach_space
,
495 controller
->attach_address
,
496 controller
->attach_size
,
498 controller
->attach_address
= (val
& 0x0F0) << 12;
499 hw_attach_address (hw_parent (me
), 0,
500 controller
->attach_space
,
501 controller
->attach_address
,
502 controller
->attach_size
,
505 if ((old_bank
& 0x0F) != (val
& 0x0F))
512 /* Writing the config is similar to programing the eeprom.
513 The config register value is the last byte of the EEPROM.
514 This last byte is not mapped in memory (that's why we have
515 to add '1' to 'end_addr'). */
524 if (val
== 0xAA && cpu
->ios
[addr
] == 0x55)
527 /* COP reset here. */
535 cpu
->ios
[addr
] = val
;
539 m68hc11cpu_io_write_buffer (struct hw
*me
,
546 struct m68hc11cpu
*controller
= hw_data (me
);
551 HW_TRACE ((me
, "write 0x%08lx %d", (long) base
, (int) nr_bytes
));
554 cpu
= STATE_CPU (sd
, 0);
555 base
-= controller
->attach_address
;
556 result
= sim_core_write_buffer (sd
, cpu
,
557 io_map
, source
, base
, nr_bytes
);
568 val
= *((uint8
*) source
);
569 m68hc11cpu_io_write (me
, cpu
, base
, val
);
578 const struct hw_descriptor dv_m68hc11_descriptor
[] = {
579 { "m68hc11", m68hc11cpu_finish
, },