diff options
Diffstat (limited to 'hw/hppa')
-rw-r--r-- | hw/hppa/Kconfig | 1 | ||||
-rw-r--r-- | hw/hppa/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/hppa/hppa_sys.h | 2 | ||||
-rw-r--r-- | hw/hppa/lasi.c | 360 | ||||
-rw-r--r-- | hw/hppa/machine.c | 8 | ||||
-rw-r--r-- | hw/hppa/trace-events | 5 |
6 files changed, 376 insertions, 2 deletions
diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig index 6e5d74a825..2a7b38d6d6 100644 --- a/hw/hppa/Kconfig +++ b/hw/hppa/Kconfig @@ -10,3 +10,4 @@ config DINO select IDE_CMD646 select MC146818RTC select LSI_SCSI_PCI + select LASI_82596 diff --git a/hw/hppa/Makefile.objs b/hw/hppa/Makefile.objs index 67838f50a3..eac3467d8a 100644 --- a/hw/hppa/Makefile.objs +++ b/hw/hppa/Makefile.objs @@ -1 +1 @@ -obj-$(CONFIG_DINO) += pci.o machine.o dino.o +obj-$(CONFIG_DINO) += pci.o machine.o dino.o lasi.o diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h index 4e5019695e..4d08501464 100644 --- a/hw/hppa/hppa_sys.h +++ b/hw/hppa/hppa_sys.h @@ -12,6 +12,8 @@ #include "hppa_hardware.h" PCIBus *dino_init(MemoryRegion *, qemu_irq *, qemu_irq *); +DeviceState *lasi_init(MemoryRegion *); +#define enable_lasi_lan() 0 #define TYPE_DINO_PCI_HOST_BRIDGE "dino-pcihost" diff --git a/hw/hppa/lasi.c b/hw/hppa/lasi.c new file mode 100644 index 0000000000..51752589f3 --- /dev/null +++ b/hw/hppa/lasi.c @@ -0,0 +1,360 @@ +/* + * HP-PARISC Lasi chipset emulation. + * + * (C) 2019 by Helge Deller <deller@gmx.de> + * + * This work is licensed under the GNU GPL license version 2 or later. + * + * Documentation available at: + * https://parisc.wiki.kernel.org/images-parisc/7/79/Lasi_ers.pdf + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "cpu.h" +#include "trace.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "sysemu/sysemu.h" +#include "sysemu/runstate.h" +#include "hppa_sys.h" +#include "hw/net/lasi_82596.h" +#include "hw/char/parallel.h" +#include "hw/char/serial.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" + +#define TYPE_LASI_CHIP "lasi-chip" + +#define LASI_IRR 0x00 /* RO */ +#define LASI_IMR 0x04 +#define LASI_IPR 0x08 +#define LASI_ICR 0x0c +#define LASI_IAR 0x10 + +#define LASI_PCR 0x0C000 /* LASI Power Control register */ +#define LASI_ERRLOG 0x0C004 /* LASI Error Logging register */ +#define LASI_VER 0x0C008 /* LASI Version Control register */ +#define LASI_IORESET 0x0C00C /* LASI I/O Reset register */ +#define LASI_AMR 0x0C010 /* LASI Arbitration Mask register */ +#define LASI_IO_CONF 0x7FFFE /* LASI primary configuration register */ +#define LASI_IO_CONF2 0x7FFFF /* LASI secondary configuration register */ + +#define LASI_BIT(x) (1ul << (x)) +#define LASI_IRQ_BITS (LASI_BIT(5) | LASI_BIT(7) | LASI_BIT(8) | LASI_BIT(9) \ + | LASI_BIT(13) | LASI_BIT(14) | LASI_BIT(16) | LASI_BIT(17) \ + | LASI_BIT(18) | LASI_BIT(19) | LASI_BIT(20) | LASI_BIT(21) \ + | LASI_BIT(26)) + +#define ICR_BUS_ERROR_BIT LASI_BIT(8) /* bit 8 in ICR */ +#define ICR_TOC_BIT LASI_BIT(1) /* bit 1 in ICR */ + +#define LASI_CHIP(obj) \ + OBJECT_CHECK(LasiState, (obj), TYPE_LASI_CHIP) + +#define LASI_RTC_HPA (LASI_HPA + 0x9000) + +typedef struct LasiState { + PCIHostState parent_obj; + + uint32_t irr; + uint32_t imr; + uint32_t ipr; + uint32_t icr; + uint32_t iar; + + uint32_t errlog; + uint32_t amr; + uint32_t rtc; + time_t rtc_ref; + + MemoryRegion this_mem; +} LasiState; + +static bool lasi_chip_mem_valid(void *opaque, hwaddr addr, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + bool ret = false; + + switch (addr) { + case LASI_IRR: + case LASI_IMR: + case LASI_IPR: + case LASI_ICR: + case LASI_IAR: + + case (LASI_LAN_HPA - LASI_HPA): + case (LASI_LPT_HPA - LASI_HPA): + case (LASI_UART_HPA - LASI_HPA): + case (LASI_RTC_HPA - LASI_HPA): + + case LASI_PCR ... LASI_AMR: + ret = true; + } + + trace_lasi_chip_mem_valid(addr, ret); + return ret; +} + +static MemTxResult lasi_chip_read_with_attrs(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + LasiState *s = opaque; + MemTxResult ret = MEMTX_OK; + uint32_t val; + + switch (addr) { + case LASI_IRR: + val = s->irr; + break; + case LASI_IMR: + val = s->imr; + break; + case LASI_IPR: + val = s->ipr; + /* Any read to IPR clears the register. */ + s->ipr = 0; + break; + case LASI_ICR: + val = s->icr & ICR_BUS_ERROR_BIT; /* bus_error */ + break; + case LASI_IAR: + val = s->iar; + break; + + case (LASI_LAN_HPA - LASI_HPA): + case (LASI_LPT_HPA - LASI_HPA): + case (LASI_UART_HPA - LASI_HPA): + val = 0; + break; + case (LASI_RTC_HPA - LASI_HPA): + val = time(NULL); + val += s->rtc_ref; + break; + + case LASI_PCR: + case LASI_VER: /* only version 0 existed. */ + case LASI_IORESET: + val = 0; + break; + case LASI_ERRLOG: + val = s->errlog; + break; + case LASI_AMR: + val = s->amr; + break; + + default: + /* Controlled by lasi_chip_mem_valid above. */ + g_assert_not_reached(); + } + + trace_lasi_chip_read(addr, val); + + *data = val; + return ret; +} + +static MemTxResult lasi_chip_write_with_attrs(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LasiState *s = opaque; + + trace_lasi_chip_write(addr, val); + + switch (addr) { + case LASI_IRR: + /* read-only. */ + break; + case LASI_IMR: + s->imr = val; /* 0x20 ?? */ + assert((val & LASI_IRQ_BITS) == val); + break; + case LASI_IPR: + /* Any write to IPR clears the register. */ + s->ipr = 0; + break; + case LASI_ICR: + s->icr = val; + /* if (val & ICR_TOC_BIT) issue_toc(); */ + break; + case LASI_IAR: + s->iar = val; + break; + + case (LASI_LAN_HPA - LASI_HPA): + /* XXX: reset LAN card */ + break; + case (LASI_LPT_HPA - LASI_HPA): + /* XXX: reset parallel port */ + break; + case (LASI_UART_HPA - LASI_HPA): + /* XXX: reset serial port */ + break; + case (LASI_RTC_HPA - LASI_HPA): + s->rtc_ref = val - time(NULL); + break; + + case LASI_PCR: + if (val == 0x02) /* immediately power off */ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + break; + case LASI_ERRLOG: + s->errlog = val; + break; + case LASI_VER: + /* read-only. */ + break; + case LASI_IORESET: + break; /* XXX: TODO: Reset various devices. */ + case LASI_AMR: + s->amr = val; + break; + + default: + /* Controlled by lasi_chip_mem_valid above. */ + g_assert_not_reached(); + } + return MEMTX_OK; +} + +static const MemoryRegionOps lasi_chip_ops = { + .read_with_attrs = lasi_chip_read_with_attrs, + .write_with_attrs = lasi_chip_write_with_attrs, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + .accepts = lasi_chip_mem_valid, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const VMStateDescription vmstate_lasi = { + .name = "Lasi", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irr, LasiState), + VMSTATE_UINT32(imr, LasiState), + VMSTATE_UINT32(ipr, LasiState), + VMSTATE_UINT32(icr, LasiState), + VMSTATE_UINT32(iar, LasiState), + VMSTATE_UINT32(errlog, LasiState), + VMSTATE_UINT32(amr, LasiState), + VMSTATE_END_OF_LIST() + } +}; + + +static void lasi_set_irq(void *opaque, int irq, int level) +{ + LasiState *s = opaque; + uint32_t bit = 1u << irq; + + if (level) { + s->ipr |= bit; + if (bit & s->imr) { + uint32_t iar = s->iar; + s->irr |= bit; + if ((s->icr & ICR_BUS_ERROR_BIT) == 0) { + stl_be_phys(&address_space_memory, iar & -32, iar & 31); + } + } + } +} + +static int lasi_get_irq(unsigned long hpa) +{ + switch (hpa) { + case LASI_HPA: + return 14; + case LASI_UART_HPA: + return 5; + case LASI_LPT_HPA: + return 7; + case LASI_LAN_HPA: + return 8; + case LASI_SCSI_HPA: + return 9; + case LASI_AUDIO_HPA: + return 13; + case LASI_PS2KBD_HPA: + case LASI_PS2MOU_HPA: + return 26; + default: + g_assert_not_reached(); + } +} + +DeviceState *lasi_init(MemoryRegion *address_space) +{ + DeviceState *dev; + LasiState *s; + + dev = qdev_create(NULL, TYPE_LASI_CHIP); + s = LASI_CHIP(dev); + s->iar = CPU_HPA + 3; + + /* Lasi access from main memory. */ + memory_region_init_io(&s->this_mem, OBJECT(s), &lasi_chip_ops, + s, "lasi", 0x100000); + memory_region_add_subregion(address_space, LASI_HPA, &s->this_mem); + + qdev_init_nofail(dev); + + /* LAN */ + if (enable_lasi_lan()) { + qemu_irq lan_irq = qemu_allocate_irq(lasi_set_irq, s, + lasi_get_irq(LASI_LAN_HPA)); + lasi_82596_init(address_space, LASI_LAN_HPA, lan_irq); + } + + /* Parallel port */ + qemu_irq lpt_irq = qemu_allocate_irq(lasi_set_irq, s, + lasi_get_irq(LASI_LPT_HPA)); + parallel_mm_init(address_space, LASI_LPT_HPA + 0x800, 0, + lpt_irq, parallel_hds[0]); + + /* Real time clock (RTC), it's only one 32-bit counter @9000 */ + s->rtc = time(NULL); + s->rtc_ref = 0; + + if (serial_hd(1)) { + /* Serial port */ + qemu_irq serial_irq = qemu_allocate_irq(lasi_set_irq, s, + lasi_get_irq(LASI_UART_HPA)); + serial_mm_init(address_space, LASI_UART_HPA + 0x800, 0, + serial_irq, 8000000 / 16, + serial_hd(1), DEVICE_NATIVE_ENDIAN); + } + return dev; +} + +static void lasi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_lasi; +} + +static const TypeInfo lasi_pcihost_info = { + .name = TYPE_LASI_CHIP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LasiState), + .class_init = lasi_class_init, +}; + +static void lasi_register_types(void) +{ + type_register_static(&lasi_pcihost_info); +} + +type_init(lasi_register_types) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 5d0de26140..33e3769d0b 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -16,6 +16,7 @@ #include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" +#include "hw/net/lasi_82596.h" #include "hppa_sys.h" #include "qemu/units.h" #include "qapi/error.h" @@ -101,6 +102,9 @@ static void machine_hppa_init(MachineState *machine) "ram", ram_size); memory_region_add_subregion(addr_space, 0, ram_region); + /* Init Lasi chip */ + lasi_init(addr_space); + /* Init Dino (PCI host bus chip). */ pci_bus = dino_init(addr_space, &rtc_irq, &serial_irq); assert(pci_bus); @@ -125,7 +129,9 @@ static void machine_hppa_init(MachineState *machine) /* Network setup. e1000 is good enough, failing Tulip support. */ for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); + if (!enable_lasi_lan()) { + pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); + } } /* Load firmware. Given that this is not "real" firmware, diff --git a/hw/hppa/trace-events b/hw/hppa/trace-events index f943b16c4e..3ff620319a 100644 --- a/hw/hppa/trace-events +++ b/hw/hppa/trace-events @@ -7,3 +7,8 @@ hppa_pci_iack_write(void) "" dino_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d" dino_chip_read(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" dino_chip_write(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" + +# lasi.c +lasi_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d" +lasi_chip_read(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" +lasi_chip_write(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" |