/***************************************************************************** * pce * *****************************************************************************/ /***************************************************************************** * File name: src/arch/sim405/sim405.c * * Created: 2004-06-01 by Hampa Hug * * Copyright: (C) 2004-2018 Hampa Hug * * Copyright: (C) 2004-2006 Lukas Ruf * *****************************************************************************/ /***************************************************************************** * This program is free software. You can redistribute it and / or modify it * * under the terms of the GNU General Public License version 2 as published * * by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY, without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * * Public License for more details. * *****************************************************************************/ /***************************************************************************** * This software was developed at the Computer Engineering and Networks * * Laboratory (TIK), Swiss Federal Institute of Technology (ETH) Zurich. * *****************************************************************************/ #include "main.h" #include #include "hook.h" #include "msg.h" #include "pci.h" #include "sercons.h" #include "sim405.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define S405_CLOCK (200UL * 1000UL * 1000UL) static unsigned long s405_get_dcr (void *ext, unsigned long dcrn); static void s405_set_dcr (void *ext, unsigned long dcrn, unsigned long val); void s405_break (sim405_t *sim, unsigned char val); static void s405_setup_system (sim405_t *sim, ini_sct_t *ini) { ini_sct_t *sct; const char *model; unsigned long uicinv; unsigned long serial_clock; int sync_time_base; sct = ini_next_sct (ini, NULL, "system"); if (sct == NULL) { sct = ini_next_sct (ini, NULL, "powerpc"); } ini_get_string (sct, "model", &model, "ppc405"); ini_get_uint32 (sct, "uic_invert", &uicinv, 0x0000007f); ini_get_uint32 (sct, "serial_clock", &serial_clock, 115200); ini_get_bool (sct, "sync_time_base", &sync_time_base, 1); pce_log_tag (MSG_INF, "CPU:", "model=%s uic-inv=%08lX sync_time_base=%d\n", model, uicinv, sync_time_base ); sim->ppc = p405_new (); sim->sync_time_base = (sync_time_base != 0); p405_set_mem_fct (sim->ppc, sim->mem, &mem_get_uint8, &mem_get_uint16_be, &mem_get_uint32_be, &mem_set_uint8, &mem_set_uint16_be, &mem_set_uint32_be ); if (sim->ram != NULL) { p405_set_ram (sim->ppc, mem_blk_get_data (sim->ram), mem_blk_get_size (sim->ram)); } p405_set_dcr_fct (sim->ppc, sim, &s405_get_dcr, &s405_set_dcr); p405uic_init (&sim->uic); p405uic_set_invert (&sim->uic, uicinv); p405uic_set_nint_fct (&sim->uic, sim->ppc, p405_interrupt); if (serial_clock > 0) { pce_log_tag (MSG_INF, "SERIAL:", "clock=%lu (%lu)\n", serial_clock, 16 * serial_clock ); sim->serial_clock = 16 * serial_clock; } } static void s405_setup_serport (sim405_t *sim, ini_sct_t *ini) { unsigned i; unsigned long addr; unsigned irq; unsigned multichar, clock_mul; const char *driver; const char *chip; ini_sct_t *sct; static unsigned long defbase[2] = { 0xef600300, 0xef600400 }; static unsigned defirq[2] = { 0, 1 }; sim->serport[0] = NULL; sim->serport[1] = NULL; i = 0; sct = NULL; while ((sct = ini_next_sct (ini, sct, "serial")) != NULL) { if (i >= 2) { break; } ini_get_uint32 (sct, "address", &addr, defbase[i]); ini_get_uint16 (sct, "irq", &irq, defirq[i]); ini_get_string (sct, "uart", &chip, "8250"); ini_get_uint16 (sct, "multichar", &multichar, 1); ini_get_uint16 (sct, "clock_mul", &clock_mul, 1); ini_get_string (sct, "driver", &driver, NULL); pce_log_tag (MSG_INF, "UART:", "n=%u addr=0x%08lx irq=%u uart=%s multi=%u clock_mul=%u driver=%s\n", i, addr, irq, chip, multichar, clock_mul, (driver == NULL) ? "" : driver ); sim->serport[i] = ser_new (addr, 0); if (sim->serport[i] == NULL) { pce_log (MSG_ERR, "*** serial port setup failed [%08lX/%u -> %s]\n", addr, irq, (driver == NULL) ? "" : driver ); } else { e8250_t *uart; if (driver != NULL) { if (ser_set_driver (sim->serport[i], driver)) { pce_log (MSG_ERR, "*** can't open driver (%s)\n", driver); } } uart = &sim->serport[i]->uart; e8250_set_buf_size (uart, 256, 256); e8250_set_multichar (uart, multichar, multichar); e8250_set_clock_mul (uart, clock_mul); e8250_set_bit_clk_div (uart, (S405_CLOCK / 16) / sim->serial_clock); if (e8250_set_chip_str (uart, chip)) { pce_log (MSG_ERR, "*** unknown UART chip (%s)\n", chip); } e8250_set_irq_fct (uart, &sim->uic, p405uic_get_irq_fct (&sim->uic, irq) ); p405uic_set_force_polarity (&sim->uic, irq, 1); mem_add_blk (sim->mem, ser_get_reg (sim->serport[i]), 0); i += 1; } } } static void s405_setup_sercons (sim405_t *sim, ini_sct_t *ini) { ini_sct_t *sct; unsigned ser; sct = ini_next_sct (ini, NULL, "sercons"); if (sct == NULL) { ser = 0; } else { ini_get_uint16 (sct, "serial", &ser, 0); } pce_log_tag (MSG_INF, "CONSOLE:", "serport=%u\n", ser); if (ser >= 2) { return; } sim->sercons = ser; } static void s405_setup_slip (sim405_t *sim, ini_sct_t *ini) { ini_sct_t *sct; unsigned ser; const char *name; e8250_t *uart; sct = ini_next_sct (ini, NULL, "slip"); if (sct == NULL) { return; } ini_get_uint16 (sct, "serial", &ser, 0); ini_get_string (sct, "interface", &name, "tun0"); pce_log_tag (MSG_INF, "SLIP:", "serport=%u interface=%s\n", ser, name); if (ser >= 2) { return; } if (sim->serport[ser] == NULL) { return; } sim->slip = slip_new(); if (sim->slip == NULL) { return; } uart = ser_get_uart (sim->serport[ser]); e8250_set_send_fct (uart, sim->slip, slip_uart_check_out); e8250_set_recv_fct (uart, sim->slip, slip_uart_check_inp); e8250_set_setup_fct (uart, NULL, NULL); slip_set_get_uint8_fct (sim->slip, uart, e8250_send); slip_set_set_uint8_fct (sim->slip, uart, e8250_receive); if (slip_set_tun (sim->slip, name)) { pce_log (MSG_ERR, "*** creating tun interface failed (%s)\n", name); } } static void s405_setup_disks (sim405_t *sim, ini_sct_t *ini) { sim->dsks = ini_get_disks (ini); } static void s405_setup_pci (sim405_t *sim, ini_sct_t *ini) { sim->pci = s405_pci_new(); mem_add_blk (sim->mem, s405_pci_get_mem_ioa (sim->pci), 0); mem_add_blk (sim->mem, s405_pci_get_mem_iob (sim->pci), 0); mem_add_blk (sim->mem, s405_pci_get_mem_cfg (sim->pci), 0); mem_add_blk (sim->mem, s405_pci_get_mem_special (sim->pci), 0); mem_add_blk (sim->mem, s405_pci_get_mem_csr (sim->pci), 0); pce_log_tag (MSG_INF, "PCI:", "initialized\n"); } static void s405_setup_ata (sim405_t *sim, ini_sct_t *ini) { unsigned pcidev, pciirq; unsigned long vendor_id, device_id; ini_sct_t *sct; pci_ata_init (&sim->pciata); sct = ini_next_sct (ini, NULL, "pci_ata"); if (sct == NULL) { return; } ini_get_uint16 (sct, "pci_device", &pcidev, 1); ini_get_uint16 (sct, "pci_irq", &pciirq, 31); ini_get_uint32 (sct, "vendor_id", &vendor_id, PCIID_VENDOR_VIA); ini_get_uint32 (sct, "device_id", &device_id, PCIID_VIA_82C561); pce_log_tag (MSG_INF, "PCI-ATA:", "pcidev=%u irq=%u vendor=0x%04lx id=0x%04lx\n", pcidev, pciirq, vendor_id, device_id ); pci_dev_set_device_id (&sim->pciata.pci, vendor_id, device_id); pci_set_device (&sim->pci->bus, &sim->pciata.pci, pcidev); pci_dev_set_intr_fct (&sim->pciata.pci, 0, &sim->uic, p405uic_get_irq_fct (&sim->uic, pciirq) ); ini_get_pci_ata (&sim->pciata, sim->dsks, sct); } static void s405_setup_ds1743 (sim405_t *sim, ini_sct_t *sct) { unsigned long addr, size; const char *fname; dev_ds1743_t *rtc; ini_get_uint32 (sct, "address", &addr, 0xf0000000); ini_get_uint32 (sct, "size", &size, 8192); ini_get_string (sct, "file", &fname, NULL); pce_log_tag (MSG_INF, "DS1743:", "addr=0x%08lx size=%lu file=%s\n", addr, size, (fname != NULL) ? fname : "" ); rtc = dev_ds1743_new (addr, size); if (rtc == NULL) { return; } if (fname != NULL) { if (dev_ds1743_set_fname (rtc, fname)) { pce_log (MSG_ERR, "*** opening file failed (%s)\n", fname ); } } mem_add_blk (sim->mem, &rtc->blk, 0); dev_lst_add (&sim->devlst, &rtc->dev); } static void s405_setup_devices (sim405_t *sim, ini_sct_t *ini) { ini_sct_t *sct; const char *type; sct = NULL; while ((sct = ini_next_sct (ini, sct, "device")) != NULL) { ini_get_string (sct, "type", &type, NULL); if (type != NULL) { if (strcmp (type, "ds1743") == 0) { s405_setup_ds1743 (sim, sct); } else { pce_log (MSG_INF, "*** unknown device '%s'\n", type ); } } } } sim405_t *s405_new (ini_sct_t *ini) { unsigned i; sim405_t *sim; sim = malloc (sizeof (sim405_t)); if (sim == NULL) { return (NULL); } sim->brk = 0; sim->clk_cnt = 0; sim->real_clk = clock(); sim->sync_clock_sim = 0; sim->sync_clock_real = 0; sim->sync_interval = 0; sim->serial_clock = 1; sim->serial_clock_count = 0; pce_get_interval_us (&sim->sync_interval); for (i = 0; i < 4; i++) { sim->clk_div[i] = 0; } s405_hook_init (sim); bps_init (&sim->bps); dev_lst_init (&sim->devlst); sim->mem = mem_new(); ini_get_ram (sim->mem, ini, &sim->ram); ini_get_rom (sim->mem, ini); s405_setup_system (sim, ini); s405_setup_serport (sim, ini); s405_setup_sercons (sim, ini); s405_setup_slip (sim, ini); s405_setup_disks (sim, ini); s405_setup_pci (sim, ini); s405_setup_ata (sim, ini); s405_setup_devices (sim, ini); sim->ocm0_iscntl = 0; sim->ocm0_isarc = 0; sim->ocm0_dscntl = 0; sim->ocm0_dsarc = 0; sim->cpc0_cr0 = 0x00000000; sim->cpc0_cr1 = 0x00000000; sim->cpc0_psr = 0x00000400; pce_load_mem_ini (sim->mem, ini); return (sim); } void s405_del (sim405_t *sim) { if (sim == NULL) { return; } dev_lst_free (&sim->devlst); pci_ata_free (&sim->pciata); s405_pci_del (sim->pci); dsks_del (sim->dsks); slip_del (sim->slip); ser_del (sim->serport[1]); ser_del (sim->serport[0]); p405uic_free (&sim->uic); p405_del (sim->ppc); mem_del (sim->mem); bps_free (&sim->bps); s405_hook_free (sim); free (sim); } unsigned long long s405_get_clkcnt (sim405_t *sim) { return (sim->clk_cnt); } static unsigned long s405_get_dcr (void *ext, unsigned long dcrn) { sim405_t *sim; sim = (sim405_t *) ext; switch (dcrn) { case 0: s405_break (sim, PCE_BRK_STOP); break; case SIM405_DCRN_OCM0_ISARC: /* 0x18 */ return (sim->ocm0_isarc); case SIM405_DCRN_OCM0_ISCNTL: /* 0x19 */ return (sim->ocm0_iscntl); case SIM405_DCRN_OCM0_DSARC: /* 0x1a */ return (sim->ocm0_dsarc); case SIM405_DCRN_OCM0_DSCNTL: /* 0x1b */ return (sim->ocm0_dscntl); case SIM405_DCRN_CPC0_CR0: /* 0xb1 */ return (sim->cpc0_cr0); case SIM405_DCRN_CPC0_CR1: /* 0xb2 */ return (sim->cpc0_cr1); case SIM405_DCRN_CPC0_PSR: /* 0xb4 */ return (sim->cpc0_psr); case SIM405_DCRN_UIC0_SR: return (p405uic_get_sr (&sim->uic)); case SIM405_DCRN_UIC0_ER: return (p405uic_get_er (&sim->uic)); case SIM405_DCRN_UIC0_CR: return (p405uic_get_cr (&sim->uic)); case SIM405_DCRN_UIC0_PR: return (p405uic_get_er (&sim->uic)); case SIM405_DCRN_UIC0_TR: return (p405uic_get_er (&sim->uic)); case SIM405_DCRN_UIC0_MSR: return (p405uic_get_msr (&sim->uic)); case SIM405_DCRN_UIC0_VR: return (p405uic_get_vr (&sim->uic)); case SIM405_DCRN_MAL0_CFG: /* 0x180 */ return (0); default: pce_log (MSG_DEB, "%08lX: get dcr %03lx\n", (unsigned long) p405_get_pc (sim->ppc), dcrn ); break; } return (0); } static void s405_set_dcr (void *ext, unsigned long dcrn, unsigned long val) { sim405_t *sim; sim = (sim405_t *) ext; switch (dcrn) { case SIM405_DCRN_OCM0_ISARC: /* 0x18 */ sim->ocm0_isarc = val; break; case SIM405_DCRN_OCM0_ISCNTL: /* 0x19 */ sim->ocm0_iscntl = val; if (val & 0x80000000) { pce_log (MSG_DEB, "%08lX: instruction side ocm0 enabled\n", (unsigned long) p405_get_pc (sim->ppc) ); } break; case SIM405_DCRN_OCM0_DSARC: /* 0x1a */ sim->ocm0_dsarc = val; break; case SIM405_DCRN_OCM0_DSCNTL: /* 0x1b */ sim->ocm0_dscntl = val; if (val & 0x80000000) { pce_log (MSG_DEB, "%08lX: data side ocm0 enabled\n", (unsigned long) p405_get_pc (sim->ppc) ); } break; case SIM405_DCRN_CPC0_CR0: /* 0xb1 */ sim->cpc0_cr0 = val; break; case SIM405_DCRN_CPC0_CR1: /* 0xb2 */ sim->cpc0_cr1 = val; break; case SIM405_DCRN_CPC0_PSR: /* 0xb4 */ sim->cpc0_psr = val; break; case SIM405_DCRN_UIC0_SR: p405uic_set_sr (&sim->uic, val); break; case SIM405_DCRN_UIC0_ER: p405uic_set_er (&sim->uic, val); break; case SIM405_DCRN_UIC0_CR: p405uic_set_cr (&sim->uic, val); break; case SIM405_DCRN_UIC0_PR: p405uic_set_pr (&sim->uic, val); break; case SIM405_DCRN_UIC0_TR: p405uic_set_tr (&sim->uic, val); break; case SIM405_DCRN_UIC0_VCR: p405uic_set_vcr (&sim->uic, val); break; case SIM405_DCRN_MAL0_CFG: /* 0x180 */ break; default: pce_log (MSG_DEB, "%08lX: set dcr %03lx <- %08lx\n", (unsigned long) p405_get_pc (sim->ppc), dcrn, val ); break; } } void s405_break (sim405_t *sim, unsigned char val) { if ((val == PCE_BRK_STOP) || (val == PCE_BRK_ABORT)) { sim->brk = val; } } void s405_set_keycode (sim405_t *sim, unsigned char val) { if (sim->serport[sim->sercons] != NULL) { ser_receive (sim->serport[sim->sercons], val); } } void s405_reset (sim405_t *sim) { p405_reset (sim->ppc); } void s405_clock_discontinuity (sim405_t *sim) { sim->sync_clock_sim = 0; pce_get_interval_us (&sim->sync_interval); } static void s405_sync (sim405_t *sim) { unsigned long vclk; unsigned long rclk; vclk = sim->sync_clock_sim; sim->sync_clock_sim = 0; rclk = pce_get_interval_us (&sim->sync_interval); rclk = (S405_CLOCK * (unsigned long long) rclk) / (1 * 1000000); if (vclk < rclk) { p405_add_timer_clock (sim->ppc, rclk - vclk); } } void s405_clock (sim405_t *sim, unsigned n) { unsigned long clk, ser; if (sim->clk_div[0] >= 256) { clk = sim->clk_div[0] & ~255UL; sim->clk_div[1] += clk; sim->clk_div[0] &= 255; if (sim->clk_div[1] >= 4096) { clk = sim->clk_div[1] & ~4095UL; sim->clk_div[2] += clk; sim->clk_div[1] &= 4095UL; if (sim->serport[0] != NULL) { ser_clock (sim->serport[0], clk); } if (sim->serport[1] != NULL) { ser_clock (sim->serport[1], clk); } if (sim->slip != NULL) { slip_clock (sim->slip, clk); } if (sim->clk_div[2] >= 65536) { scon_check (sim); if (sim->sync_time_base) { s405_sync (sim); } sim->clk_div[2] &= 65535; } } } ser = sim->serial_clock_count >> 10; if (ser > 0) { if (sim->serport[0] != NULL) { e8250_clock (&sim->serport[0]->uart, ser); } if (sim->serport[1] != NULL) { e8250_clock (&sim->serport[1]->uart, ser); } sim->serial_clock_count -= (ser << 4); } p405_clock (sim->ppc, n); sim->clk_cnt += n; sim->clk_div[0] += n; sim->sync_clock_sim += n; sim->serial_clock_count += n; }